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: try:
# pygcode # pygcode
from pygcode import Machine, Mode 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: except ImportError:
import sys, os, inspect import sys, os, inspect
@ -131,9 +133,11 @@ def range_type(value):
# --- Create Parser # --- 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( parser.add_argument(
'infile', type=argparse.FileType('r'), nargs=1, 'infile', type=argparse.FileType('r'),
help="gcode file to normalize", help="gcode file to normalize",
) )
parser.add_argument( parser.add_argument(
@ -159,35 +163,52 @@ post_crop = False
(is_first, is_last) = args.range (is_first, is_last) = args.range
for (i, line_str) in enumerate(args.infile.readlines()): for (i, line_str) in enumerate(args.infile.readlines()):
line = Line(line_str) line = Line(line_str)
# TODO: remember machine's maximum values for each axis # remember machine's state before processing the current line
# TODO: based on machine.abs_pos
# remember machine's (relevant) state before processing
# TODO: create machine.__copy__ to copy position & state
old_machine = copy(machine) old_machine = copy(machine)
machine.process_block(line.block) machine.process_block(line.block)
if pre_crop: if pre_crop:
if is_first(i + 1, old_machine.pos): if is_first(i + 1, machine.pos):
# First line inside cropping range # First line inside cropping range
pre_crop = False pre_crop = False
# TODO: print mode # Set machine's accumulated mode (from everything that's been cut)
print(old_machine.mode) mode_str = str(old_machine.mode)
# TODO: position machine before first cropped line if mode_str:
# TODO: rapid move to Z (maximum Z) print(Comment("machine mode before cropping"))
# TODO: rapid move to X,Y print(mode_str)
# TODO: rapid move to Z (machine.pos.Z)
# 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 (pre_crop, post_crop) == (False, False):
if is_last(i + 1, machine.pos):
print(line) # First line **outside** the area being cropped
# (ie: this line won't be output)
if is_last(i + 1, old_machine.pos): post_crop = True # although, irrelevant because...
# Last line in cropping range break
post_crop = True else:
# inside cropping area
print(line)

View File

@ -824,9 +824,11 @@ class GCodePlaneSelect(GCode):
# vectorYZ = GCodeSelectYZPlane.quat * (GCodeSelectZXPlane.quat.conjugate() * vectorZX) # vectorYZ = GCodeSelectYZPlane.quat * (GCodeSelectZXPlane.quat.conjugate() * vectorZX)
quat = None # Quaternion quat = None # Quaternion
# -- Plane Normal # -- Plane Axis Information
# Vector normal to plane (such that XYZ axes follow the right-hand rule) # Vector normal to plane (such that XYZ axes follow the right-hand rule)
normal_axis = None # Letter of normal axis (upper case) normal_axis = None # Letter of normal axis (upper case)
# Axes of plane
plane_axes = set()
normal = None # Vector3 normal = None # Vector3
@ -835,6 +837,7 @@ class GCodeSelectXYPlane(GCodePlaneSelect):
word_key = Word('G', 17) word_key = Word('G', 17)
quat = Quaternion() # no effect quat = Quaternion() # no effect
normal_axis = 'Z' normal_axis = 'Z'
plane_axes = set('XY')
normal = Vector3(0., 0., 1.) normal = Vector3(0., 0., 1.)
@ -846,6 +849,7 @@ class GCodeSelectZXPlane(GCodePlaneSelect):
Vector3(0., 0., 1.), Vector3(1., 0., 0.) Vector3(0., 0., 1.), Vector3(1., 0., 0.)
) )
normal_axis = 'Y' normal_axis = 'Y'
plane_axes = set('ZX')
normal = Vector3(0., 1., 0.) normal = Vector3(0., 1., 0.)
@ -857,6 +861,7 @@ class GCodeSelectYZPlane(GCodePlaneSelect):
Vector3(0., 1., 0.), Vector3(0., 0., 1.) Vector3(0., 1., 0.), Vector3(0., 0., 1.)
) )
normal_axis = 'X' normal_axis = 'X'
plane_axes = set('YZ')
normal = Vector3(1., 0., 0.) 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 = defaultdict(lambda: 0.0, dict((k, 0.0) for k in self.axes))
self._value.update(kwargs) self._value.update(kwargs)
def __copy__(self):
return self.__class__(axes=copy(self.axes), unit=self._unit, **self.values)
def update(self, **coords): def update(self, **coords):
for (k, v) in coords.items(): for (k, v) in coords.items():
setattr(self, k, v) setattr(self, k, v)
@ -74,10 +77,6 @@ class Position(object):
else: else:
self.__dict__[key] = value self.__dict__[key] = value
# Copy
def __copy__(self):
return self.__class__(axes=copy(self.axes), unit=self._unit, **self.values)
# Equality # Equality
def __eq__(self, other): def __eq__(self, other):
if self.axes ^ other.axes: if self.axes ^ other.axes:
@ -137,6 +136,36 @@ class Position(object):
self._value[k] *= factor self._value[k] *= factor
self._unit = value 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 @property
def words(self): def words(self):
return sorted(Word(k, self._value[k]) for k in self.axes) return sorted(Word(k, self._value[k]) for k in self.axes)
@ -149,6 +178,7 @@ class Position(object):
def vector(self): def vector(self):
return Vector3(self._value['X'], self._value['Y'], self._value['Z']) return Vector3(self._value['X'], self._value['Y'], self._value['Z'])
# String representation(s)
def __repr__(self): def __repr__(self):
return "<{class_name}: {coordinates}>".format( return "<{class_name}: {coordinates}>".format(
class_name=self.__class__.__name__, class_name=self.__class__.__name__,
@ -157,12 +187,13 @@ class Position(object):
class CoordinateSystem(object): class CoordinateSystem(object):
def __init__(self, axes): def __init__(self, axes=None):
self.offset = Position(axes) self.offset = Position(axes=axes)
def __add__(self, other): def __copy__(self):
if isinstance(other, CoordinateSystem): obj = self.__class__()
pass obj.offset = copy(self.offset)
return obj
def __repr__(self): def __repr__(self):
return "<{class_name}: offset={offset}>".format( 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 # AFAIK: this is everything needed to remember a machine's state that isn't
# handled by modal gcodes. # handled by modal gcodes.
def __init__(self, axes=None): def __init__(self, axes=None):
self._axes = axes
# Coordinate Systems # Coordinate Systems
self.coord_systems = {} self.coord_systems = {}
for i in range(1, 10): # G54-G59.3 for i in range(1, 10): # G54-G59.3
@ -206,6 +238,12 @@ class State(object):
# Coordinate System selection: # Coordinate System selection:
# - G54-G59: select coordinate system (offsets from machine coordinates set by G10 L2) # - 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 @property
def coord_sys(self): def coord_sys(self):
"""Current equivalent coordinate system, including all """ """Current equivalent coordinate system, including all """
@ -249,11 +287,17 @@ class Mode(object):
# Mode is defined by gcodes set by processed blocks: # Mode is defined by gcodes set by processed blocks:
# see modal_group in gcode.py module for details # see modal_group in gcode.py module for details
def __init__(self): def __init__(self, set_default=True):
self.modal_groups = defaultdict(lambda: None) self.modal_groups = defaultdict(lambda: None)
# Initialize # Initialize
self.set_mode(*Line(self.default_mode).block.gcodes) 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): def set_mode(self, *gcode_list):
""" """
@ -335,6 +379,18 @@ class Machine(object):
# Absolute machine position # Absolute machine position
self.abs_pos = self.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): def set_mode(self, *gcode_list):
self.mode.set_mode(*gcode_list) # passthrough 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)) coord_sys_offset = getattr(self.state.coord_sys, 'offset', Position(axes=self.axes))
temp_offset = self.state.offset temp_offset = self.state.offset
self.abs_pos = (value + temp_offset) + coord_sys_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 =================== # =================== Machine Actions ===================
def move_to(self, rapid=False, **coords): def move_to(self, rapid=False, **coords):