first working crop script

This commit is contained in:
Peter Boin 2017-07-30 01:24:21 +10:00
parent a82becb61d
commit 53de97d9b0
3 changed files with 121 additions and 34 deletions

View File

@ -14,7 +14,9 @@ for pygcode_lib_type in ('installed_lib', 'relative_lib'):
try:
# pygcode
from pygcode import Machine, Mode
from pygcode import Line
from pygcode import Line, Comment
from pygcode import GCodePlaneSelect, GCodeSelectXYPlane
from pygcode import GCodeRapidMove
except ImportError:
import sys, os, inspect
@ -131,9 +133,11 @@ def range_type(value):
# --- Create Parser
parser = argparse.ArgumentParser(description='Crop a gcode file down to the')
parser = argparse.ArgumentParser(
description="Remove gcode before and after given 'from' and 'to' events"
)
parser.add_argument(
'infile', type=argparse.FileType('r'), nargs=1,
'infile', type=argparse.FileType('r'),
help="gcode file to normalize",
)
parser.add_argument(
@ -159,35 +163,52 @@ post_crop = False
(is_first, is_last) = args.range
for (i, line_str) in enumerate(args.infile.readlines()):
line = Line(line_str)
# TODO: remember machine's maximum values for each axis
# TODO: based on machine.abs_pos
# remember machine's (relevant) state before processing
# TODO: create machine.__copy__ to copy position & state
# remember machine's state before processing the current line
old_machine = copy(machine)
machine.process_block(line.block)
if pre_crop:
if is_first(i + 1, old_machine.pos):
if is_first(i + 1, machine.pos):
# First line inside cropping range
pre_crop = False
# TODO: print mode
print(old_machine.mode)
# TODO: position machine before first cropped line
# TODO: rapid move to Z (maximum Z)
# TODO: rapid move to X,Y
# TODO: rapid move to Z (machine.pos.Z)
# Set machine's accumulated mode (from everything that's been cut)
mode_str = str(old_machine.mode)
if mode_str:
print(Comment("machine mode before cropping"))
print(mode_str)
# Getting machine's current (modal) selected plane
plane = old_machine.mode.plane_selection
if not isinstance(plane, GCodePlaneSelect):
plane = GCodeSelectXYPlane() # default to XY plane
# --- position machine before first cropped line
print(Comment("traverse into position, up, over, and down"))
# rapid move to Z (maximum Z the machine has experienced thus far)
print(GCodeRapidMove(**{
plane.normal_axis: getattr(old_machine.abs_range_max, plane.normal_axis),
}))
# rapid move to X,Y
print(GCodeRapidMove(**dict(
(k, v) for (k, v) in old_machine.pos.values.items()
if k in plane.plane_axes
)))
# rapid move to Z (machine.pos.Z)
print(GCodeRapidMove(**{
plane.normal_axis: getattr(old_machine.pos, plane.normal_axis),
}))
print('')
if (pre_crop, post_crop) == (False, False):
if is_last(i + 1, machine.pos):
# First line **outside** the area being cropped
# (ie: this line won't be output)
post_crop = True # although, irrelevant because...
break
else:
# inside cropping area
print(line)
if is_last(i + 1, old_machine.pos):
# Last line in cropping range
post_crop = True

View File

@ -824,9 +824,11 @@ class GCodePlaneSelect(GCode):
# vectorYZ = GCodeSelectYZPlane.quat * (GCodeSelectZXPlane.quat.conjugate() * vectorZX)
quat = None # Quaternion
# -- Plane Normal
# -- Plane Axis Information
# Vector normal to plane (such that XYZ axes follow the right-hand rule)
normal_axis = None # Letter of normal axis (upper case)
# Axes of plane
plane_axes = set()
normal = None # Vector3
@ -835,6 +837,7 @@ class GCodeSelectXYPlane(GCodePlaneSelect):
word_key = Word('G', 17)
quat = Quaternion() # no effect
normal_axis = 'Z'
plane_axes = set('XY')
normal = Vector3(0., 0., 1.)
@ -846,6 +849,7 @@ class GCodeSelectZXPlane(GCodePlaneSelect):
Vector3(0., 0., 1.), Vector3(1., 0., 0.)
)
normal_axis = 'Y'
plane_axes = set('ZX')
normal = Vector3(0., 1., 0.)
@ -857,6 +861,7 @@ class GCodeSelectYZPlane(GCodePlaneSelect):
Vector3(0., 1., 0.), Vector3(0., 0., 1.)
)
normal_axis = 'X'
plane_axes = set('YZ')
normal = Vector3(1., 0., 0.)

View File

