big refactor to untangle embroidery from stitch generation

pull/1/head
Lex Neva 2016-10-26 15:04:15 -04:00
rodzic 7cb1b27a67
commit 90e4cc2c8c
2 zmienionych plików z 96 dodań i 126 usunięć

Wyświetl plik

@ -4,6 +4,7 @@
import math import math
import sys import sys
from copy import deepcopy
class Point: class Point:
def __init__(self, x, y): def __init__(self, x, y):
@ -58,20 +59,25 @@ class Point:
def __cmp__(self, other): def __cmp__(self, other):
return cmp(self.as_tuple(), other.as_tuple()) return cmp(self.as_tuple(), other.as_tuple())
class Embroidery: class Stitch(Point):
def __init__(self): def __init__(self, x, y, color=None, jumpStitch=False):
self.coords = [] Point.__init__(self, x, y)
self.color = color
self.jumpStitch = jumpStitch
def addStitch(self, coord): class Embroidery:
if len(self.coords) == 0 or self.coords[-1] != coord: def __init__(self, stitches, pixels_per_millimeter=1):
self.coords.append(coord) self.stitches = deepcopy(stitches)
self.scale(1.0/pixels_per_millimeter)
self.scale((1, -1))
self.translate_to_origin()
def translate_to_origin(self): def translate_to_origin(self):
if (len(self.coords)==0): if (len(self.stitches)==0):
return return
(maxx,maxy) = (self.coords[0].x,self.coords[0].y) (maxx,maxy) = (self.stitches[0].x,self.stitches[0].y)
(minx,miny) = (self.coords[0].x,self.coords[0].y) (minx,miny) = (self.stitches[0].x,self.stitches[0].y)
for p in self.coords: for p in self.stitches:
minx = min(minx,p.x) minx = min(minx,p.x)
miny = min(miny,p.y) miny = min(miny,p.y)
maxx = max(maxx,p.x) maxx = max(maxx,p.x)
@ -83,22 +89,22 @@ class Embroidery:
return (minx, miny) return (minx, miny)
def translate(self, dx, dy): def translate(self, dx, dy):
for p in self.coords: for p in self.stitches:
p.x += dx p.x += dx
p.y += dy p.y += dy
def scale(self, sc): def scale(self, sc):
if not isinstance(sc, (tuple, list)): if not isinstance(sc, (tuple, list)):
sc = (sc, sc) sc = (sc, sc)
for p in self.coords: for p in self.stitches:
p.x *= sc[0] p.x *= sc[0]
p.y *= sc[1] p.y *= sc[1]
def export_ksm(self, dbg): def export_ksm(self):
str = "" str = ""
self.pos = Point(0,0) self.pos = Point(0,0)
lastColor = None lastColor = None
for stitch in self.coords: for stitch in self.stitches:
if (lastColor!=None and stitch.color!=lastColor): if (lastColor!=None and stitch.color!=lastColor):
mode_byte = 0x99 mode_byte = 0x99
#dbg.write("Color change!\n") #dbg.write("Color change!\n")
@ -121,13 +127,13 @@ class Embroidery:
self.pos = stitch self.pos = stitch
return str return str
def export_melco(self, dbg): def export_melco(self):
self.str = "" self.str = ""
self.pos = self.coords[0] self.pos = self.stitches[0]
#dbg.write("stitch count: %d\n" % len(self.coords)) #dbg.write("stitch count: %d\n" % len(self.stitches))
lastColor = None lastColor = None
numColors = 0x0 numColors = 0x0
for stitch in self.coords[1:]: for stitch in self.stitches[1:]:
if (lastColor!=None and stitch.color!=lastColor): if (lastColor!=None and stitch.color!=lastColor):
numColors += 1 numColors += 1
# color change # color change
@ -164,14 +170,14 @@ class Embroidery:
self.pos = stitch self.pos = stitch
return self.str return self.str
def export_csv(self, dbg): def export_csv(self):
self.str = "" self.str = ""
self.str += '"#","[THREAD_NUMBER]","[RED]","[GREEN]","[BLUE]","[DESCRIPTION]","[CATALOG_NUMBER]"\n' self.str += '"#","[THREAD_NUMBER]","[RED]","[GREEN]","[BLUE]","[DESCRIPTION]","[CATALOG_NUMBER]"\n'
self.str += '"#","[STITCH_TYPE]","[X]","[Y]"\n' self.str += '"#","[STITCH_TYPE]","[X]","[Y]"\n'
lastStitch = None lastStitch = None
colorIndex = 0 colorIndex = 0
for stitch in self.coords: for stitch in self.stitches:
if lastStitch is not None and stitch.color != lastStitch.color: if lastStitch is not None and stitch.color != lastStitch.color:
self.str += '"*","COLOR","%f","%f"\n' % (lastStitch.x, lastStitch.y) self.str += '"*","COLOR","%f","%f"\n' % (lastStitch.x, lastStitch.y)
if lastStitch is None or stitch.color != lastStitch.color: if lastStitch is None or stitch.color != lastStitch.color:
@ -188,10 +194,10 @@ class Embroidery:
self.str += '"*","END","%f","%f"\n' % (lastStitch.x, lastStitch.y) self.str += '"*","END","%f","%f"\n' % (lastStitch.x, lastStitch.y)
return self.str return self.str
def export_gcode(self, dbg): def export_gcode(self):
ret = [] ret = []
lastColor = None lastColor = None
for stitch in self.coords: for stitch in self.stitches:
if stitch.color != lastColor: if stitch.color != lastColor:
ret.append('M0 ;MSG, Color change; prepare for %s\n' % stitch.color) ret.append('M0 ;MSG, Color change; prepare for %s\n' % stitch.color)
lastColor = stitch.color lastColor = stitch.color
@ -199,24 +205,16 @@ class Embroidery:
ret.append('M0 ;MSG, EMBROIDER stitch\n') ret.append('M0 ;MSG, EMBROIDER stitch\n')
return ''.join(ret) return ''.join(ret)
def export_paths(self, dbg): def export(self, filename, format):
paths = [] fp = open(filename, "wb")
lastColor = None
lastStitch = None if format == "melco":
for stitch in self.coords: fp.write(self.export_melco())
if stitch.jumpStitch: elif format == "csv":
if lastColor == stitch.color: fp.write(self.export_csv())
paths.append([None, []]) elif format == "gcode":
if lastStitch is not None: fp.write(self.export_gcode())
paths[-1][1].append(['M', lastStitch.as_tuple()]) fp.close()
paths[-1][1].append(['L', stitch.as_tuple()])
lastColor = None
if stitch.color != lastColor:
paths.append([stitch.color, []])
paths[-1][1].append(['L' if len(paths[-1][1]) > 0 else 'M', stitch.as_tuple()])
lastColor = stitch.color
lastStitch = stitch
return paths
class Test: class Test:
def __init__(self): def __init__(self):

