kopia lustrzana https://github.com/inkstitch/inkstitch
big refactor to untangle embroidery from stitch generation
rodzic
7cb1b27a67
commit
90e4cc2c8c
78
PyEmb.py
78
PyEmb.py
|
@ -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):
|
||||||
|
|
144
embroider.py
144
embroider.py
|
@ -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":
|
||||||
|
|
Ładowanie…
Reference in New Issue