@ -52,6 +52,9 @@ class Position(object):
self._value = defaultdict(lambda: 0.0, dict((k, 0.0) for k in self.axes))
self._value.update(kwargs)
def __copy__(self):
return self.__class__(axes=copy(self.axes), unit=self._unit, **self.values)
def update(self, **coords):
for (k, v) in coords.items():
setattr(self, k, v)
@ -74,10 +77,6 @@ class Position(object):
else:
self.__dict__[key] = value
# Copy
def __copy__(self):
return self.__class__(axes=copy(self.axes), unit=self._unit, **self.values)
# Equality
def __eq__(self, other):
if self.axes ^ other.axes:
@ -137,6 +136,36 @@ class Position(object):
self._value[k] *= factor
self._unit = value
# Min/Max
@classmethod
def _cmp(cls, p1, p2, key):
"""
Returns a position of the combined min/max values for each axis
(eg: key=min for)
note: the result is not necessarily equal to either p1 or p2.
:param p1: Position instance
:param p2: Position instance
:return: Position instance with the highest/lowest value per axis
"""
if p2.unit != p1.unit:
p2 = copy(p2)
p2.unit = p1.unit
return cls(
unit=p1.unit,
**dict(
(k, key(getattr(p1, k), getattr(p2, k))) for k in p1.values
)
)
@classmethod
def min(cls, a, b):
return cls._cmp(a, b, key=min)
@classmethod
def max(cls, a, b):
return cls._cmp(a, b, key=max)
# Words & Values
@property
def words(self):
return sorted(Word(k, self._value[k]) for k in self.axes)
@ -149,6 +178,7 @@ class Position(object):
def vector(self):
return Vector3(self._value['X'], self._value['Y'], self._value['Z'])
# String representation(s)
def __repr__(self):
return "<{class_name}: {coordinates}>".format(
class_name=self.__class__.__name__,
@ -157,12 +187,13 @@ class Position(object):
class CoordinateSystem(object):
def __init__(self, axes):
self.offset = Position(axes)
def __init__(self, axes=None):
self.offset = Position(axes=axes)
def __add__(self, other):
if isinstance(other, CoordinateSystem):
pass
def __copy__(self):
obj = self.__class__()
obj.offset = copy(self.offset)
return obj
def __repr__(self):
return "<{class_name}: offset={offset}>".format(
@ -178,6 +209,7 @@ class State(object):
# AFAIK: this is everything needed to remember a machine's state that isn't
# handled by modal gcodes.
def __init__(self, axes=None):
self._axes = axes
# Coordinate Systems
self.coord_systems = {}
for i in range(1, 10): # G54-G59.3
@ -206,6 +238,12 @@ class State(object):
# Coordinate System selection:
# - G54-G59: select coordinate system (offsets from machine coordinates set by G10 L2)
def __copy__(self):
obj = self.__class__(axes=self._axes)
obj.coord_systems = [copy(cs) for cs in self.coord_systems]
obj.offset = copy(self.offset)
return obj
@property
def coord_sys(self):
"""Current equivalent coordinate system, including all """
@ -249,12 +287,18 @@ class Mode(object):
# Mode is defined by gcodes set by processed blocks:
# see modal_group in gcode.py module for details
def __init__(self):
def __init__(self, set_default=True):
self.modal_groups = defaultdict(lambda: None)
# Initialize
if set_default:
self.set_mode(*Line(self.default_mode).block.gcodes)
def __copy__(self):
obj = self.__class__(set_default=False)
obj.modal_groups = deepcopy(self.modal_groups)
return obj
def set_mode(self, *gcode_list):
"""
Set machine mode from given gcodes (will not be processed)
@ -335,6 +379,18 @@ class Machine(object):
# Absolute machine position
self.abs_pos = self.Position()
# Machine's motion range (min/max corners of a bounding box)
self.abs_range_min = copy(self.abs_pos)
self.abs_range_max = copy(self.abs_pos)
def __copy__(self):
obj = self.__class__()
obj.mode = copy(self.mode)
obj.state = copy(self.state)
obj.abs_pos = copy(self.abs_pos)
obj.abs_range_min = copy(self.abs_range_min)
obj.abs_range_max = copy(self.abs_range_max)
return obj
def set_mode(self, *gcode_list):
self.mode.set_mode(*gcode_list) # passthrough
@ -425,6 +481,11 @@ class Machine(object):
coord_sys_offset = getattr(self.state.coord_sys, 'offset', Position(axes=self.axes))
temp_offset = self.state.offset
self.abs_pos = (value + temp_offset) + coord_sys_offset
self._update_abs_range(self.abs_pos)
def _update_abs_range(self, pos):
self.abs_range_min = Position.min(pos, self.abs_range_min)
self.abs_range_max = Position.max(pos, self.abs_range_max)
# =================== Machine Actions ===================
def move_to(self, rapid=False, **coords):