mirror of
https://git.mirrors.martin98.com/https://github.com/petaflot/pygcode
synced 2025-04-22 05:40:07 +08:00
first working crop script
This commit is contained in:
parent
a82becb61d
commit
53de97d9b0
@ -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)
|
||||||
|
@ -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.)
|
||||||
|
|
||||||
|
|
||||||
|
@ -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):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user