summaryrefslogtreecommitdiff
path: root/embroider.py
diff options
context:
space:
mode:
Diffstat (limited to 'embroider.py')
-rw-r--r--embroider.py141
1 files changed, 120 insertions, 21 deletions
diff --git a/embroider.py b/embroider.py
index d5a963de..25884316 100644
--- a/embroider.py
+++ b/embroider.py
@@ -51,7 +51,7 @@ EMBROIDERABLE_TAGS = (SVG_PATH_TAG, SVG_POLYLINE_TAG)
PIXELS_PER_MM = 96 / 25.4
class Param(object):
- def __init__(self, name, description, unit=None, values=[], type=None, group=None, inverse=False, default=None):
+ def __init__(self, name, description, unit=None, values=[], type=None, group=None, inverse=False, default=None, tooltip=None, sort_index=0):
self.name = name
self.description = description
self.unit = unit
@@ -60,6 +60,8 @@ class Param(object):
self.group = group
self.inverse = inverse
self.default = default
+ self.tooltip = tooltip
+ self.sort_index = sort_index
def __repr__(self):
return "Param(%s)" % vars(self)
@@ -310,8 +312,37 @@ class EmbroideryElement(object):
return flattened
+ @property
+ @param('trim_after',
+ 'TRIM after',
+ tooltip='Trim thread after this object (for supported machines and file formats)',
+ type='boolean',
+ default=False,
+ sort_index=1000)
+ def trim_after(self):
+ return self.get_boolean_param('trim_after', False)
+
+ @property
+ @param('stop_after',
+ 'STOP after',
+ tooltip='Add STOP instruction after this object (for supported machines and file formats)',
+ type='boolean',
+ default=False,
+ sort_index=1000)
+ def stop_after(self):
+ return self.get_boolean_param('stop_after', False)
+
def to_patches(self, last_patch):
- raise NotImplementedError("%s must implement to_path()" % self.__class__.__name__)
+ raise NotImplementedError("%s must implement to_patches()" % self.__class__.__name__)
+
+ def embroider(self, last_patch):
+ patches = self.to_patches(last_patch)
+
+ if patches:
+ patches[-1].trim_after = self.trim_after
+ patches[-1].stop_after = self.stop_after
+
+ return patches
def fatal(self, message):
print >> sys.stderr, "error:", message
@@ -1196,7 +1227,7 @@ class Stroke(EmbroideryElement):
return self.get_float_param("zigzag_spacing_mm")
@property
- @param('repeats', 'Repeats', type='int')
+ @param('repeats', 'Repeats', type='int', default="1")
def repeats(self):
return self.get_int_param("repeats", 1)
@@ -1762,6 +1793,7 @@ def detect_classes(node):
def descendants(node):
+
nodes = []
element = EmbroideryElement(node)
@@ -1780,9 +1812,11 @@ def descendants(node):
return nodes
class Patch:
- def __init__(self, color=None, stitches=None):
+ def __init__(self, color=None, stitches=None, trim_after=False, stop_after=False):
self.color = color
self.stitches = stitches or []
+ self.trim_after = trim_after
+ self.stop_after = stop_after
def __add__(self, other):
if isinstance(other, Patch):
@@ -1797,12 +1831,64 @@ class Patch:
return Patch(self.color, self.stitches[::-1])
+def process_stop_after(stitches):
+ # The user wants the machine to pause after this patch. This can
+ # be useful for applique and similar on multi-needle machines that
+ # normally would not stop between colors.
+ #
+ # On such machines, the user assigns needles to the colors in the
+ # design before starting stitching. C01, C02, etc are normal
+ # needles, but C00 is special. For a block of stitches assigned
+ # to C00, the machine will continue sewing with the last color it
+ # had and pause after it completes the C00 block.
+ #
+ # That means we need to introduce an artificial color change
+ # shortly before the current stitch so that the user can set that
+ # to C00. We'll go back 3 stitches and do that:
+
+ if len(stitches) >= 3:
+ stitches[-3].stop = True
+
+ # and also add a color change on this stitch, completing the C00
+ # block:
+
+ stitches[-1].stop = True
+
+ # reference for the above: https://github.com/lexelby/inkstitch/pull/29#issuecomment-359175447
+
+
+def process_trim(stitches, next_stitch):
+ # DST (and maybe other formats?) has no actual TRIM instruction.
+ # Instead, 3 sequential JUMPs cause the machine to trim the thread.
+ #
+ # To support both DST and other formats, we'll add a TRIM and two
+ # JUMPs. The TRIM will be converted to a JUMP by libembroidery
+ # if saving to DST, resulting in the 3-jump sequence.
+
+ delta = next_stitch - stitches[-1]
+ delta = delta * (1/4.0)
+
+ pos = stitches[-1]
+
+ for i in xrange(3):
+ pos += delta
+ stitches.append(PyEmb.Stitch(pos.x, pos.y, stitches[-1].color, jump=True))
+
+ # first one should be TRIM instead of JUMP
+ stitches[-3].jump = False
+ stitches[-3].trim = True
+
+
def patches_to_stitches(patch_list, collapse_len_px=0):
stitches = []
last_stitch = None
last_color = None
+ need_trim = False
for patch in patch_list:
+ if not patch.stitches:
+ continue
+
jump_stitch = True
for stitch in patch.stitches:
if last_stitch and last_color == patch.color:
@@ -1818,31 +1904,46 @@ def patches_to_stitches(patch_list, collapse_len_px=0):
# dbg.write("... collapsed\n")
jump_stitch = False
- # dbg.write("stitch color %s\n" % patch.color)
+ if stitches and last_color and last_color != patch.color:
+ # add a color change
+ stitches.append(PyEmb.Stitch(last_stitch.x, last_stitch.y, last_color, stop=True))
- newStitch = PyEmb.Stitch(stitch.x, stitch.y, patch.color, jump_stitch)
- stitches.append(newStitch)
+ if need_trim:
+ process_trim(stitches, stitch)
+ need_trim = False
+
+ if jump_stitch:
+ stitches.append(PyEmb.Stitch(stitch.x, stitch.y, patch.color, jump=True))
+
+ stitches.append(PyEmb.Stitch(stitch.x, stitch.y, patch.color, jump=False))
jump_stitch = False
last_stitch = stitch
last_color = patch.color
+ if patch.trim_after:
+ need_trim = True
+
+ if patch.stop_after:
+ process_stop_after(stitches)
+
return stitches
def stitches_to_polylines(stitches):
polylines = []
last_color = None
last_stitch = None
+ trimming = False
for stitch in stitches:
- #if stitch.jump_stitch:
- #if last_color == stitch.color:
- # polylines.append([None, [last_stitch.as_tuple(), stitch.as_tuple()]])
+ if stitch.color != last_color or stitch.trim:
+ trimming = True
+ polylines.append([stitch.color, []])
- # last_color = None
+ if trimming and (stitch.jump or stitch.trim):
+ continue
- if stitch.color != last_color:
- polylines.append([stitch.color, []])
+ trimming = False
polylines[-1][1].append(stitch.as_tuple())
@@ -1905,10 +2006,9 @@ class Embroider(inkex.Effect):
dest="hide_layers", default="true",
help="Hide all other layers when the embroidery layer is generated")
self.OptionParser.add_option("-O", "--output_format",
- action="store", type="choice",
- choices=["melco", "csv", "gcode"],
- dest="output_format", default="melco",
- help="File output format")
+ action="store", type="string",
+ dest="output_format", default="csv",
+ help="Output file extenstion (default: csv)")
self.OptionParser.add_option("-P", "--path",
action="store", type="string",
dest="path", default=".",
@@ -1936,7 +2036,7 @@ class Embroider(inkex.Effect):
output_path = os.path.join(self.options.path, self.options.output_file)
else:
svg_filename = self.document.getroot().get(inkex.addNS('docname', 'sodipodi'), "embroidery.svg")
- csv_filename = svg_filename.replace('.svg', '.csv')
+ csv_filename = svg_filename.replace('.svg', '.%s' % self.options.output_format)
output_path = os.path.join(self.options.path, csv_filename)
def add_suffix(path, suffix):
@@ -2008,11 +2108,10 @@ class Embroider(inkex.Effect):
else:
last_patch = None
- patches.extend(element.to_patches(last_patch))
+ patches.extend(element.embroider(last_patch))
stitches = patches_to_stitches(patches, self.options.collapse_length_mm * PIXELS_PER_MM)
- emb = PyEmb.Embroidery(stitches, PIXELS_PER_MM)
- emb.export(self.get_output_path(), self.options.output_format)
+ PyEmb.write_embroidery_file(self.get_output_path(), stitches)
new_layer = inkex.etree.SubElement(self.document.getroot(), SVG_GROUP_TAG, {})
new_layer.set('id', self.uniqueId("embroidery"))