Skip to content

Commit

Permalink
version 3.5
Browse files Browse the repository at this point in the history
New "Cycle" setup mode
Add progress bars in CLI
New draw_path function in Python API
Now requiring Python 3.7
Bug fixes in issuing warnings
New feature to enable alternate servo control pin
Add new Python API Example scripts
Significant refactoring in several areas
Move most Python API specific functions to pyaxidraw.
  • Loading branch information
oskay committed Aug 16, 2022
1 parent 51e2fef commit e753fde
Show file tree
Hide file tree
Showing 22 changed files with 2,134 additions and 882 deletions.
33 changes: 19 additions & 14 deletions cli/axicli/axidraw_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
from plotink.plot_utils_import import from_dependency_import # plotink
exit_status = from_dependency_import("ink_extensions_utils.exit_status")

cli_version = "AxiDraw Command Line Interface 3.4.0"
cli_version = "AxiDraw Command Line Interface 3.5.0"

quick_help = '''
Basic syntax to plot a file: axicli svg_in [OPTIONS]
Expand Down Expand Up @@ -102,7 +102,7 @@ def axidraw_CLI(dev = False):

parser.add_argument("-m","--mode", \
metavar='MODENAME', type=str, \
help="Mode. One of: [plot, layers, align, toggle, manual, " \
help="Mode. One of: [plot, layers, align, toggle, cycle, manual, " \
+ "sysinfo, version, res_plot, res_home, reorder (deprecated)]. Default: plot.")

parser.add_argument("-s","--speed_pendown", \
Expand Down Expand Up @@ -239,6 +239,10 @@ def axidraw_CLI(dev = False):
action='store_const', const='True',
help="Output the version of axicli")

parser.add_argument("-b","--progress", \
action="store_const", const='True', \
help='Enable CLI progress bar while plotting')

args = parser.parse_args()

# Handle trivial cases
Expand All @@ -251,15 +255,9 @@ def axidraw_CLI(dev = False):
if args.mode == "options":
quit()

if args.mode == "timing":
quit()

# Detect certain "trivial" cases that do not require an input file
use_trivial_file = False

if args.mode == "align" or args.mode == "toggle" \
or args.mode == "version" or args.mode == "sysinfo" \
or args.mode == "manual":
if args.mode in ["align", "toggle", "cycle", "version", "sysinfo", "manual"]:
use_trivial_file = True

svg_input = args.svg_in
Expand All @@ -282,6 +280,10 @@ def axidraw_CLI(dev = False):
if args.reordering is not None:
adc.options.reordering = args.reordering

if args.progress is not None:
# Pass through to AxiDraw Control; this option is CLI specific.
adc.options.progress = args.progress

print("Re-ordering SVG File.")
print("This can take a while for large files.")
print("(Warning: Reorder mode is deprecated and will be removed in a future version.)")
Expand Down Expand Up @@ -329,13 +331,16 @@ def axidraw_CLI(dev = False):

# assign command line options to adc's options.
# additionally, look inside the config to see if any command line options were set there
option_names = ["mode", "speed_pendown", "speed_penup", "accel", "pen_pos_down", "pen_pos_up",
"pen_rate_lower", "pen_rate_raise", "pen_delay_down", "pen_delay_up",
"random_start", "reordering", "no_rotate", "const_speed", "report_time",
"manual_cmd", "walk_dist", "layer", "copies", "page_delay", "preview",
"rendering", "model", "port", "port_config", 'digest', 'webhook', 'webhook_url',]
option_names = ['mode', 'speed_pendown', 'speed_penup', 'accel', 'pen_pos_down', 'pen_pos_up',
'pen_rate_lower', 'pen_rate_raise', 'pen_delay_down', 'pen_delay_up',
'random_start', 'reordering', 'no_rotate', 'const_speed', 'report_time',
'manual_cmd', 'walk_dist', 'layer', 'copies', 'page_delay', 'preview',
'rendering', 'model', 'port', 'port_config', 'webhook', 'webhook_url',
'digest', 'progress']
utils.assign_option_values(adc.options, args, [config_dict], option_names)

adc.cli_api = True # Set flag that this is being called from the CLI.

exit_status.run(adc.effect) # Plot the document
if utils.has_output(adc) and not use_trivial_file:
utils.output_result(args.output_file, adc.outdoc)
Expand Down
203 changes: 203 additions & 0 deletions cli/examples_python/interactive_draw_path.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -#-

'''
interactive_draw_path.py
Demonstrate use of axidraw module in "interactive" mode, drawing continuous
paths using the draw_path function.
Run this demo by calling: python interactive_draw_path.py
Interactive mode is a mode of use, designed for plotting individual motion
segments upon request, using direct XY control. It is a complement to the
usual plotting modes, which take an SVG document as input.
So long as the AxiDraw is started in the home corner, moves are limit checked,
and constrained to be within the safe travel range of the AxiDraw.
AxiDraw python API documentation is hosted at: https://axidraw.com/doc/py_api/
---------------------------------------------------------------------
About this software:
The AxiDraw writing and drawing machine is a product of Evil Mad Scientist
Laboratories. https://axidraw.com https://shop.evilmadscientist.com
This open source software is written and maintained by Evil Mad Scientist
to support AxiDraw users across a wide range of applications. Please help
support Evil Mad Scientist and open source software development by purchasing
genuine AxiDraw hardware.
AxiDraw software development is hosted at https://github.com/evil-mad/axidraw
Additional AxiDraw documentation is available at http://axidraw.com/docs
AxiDraw owners may request technical support for this software through our
github issues page, support forums, or by contacting us directly at:
https://shop.evilmadscientist.com/contact
---------------------------------------------------------------------
Copyright 2020 Windell H. Oskay, Evil Mad Scientist Laboratories
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
'''

import sys
import math
import time
from pyaxidraw import axidraw

ad = axidraw.AxiDraw() # Initialize class



def print_position():
'''
Query, report, and print position and pen state
'''
turtle_position = ad.turtle_pos()
current_position = ad.current_pos()
print(f"Turtle position: {turtle_position[0]:0.3f}, {turtle_position[1]:0.3f}")
print(f"Actual position: {current_position[0]:0.3f}, {current_position[1]:0.3f}\n")

ad.interactive() # Enter interactive mode
connected = ad.connect() # Open serial port to AxiDraw

if not connected:
sys.exit() # end script

ad.options.speed_pendown = 10 # set pen-down speed to slow
ad.update() # Process changes to options

# Create a path that moves the turtle out of bounds and back:
# Default units are in inches.
vertex_list_1 = [[0,1], [1,1], [3,-1], [5,1]]

print("Draw a path that takes us out of bounds and back:\n")
print("vertex list: " + str(vertex_list_1) + "\n")


ad.draw_path(vertex_list_1) # Plot the path
print("Finished first vertex list, working in inch units. Final pen position:\n")
print_position()
print("Set cm units and repeat with same vertex list, but smaller:\n")


ad.options.units = 1 # Switch to cm units
ad.update() # Process changes to options
ad.draw_path(vertex_list_1) # Plot the path
print_position()

print("Set mm units and draw the same vertex list, even smaller:\n")

ad.options.units = 2 # Switch to cm units
ad.update() # Process changes to options
ad.draw_path(vertex_list_1) # Plot the path
print_position()



ad.options.units = 0 # Switch to inch units
ad.update() # Process changes to options


print("Draw a path that takes us out of bounds:\n")

vertex_list_1 = [[0,1], [1,1], [3,-1]]
print("vertex list: " + str(vertex_list_1) + "\n")

ad.draw_path(vertex_list_1) # Plot the path

print("Note that the turtle position and physical position do not agree:\n")
print_position()
time.sleep(1)


print("Increase speed, draw a hexagon:\n")

ad.options.speed_pendown = 20 # set pen-down speed to slow
ad.update() # Process changes to options

vertex_list_2 = []
CENTER_X = 1
CENTER_Y = 2
RADIUS = 1
VERTICES = 6

for angle in range(VERTICES + 1):
x_position = CENTER_X + RADIUS * math.cos(math.tau * angle / VERTICES)
y_position = CENTER_Y + RADIUS * math.sin(math.tau * angle / VERTICES)
vertex_list_2.append([x_position, y_position])

ad.draw_path(vertex_list_2) # Plot the path

print("Switching to mm units.\n")

ad.options.units = 2 # Switch to mm units
ad.update() # Process changes to options

print("Draw a circumscribed circle around the hexagon, with 120 segments:\n")

vertex_list_3 = []
CENTER_X = 25.4
CENTER_Y = 50.8
RADIUS = 25.4
VERTICES = 120

for angle in range(VERTICES + 1):
x_position = CENTER_X + RADIUS * math.cos(math.tau * angle / VERTICES)
y_position = CENTER_Y + RADIUS * math.sin(math.tau * angle / VERTICES)
vertex_list_3.append([x_position, y_position])

ad.draw_path(vertex_list_3) # Plot the path


print("Finally, draw some quick squiggles...\n")

ad.options.speed_pendown = 50 # Turn up speed
ad.update() # Process changes to options

vertex_list_4 = []
VERTICES = 120
START_X = 75
START_Y = 50.8
Y_RADIUS = 20
X_RADIUS = 4
VERTICES = 400

for vertex in range(VERTICES + 1):
x_position = START_X + vertex / 10 + X_RADIUS * math.sin(20 * math.tau * vertex / VERTICES)
y_position = START_Y + Y_RADIUS * math.sin(10 * math.tau * vertex / VERTICES)
vertex_list_4.append([x_position, y_position])

ad.draw_path(vertex_list_4) # Plot the path
print("And finish back at home.\n")
ad.moveto(0,0) # Pen-up return home

ad.disconnect() # Close serial port to AxiDraw
2 changes: 1 addition & 1 deletion cli/examples_python/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@


import os.path
from axidrawinternal import axidraw
from pyaxidraw import axidraw

ad = axidraw.AxiDraw() # Create class instance

Expand Down
2 changes: 1 addition & 1 deletion cli/examples_python/plot_inline.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@


import random
from axidrawinternal import axidraw
from pyaxidraw import axidraw

ad = axidraw.AxiDraw() # Create class instance

Expand Down
86 changes: 86 additions & 0 deletions cli/examples_python/report_pos_inch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -#-

'''
report_pos_inch.py
Run this demo by calling: python report_pos_inch.py
Prints X.XXX, Y.YYY, where
X.XXX is the current AxiDraw X position and
Y.YYY is the current AxiDraw Y position, in inch units.
Requires Python 3.6 or newer and the AxiDraw python API, version 3.2 or newer.
AxiDraw python API documentation is hosted at: https://axidraw.com/doc/py_api/
About this software:
The AxiDraw writing and drawing machine is a product of Evil Mad Scientist
Laboratories. https://axidraw.com https://shop.evilmadscientist.com
This open source software is written and maintained by Evil Mad Scientist
to support AxiDraw users across a wide range of applications. Please help
support Evil Mad Scientist and open source software development by purchasing
genuine AxiDraw hardware.
AxiDraw software development is hosted at https://github.com/evil-mad/axidraw
Additional AxiDraw documentation is available at http://axidraw.com/docs
AxiDraw owners may request technical support for this software through our
github issues page, support forums, or by contacting us directly at:
https://shop.evilmadscientist.com/contact
Copyright 2022 Windell H. Oskay, Evil Mad Scientist Laboratories
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
'''

from pyaxidraw import axidraw

ad = axidraw.AxiDraw() # Initialize class
ad.interactive()
ad.connect() # Open USB serial session
if not ad.connected:
exit()

result = ad.usb_query('QS\r') # Query global step position
result_list = result.strip().split(",")
a_pos, b_pos = int(result_list[0]), int(result_list[1])

x_pos_inch = (a_pos + b_pos) / (4 * ad.params.native_res_factor)
y_pos_inch = (a_pos - b_pos) / (4 * ad.params.native_res_factor)
if ad.options.resolution == 2: # Low-resolution mode
x_pos_inch *= 2
y_pos_inch *= 2

print("{0:0.3f}, {1:0.3f}".format(x_pos_inch, y_pos_inch))

ad.disconnect() # Close serial port to AxiDraw
Loading

0 comments on commit e753fde

Please sign in to comment.