2014-12-26 22:15:48 +00:00
|
|
|
#!python
|
|
|
|
#!/usr/bin/python
|
|
|
|
# http://www.achatina.de/sewing/main/TECHNICL.HTM
|
|
|
|
|
|
|
|
import math
|
|
|
|
import sys
|
2016-10-26 19:04:15 +00:00
|
|
|
from copy import deepcopy
|
2014-12-26 22:15:48 +00:00
|
|
|
|
|
|
|
class Point:
|
2016-10-27 16:48:12 +00:00
|
|
|
def __init__(self, x, y):
|
|
|
|
self.x = x
|
|
|
|
self.y = y
|
2014-12-26 22:15:48 +00:00
|
|
|
|
2016-10-27 16:48:12 +00:00
|
|
|
def __add__(self, other):
|
|
|
|
return Point(self.x+other.x, self.y+other.y)
|
2014-12-26 22:15:48 +00:00
|
|
|
|
2016-10-27 16:48:12 +00:00
|
|
|
def __sub__(self, other):
|
|
|
|
return Point(self.x-other.x, self.y-other.y)
|
2014-12-26 22:15:48 +00:00
|
|
|
|
2016-10-27 16:48:12 +00:00
|
|
|
def mul(self, scalar):
|
|
|
|
return Point(self.x*scalar, self.y*scalar)
|
2016-10-27 16:49:05 +00:00
|
|
|
|
2016-10-27 16:48:12 +00:00
|
|
|
def __mul__(self, other):
|
|
|
|
if isinstance(other, Point):
|
|
|
|
# dot product
|
|
|
|
return self.x * other.x + self.y * other.y
|
|
|
|
elif isinstance(other, (int, float)):
|
|
|
|
return self.mul(other)
|
|
|
|
else:
|
|
|
|
raise ValueError("cannot multiply Point by %s" % type(other))
|
2016-02-14 03:13:47 +00:00
|
|
|
|
2016-10-27 16:48:12 +00:00
|
|
|
def __rmul__(self, other):
|
|
|
|
if isinstance(other, (int, float)):
|
|
|
|
return self.mul(other)
|
|
|
|
else:
|
|
|
|
raise ValueError("cannot multiply Point by %s" % type(other))
|
2016-02-14 03:13:47 +00:00
|
|
|
|
2016-10-27 16:48:12 +00:00
|
|
|
def __repr__(self):
|
|
|
|
return "Pt(%s,%s)" % (self.x,self.y)
|
2014-12-26 22:15:48 +00:00
|
|
|
|
2016-10-27 16:48:12 +00:00
|
|
|
def length(self):
|
|
|
|
return math.sqrt(math.pow(self.x,2.0)+math.pow(self.y,2.0))
|
2014-12-26 22:15:48 +00:00
|
|
|
|
2016-10-27 16:48:12 +00:00
|
|
|
def unit(self):
|
|
|
|
return self.mul(1.0/self.length())
|
2014-12-26 22:15:48 +00:00
|
|
|
|
2016-10-27 16:48:12 +00:00
|
|
|
def rotate_left(self):
|
|
|
|
return Point(-self.y, self.x)
|
2014-12-26 22:15:48 +00:00
|
|
|
|
2016-10-27 16:48:12 +00:00
|
|
|
def rotate(self, angle):
|
|
|
|
return Point(self.x * math.cos(angle) - self.y * math.sin(angle), self.y * math.cos(angle) + self.x * math.sin(angle))
|
2015-12-15 06:09:25 +00:00
|
|
|
|
2016-10-27 16:48:12 +00:00
|
|
|
def as_int(self):
|
|
|
|
return Point(int(round(self.x)), int(round(self.y)))
|
2014-12-26 22:15:48 +00:00
|
|
|
|
2016-10-27 16:48:12 +00:00
|
|
|
def as_tuple(self):
|
|
|
|
return (self.x,self.y)
|
2014-12-26 22:15:48 +00:00
|
|
|
|
2016-10-27 16:48:12 +00:00
|
|
|
def __cmp__(self, other):
|
|
|
|
return cmp(self.as_tuple(), other.as_tuple())
|
2014-12-26 22:15:48 +00:00
|
|
|
|
2016-10-26 19:04:15 +00:00
|
|
|
class Stitch(Point):
|
|
|
|
def __init__(self, x, y, color=None, jumpStitch=False):
|
|
|
|
Point.__init__(self, x, y)
|
|
|
|
self.color = color
|
|
|
|
self.jumpStitch = jumpStitch
|
2014-12-26 22:15:48 +00:00
|
|
|
|
2016-10-26 19:04:15 +00:00
|
|
|
class Embroidery:
|
2016-10-27 16:48:12 +00:00
|
|
|
def __init__(self, stitches, pixels_per_millimeter=1):
|
|
|
|
self.stitches = deepcopy(stitches)
|
|
|
|
self.scale(1.0/pixels_per_millimeter)
|
|
|
|
self.scale((1, -1))
|
|
|
|
self.translate_to_origin()
|
|
|
|
|
|
|
|
def translate_to_origin(self):
|
|
|
|
if (len(self.stitches)==0):
|
|
|
|
return
|
|
|
|
(maxx,maxy) = (self.stitches[0].x,self.stitches[0].y)
|
|
|
|
(minx,miny) = (self.stitches[0].x,self.stitches[0].y)
|
|
|
|
for p in self.stitches:
|
|
|
|
minx = min(minx,p.x)
|
|
|
|
miny = min(miny,p.y)
|
|
|
|
maxx = max(maxx,p.x)
|
|
|
|
maxy = max(maxy,p.y)
|
|
|
|
sx = maxx-minx
|
|
|
|
sy = maxy-miny
|
|
|
|
|
|
|
|
self.translate(-minx, -miny)
|
|
|
|
return (minx, miny)
|
|
|
|
|
|
|
|
def translate(self, dx, dy):
|
|
|
|
for p in self.stitches:
|
|
|
|
p.x += dx
|
|
|
|
p.y += dy
|
|
|
|
|
|
|
|
def scale(self, sc):
|
|
|
|
if not isinstance(sc, (tuple, list)):
|
|
|
|
sc = (sc, sc)
|
|
|
|
for p in self.stitches:
|
|
|
|
p.x *= sc[0]
|
|
|
|
p.y *= sc[1]
|
2016-10-27 16:49:05 +00:00
|
|
|
|
2016-10-27 16:48:12 +00:00
|
|
|
def export_ksm(self):
|
|
|
|
str = ""
|
|
|
|
self.pos = Point(0,0)
|
|
|
|
lastColor = None
|
|
|
|
for stitch in self.stitches:
|
|
|
|
if (lastColor!=None and stitch.color!=lastColor):
|
|
|
|
mode_byte = 0x99
|
|
|
|
#dbg.write("Color change!\n")
|
|
|
|
else:
|
|
|
|
mode_byte = 0x80
|
|
|
|
#dbg.write("color still %s\n" % stitch.color)
|
|
|
|
lastColor = stitch.color
|
|
|
|
new_int = stitch.as_int()
|
|
|
|
old_int = self.pos.as_int()
|
|
|
|
delta = new_int - old_int
|
|
|
|
assert(abs(delta.x)<=127)
|
|
|
|
assert(abs(delta.y)<=127)
|
|
|
|
str+=chr(abs(delta.y))
|
|
|
|
str+=chr(abs(delta.x))
|
|
|
|
if (delta.y<0):
|
|
|
|
mode_byte |= 0x20
|
|
|
|
if (delta.x<0):
|
|
|
|
mode_byte |= 0x40
|
|
|
|
str+=chr(mode_byte)
|
|
|
|
self.pos = stitch
|
|
|
|
return str
|
|
|
|
|
|
|
|
def export_melco(self):
|
|
|
|
self.str = ""
|
|
|
|
self.pos = self.stitches[0]
|
|
|
|
#dbg.write("stitch count: %d\n" % len(self.stitches))
|
|
|
|
lastColor = None
|
|
|
|
numColors = 0x0
|
|
|
|
for stitch in self.stitches[1:]:
|
|
|
|
if (lastColor!=None and stitch.color!=lastColor):
|
|
|
|
numColors += 1
|
|
|
|
# color change
|
|
|
|
self.str += chr(0x80)
|
|
|
|
self.str += chr(0x01)
|
|
|
|
# self.str += chr(numColors)
|
|
|
|
# self.str += chr(((numColors+0x80)>>8)&0xff)
|
|
|
|
# self.str += chr(((numColors+0x80)>>0)&0xff)
|
|
|
|
lastColor = stitch.color
|
|
|
|
new_int = stitch.as_int()
|
|
|
|
old_int = self.pos.as_int()
|
|
|
|
delta = new_int - old_int
|
|
|
|
|
|
|
|
def move(x,y):
|
|
|
|
if (x<0): x = x + 256
|
|
|
|
self.str+=chr(x)
|
|
|
|
if (y<0): y = y + 256
|
|
|
|
self.str+=chr(y)
|
2016-10-27 16:49:05 +00:00
|
|
|
|
2016-10-27 16:48:12 +00:00
|
|
|
while (delta.x!=0 or delta.y!=0):
|
|
|
|
def clamp(v):
|
|
|
|
if (v>127):
|
|
|
|
v = 127
|
|
|
|
if (v<-127):
|
|
|
|
v = -127
|
|
|
|
return v
|
|
|
|
dx = clamp(delta.x)
|
|
|
|
dy = clamp(delta.y)
|
|
|
|
move(dx,dy)
|
|
|
|
delta.x -= dx
|
|
|
|
delta.y -= dy
|
2016-10-27 16:49:05 +00:00
|
|
|
|
2016-10-27 16:48:12 +00:00
|
|
|
#dbg.write("Stitch: %s delta %s\n" % (stitch, delta))
|
|
|
|
self.pos = stitch
|
|
|
|
return self.str
|
|
|
|
|
|
|
|
def export_csv(self):
|
|
|
|
self.str = ""
|
|
|
|
self.str += '"#","[THREAD_NUMBER]","[RED]","[GREEN]","[BLUE]","[DESCRIPTION]","[CATALOG_NUMBER]"\n'
|
|
|
|
self.str += '"#","[STITCH_TYPE]","[X]","[Y]"\n'
|
|
|
|
|
|
|
|
lastStitch = None
|
|
|
|
colorIndex = 0
|
|
|
|
for stitch in self.stitches:
|
|
|
|
if lastStitch is not None and stitch.color != lastStitch.color:
|
|
|
|
self.str += '"*","COLOR","%f","%f"\n' % (lastStitch.x, lastStitch.y)
|
|
|
|
if lastStitch is None or stitch.color != lastStitch.color:
|
|
|
|
colorIndex += 1
|
|
|
|
self.str += '"$","%d","%d","%d","%d","(null)","(null)"\n' % (
|
|
|
|
colorIndex,
|
|
|
|
int(stitch.color[1:3], 16),
|
|
|
|
int(stitch.color[3:5], 16),
|
|
|
|
int(stitch.color[5:7], 16))
|
|
|
|
if stitch.jumpStitch:
|
|
|
|
self.str += '"*","JUMP","%f","%f"\n' % (stitch.x, stitch.y)
|
|
|
|
self.str += '"*","STITCH","%f","%f"\n' % (stitch.x, stitch.y)
|
|
|
|
lastStitch = stitch
|
|
|
|
self.str += '"*","END","%f","%f"\n' % (lastStitch.x, lastStitch.y)
|
|
|
|
return self.str
|
|
|
|
|
|
|
|
def export_gcode(self):
|
|
|
|
ret = []
|
|
|
|
lastColor = None
|
|
|
|
for stitch in self.stitches:
|
|
|
|
if stitch.color != lastColor:
|
|
|
|
ret.append('M0 ;MSG, Color change; prepare for %s\n' % stitch.color)
|
|
|
|
lastColor = stitch.color
|
|
|
|
ret.append('G1 X%f Y%f\n' % stitch.as_tuple())
|
|
|
|
ret.append('M0 ;MSG, EMBROIDER stitch\n')
|
|
|
|
return ''.join(ret)
|
|
|
|
|
|
|
|
def export(self, filename, format):
|
|
|
|
fp = open(filename, "wb")
|
|
|
|
|
|
|
|
if format == "melco":
|
|
|
|
fp.write(self.export_melco())
|
|
|
|
elif format == "csv":
|
|
|
|
fp.write(self.export_csv())
|
|
|
|
elif format == "gcode":
|
|
|
|
fp.write(self.export_gcode())
|
|
|
|
fp.close()
|
2014-12-27 16:30:54 +00:00
|
|
|
|
2014-12-26 22:15:48 +00:00
|
|
|
class Test:
|
2016-10-27 16:48:12 +00:00
|
|
|
def __init__(self):
|
|
|
|
emb = Embroidery()
|
|
|
|
for x in range(0,301,30):
|
|
|
|
emb.addStitch(Point(x, 0));
|
|
|
|
emb.addStitch(Point(x, 15));
|
|
|
|
emb.addStitch(Point(x, 0));
|
|
|
|
|
|
|
|
for x in range(300,-1,-30):
|
|
|
|
emb.addStitch(Point(x, -12));
|
|
|
|
emb.addStitch(Point(x, -27));
|
|
|
|
emb.addStitch(Point(x, -12));
|
|
|
|
|
|
|
|
fp = open("test.exp", "wb")
|
|
|
|
fp.write(emb.export_melco())
|
|
|
|
fp.close()
|
2014-12-26 22:15:48 +00:00
|
|
|
|
|
|
|
class Turtle:
|
2016-10-27 16:48:12 +00:00
|
|
|
def __init__(self):
|
|
|
|
self.emb = Embroidery()
|
|
|
|
self.pos = Point(0.0,0.0)
|
|
|
|
self.dir = Point(1.0,0.0)
|
|
|
|
self.emb.addStitch(self.pos)
|
|
|
|
|
|
|
|
def forward(self, dist):
|
|
|
|
self.pos = self.pos+self.dir.mul(dist)
|
|
|
|
self.emb.addStitch(self.pos)
|
|
|
|
|
|
|
|
def turn(self, degreesccw):
|
|
|
|
radcw = -degreesccw/180.0*3.141592653589
|
|
|
|
self.dir = Point(
|
|
|
|
math.cos(radcw)*self.dir.x-math.sin(radcw)*self.dir.y,
|
|
|
|
math.sin(radcw)*self.dir.x+math.cos(radcw)*self.dir.y)
|
|
|
|
|
|
|
|
def right(self, degreesccw):
|
|
|
|
self.turn(degreesccw)
|
|
|
|
|
|
|
|
def left(self, degreesccw):
|
|
|
|
self.turn(-degreesccw)
|
2016-10-27 16:49:05 +00:00
|
|
|
|
2014-12-26 22:15:48 +00:00
|
|
|
class Koch(Turtle):
|
2016-10-27 16:48:12 +00:00
|
|
|
def __init__(self, depth):
|
|
|
|
Turtle.__init__(self)
|
|
|
|
|
|
|
|
edgelen = 750.0
|
|
|
|
for i in range(3):
|
|
|
|
self.edge(depth, edgelen)
|
|
|
|
self.turn(120.0)
|
|
|
|
|
|
|
|
fp = open("koch%d.exp" % depth, "wb")
|
|
|
|
fp.write(self.emb.export_melco())
|
|
|
|
fp.close()
|
2016-10-27 16:49:05 +00:00
|
|
|
|
2016-10-27 16:48:12 +00:00
|
|
|
def edge(self, depth, dist):
|
|
|
|
if (depth==0):
|
|
|
|
self.forward(dist)
|
|
|
|
else:
|
|
|
|
self.edge(depth-1, dist/3.0)
|
|
|
|
self.turn(-60.0)
|
|
|
|
self.edge(depth-1, dist/3.0)
|
|
|
|
self.turn(120.0)
|
|
|
|
self.edge(depth-1, dist/3.0)
|
|
|
|
self.turn(-60.0)
|
|
|
|
self.edge(depth-1, dist/3.0)
|
2014-12-26 22:15:48 +00:00
|
|
|
|
|
|
|
class Hilbert(Turtle):
|
2016-10-27 16:48:12 +00:00
|
|
|
def __init__(self, level):
|
|
|
|
Turtle.__init__(self)
|
|
|
|
|
|
|
|
self.size = 10.0
|
|
|
|
self.hilbert(level, 90.0)
|
|
|
|
|
|
|
|
fp = open("hilbert%d.exp" % level, "wb")
|
|
|
|
fp.write(self.emb.export_melco())
|
|
|
|
fp.close()
|
|
|
|
|
|
|
|
# http://en.wikipedia.org/wiki/Hilbert_curve#Python
|
|
|
|
def hilbert(self, level, angle):
|
|
|
|
if (level==0):
|
|
|
|
return
|
|
|
|
self.right(angle)
|
|
|
|
self.hilbert(level-1, -angle)
|
|
|
|
self.forward(self.size)
|
|
|
|
self.left(angle)
|
|
|
|
self.hilbert(level-1, angle)
|
|
|
|
self.forward(self.size)
|
|
|
|
self.hilbert(level-1, angle)
|
|
|
|
self.left(angle)
|
|
|
|
self.forward(self.size)
|
|
|
|
self.hilbert(level-1, -angle)
|
|
|
|
self.right(angle)
|
2014-12-26 22:15:48 +00:00
|
|
|
|
|
|
|
if (__name__=='__main__'):
|
2016-10-27 16:48:12 +00:00
|
|
|
#Koch(4)
|
|
|
|
Hilbert(6)
|