inkstitch/PyEmb.py

161 wiersze
4.5 KiB
Python

#!/usr/bin/env python
# http://www.achatina.de/sewing/main/TECHNICL.HTM
import math
import libembroidery
PIXELS_PER_MM = 96 / 25.4
try:
from functools import lru_cache
except ImportError:
from backports.functools_lru_cache import lru_cache
# simplify use of lru_cache decorator
def cache(*args, **kwargs):
return lru_cache(maxsize=None)(*args, **kwargs)
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Point(self.x - other.x, self.y - other.y)
def mul(self, scalar):
return Point(self.x * scalar, self.y * scalar)
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 Point(self.x * other, self.y * other)
else:
raise ValueError("cannot multiply Point by %s" % type(other))
def __rmul__(self, other):
if isinstance(other, (int, float)):
return self.__mul__(other)
else:
raise ValueError("cannot multiply Point by %s" % type(other))
def __repr__(self):
return "Point(%s,%s)" % (self.x, self.y)
def length(self):
return math.sqrt(math.pow(self.x, 2.0) + math.pow(self.y, 2.0))
def unit(self):
return self.mul(1.0 / self.length())
def rotate_left(self):
return Point(-self.y, self.x)
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))
def as_int(self):
return Point(int(round(self.x)), int(round(self.y)))
def as_tuple(self):
return (self.x, self.y)
def __cmp__(self, other):
return cmp(self.as_tuple(), other.as_tuple())
def __getitem__(self, item):
return self.as_tuple()[item]
def __len__(self):
return 2
class Stitch(Point):
def __init__(self, x, y, color=None, jump=False, stop=False, trim=False):
self.x = x
self.y = y
self.color = color
self.jump = jump
self.trim = trim
self.stop = stop
def __repr__(self):
return "Stitch(%s, %s, %s, %s, %s, %s)" % (self.x, self.y, self.color, "JUMP" if self.jump else "", "TRIM" if self.trim else "", "STOP" if self.stop else "")
def make_thread(color):
# strip off the leading "#"
if color.startswith("#"):
color = color[1:]
thread = libembroidery.EmbThread()
thread.color = libembroidery.embColor_fromHexStr(color)
thread.description = color
thread.catalogNumber = ""
return thread
def add_thread(pattern, thread):
"""Add a thread to a pattern and return the thread's index"""
libembroidery.embPattern_addThread(pattern, thread)
return libembroidery.embThreadList_count(pattern.threadList) - 1
def get_flags(stitch):
flags = 0
if stitch.jump:
flags |= libembroidery.JUMP
if stitch.trim:
flags |= libembroidery.TRIM
if stitch.stop:
flags |= libembroidery.STOP
return flags
def write_embroidery_file(file_path, stitches):
# Embroidery machines don't care about our canvas size, so we relocate the
# design to the origin. It might make sense to center it about the origin
# instead.
min_x = min(stitch.x for stitch in stitches)
min_y = min(stitch.y for stitch in stitches)
pattern = libembroidery.embPattern_create()
threads = {}
last_color = None
for stitch in stitches:
if stitch.color != last_color:
if stitch.color not in threads:
thread = make_thread(stitch.color)
thread_index = add_thread(pattern, thread)
threads[stitch.color] = thread_index
else:
thread_index = threads[stitch.color]
libembroidery.embPattern_changeColor(pattern, thread_index)
last_color = stitch.color
flags = get_flags(stitch)
libembroidery.embPattern_addStitchAbs(pattern, stitch.x - min_x, stitch.y - min_y, flags, 0)
libembroidery.embPattern_addStitchAbs(pattern, stitch.x - min_x, stitch.y - min_y, libembroidery.END, 0)
# convert from pixels to millimeters
libembroidery.embPattern_scale(pattern, 1/PIXELS_PER_MM)
# SVG and embroidery disagree on the direction of the Y axis
libembroidery.embPattern_flipVertical(pattern)
libembroidery.embPattern_write(pattern, file_path)