Wyświetl plik

@ -46,7 +46,7 @@ dbg = open("/tmp/embroider-debug.txt", "w")
PyEmb.dbg = dbg PyEmb.dbg = dbg
#pixels_per_millimeter = 90.0 / 25.4 #pixels_per_millimeter = 90.0 / 25.4
#this actually makes each pixel worth one tenth of a millimeter #this makes each pixel worth one tenth of a millimeter
pixels_per_millimeter = 10 pixels_per_millimeter = 10
# a 0.5pt stroke becomes a straight line. # a 0.5pt stroke becomes a straight line.
@ -189,80 +189,69 @@ class Patch:
def reverse(self): def reverse(self):
return Patch(self.color, self.stitches[::-1]) return Patch(self.color, self.stitches[::-1])
class EmbroideryObject: def patches_to_stitches(patch_list, collapse_len_px=0):
def __init__(self, patch_list): stitches = []
self.patch_list = patch_list
def emit_file(self, filename, output_format, collapse_len_px): lastStitch = None
emb = PyEmb.Embroidery() lastColor = None
lastStitch = None for patch in patch_list:
lastColor = None jumpStitch = True
for patch in self.patch_list: for stitch in patch.stitches:
jumpStitch = True if lastStitch and lastColor == patch.color:
for stitch in patch.stitches: l = (stitch - lastStitch).length()
if lastStitch and lastColor == patch.color: if l <= 0.1:
c = math.sqrt((stitch.x - lastStitch.x) ** 2 + (stitch.y - lastStitch.y) ** 2) # filter out duplicate successive stitches
#dbg.write("stitch length: %f (%d/%d -> %d/%d)\n" % (c, lastStitch.x, lastStitch.y, stitch.x, stitch.y)) jumpStitch = False
continue
if c <= 0.1: if jumpStitch:
# filter out duplicate successive stitches # consider collapsing jump stitch, if it is pretty short
if l < collapse_len_px:
#dbg.write("... collapsed\n")
jumpStitch = False jumpStitch = False
continue
if jumpStitch: #dbg.write("stitch color %s\n" % patch.color)
# consider collapsing jump stich, if it is pretty short
if c < collapse_len_px:
#dbg.write("... collapsed\n")
jumpStitch = False
#dbg.write("stitch color %s\n" % patch.color) newStitch = PyEmb.Stitch(stitch.x, stitch.y, patch.color, jumpStitch)
stitches.append(newStitch)
newStitch = PyEmb.Point(stitch.x, -stitch.y) jumpStitch = False
newStitch.color = patch.color lastStitch = stitch
newStitch.jumpStitch = jumpStitch lastColor = patch.color
emb.addStitch(newStitch)
jumpStitch = False return stitches
lastStitch = newStitch
lastColor = patch.color
dx, dy = emb.translate_to_origin() def stitches_to_paths(stitches):
emb.scale(1.0/pixels_per_millimeter) paths = []
lastColor = None
lastStitch = None
for stitch in stitches:
if stitch.jumpStitch:
if lastColor == stitch.color:
paths.append([None, []])
if lastStitch is not None:
paths[-1][1].append(['M', lastStitch.as_tuple()])
paths[-1][1].append(['L', stitch.as_tuple()])
lastColor = None
if stitch.color != lastColor:
paths.append([stitch.color, []])
paths[-1][1].append(['L' if len(paths[-1][1]) > 0 else 'M', stitch.as_tuple()])
lastColor = stitch.color
lastStitch = stitch
return paths
fp = open(filename, "wb")
if output_format == "melco": def emit_inkscape(parent, stitches):
fp.write(emb.export_melco(dbg)) for color, path in stitches_to_paths(stitches):
elif output_format == "csv": dbg.write('path: %s %s\n' % (color, repr(path)))
fp.write(emb.export_csv(dbg)) inkex.etree.SubElement(parent,
elif output_format == "gcode": inkex.addNS('path', 'svg'),
fp.write(emb.export_gcode(dbg)) { 'style':simplestyle.formatStyle(
fp.close() { 'stroke': color if color is not None else '#000000',
emb.scale(pixels_per_millimeter) 'stroke-width':"0.4",
emb.translate(dx, dy) 'fill': 'none' }),
return emb 'd':simplepath.formatPath(path),
})
def emit_inkscape(self, parent, emb):
emb.scale((1, -1));
for color, path in emb.export_paths(dbg):
dbg.write('path: %s %s\n' % (color, repr(path)))
inkex.etree.SubElement(parent,
inkex.addNS('path', 'svg'),
{ 'style':simplestyle.formatStyle(
{ 'stroke': color if color is not None else '#000000',
'stroke-width':"0.4",
'fill': 'none' }),
'd':simplepath.formatPath(path),
})
def bbox(self):
x = []
y = []
for patch in self.patch_list:
for stitch in patch.stitches:
x.append(stitch.x)
y.append(stitch.y)
return (min(x), min(y), max(x), max(y))
class Embroider(inkex.Effect): class Embroider(inkex.Effect):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -662,36 +651,19 @@ class Embroider(inkex.Effect):
if self.options.hide_layers: if self.options.hide_layers:
self.hide_layers() self.hide_layers()
eo = EmbroideryObject(self.patch_list) stitches = patches_to_stitches(self.patch_list, self.collapse_len_px)
emb = eo.emit_file(self.get_output_path(), self.options.output_format, emb = PyEmb.Embroidery(stitches, pixels_per_millimeter)
self.collapse_len_px) emb.export(self.get_output_path(), self.options.output_format)
new_layer = inkex.etree.SubElement(self.document.getroot(), new_layer = inkex.etree.SubElement(self.document.getroot(),
inkex.addNS('g', 'svg'), {}) inkex.addNS('g', 'svg'), {})
new_layer.set('id', self.uniqueId("embroidery")) new_layer.set('id', self.uniqueId("embroidery"))
new_layer.set(inkex.addNS('label', 'inkscape'), 'Embroidery') new_layer.set(inkex.addNS('label', 'inkscape'), 'Embroidery')
new_layer.set(inkex.addNS('groupmode', 'inkscape'), 'layer') new_layer.set(inkex.addNS('groupmode', 'inkscape'), 'layer')
eo.emit_inkscape(new_layer, emb) emit_inkscape(new_layer, stitches)
sys.stdout = old_stdout sys.stdout = old_stdout
def emit_inkscape_bbox(self, parent, eo):
(x0, y0, x1, y1) = eo.bbox()
new_path = []
new_path.append(['M', (x0,y0)])
new_path.append(['L', (x1,y0)])
new_path.append(['L', (x1,y1)])
new_path.append(['L', (x0,y1)])
new_path.append(['L', (x0,y0)])
inkex.etree.SubElement(parent,
inkex.addNS('path', 'svg'),
{ 'style':simplestyle.formatStyle(
{ 'stroke': '#ff00ff',
'stroke-width':str(1),
'fill': 'none' }),
'd':simplepath.formatPath(new_path),
})
def hide_layers(self): def hide_layers(self):
for g in self.document.getroot().findall(inkex.addNS("g","svg")): for g in self.document.getroot().findall(inkex.addNS("g","svg")):
if g.get(inkex.addNS("groupmode", "inkscape")) == "layer": if g.get(inkex.addNS("groupmode", "inkscape")) == "layer":