user can specify origin for embroidery output by setting up guides (#52)

* user can specify origin using guides

* embroidery origin defaults to center of canvas
pull/101/head^2
Lex Neva 2018-02-26 19:42:18 -05:00 zatwierdzone przez GitHub
rodzic e958a7f82c
commit 4e7520c013
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
3 zmienionych plików z 83 dodań i 19 usunięć

Wyświetl plik

@ -1763,7 +1763,7 @@ class Embroider(inkex.Effect):
patches = elements_to_patches(self.elements)
stitches = patches_to_stitches(patches, self.options.collapse_length_mm * PIXELS_PER_MM)
inkstitch.write_embroidery_file(self.get_output_path(), stitches)
inkstitch.write_embroidery_file(self.get_output_path(), stitches, self.document.getroot())
new_layer = inkex.etree.SubElement(self.document.getroot(), SVG_GROUP_TAG, {})
new_layer.set('id', self.uniqueId("embroidery"))

Wyświetl plik

@ -14,6 +14,7 @@ import simpletransform
from bezmisc import bezierlength, beziertatlength, bezierpointatt
from cspsubdiv import cspsubdiv
import cubicsuperpath
from shapely import geometry as shgeo
try:
@ -126,12 +127,17 @@ def convert_length(length):
raise ValueError(_("Unknown unit: %s") % units)
@cache
def get_doc_size(svg):
doc_width = convert_length(svg.get('width'))
doc_height = convert_length(svg.get('height'))
return doc_width, doc_height
@cache
def get_viewbox_transform(node):
# somewhat cribbed from inkscape-silhouette
doc_width = convert_length(node.get('width'))
doc_height = convert_length(node.get('height'))
doc_width, doc_height = get_doc_size(node)
viewbox = node.get('viewBox').strip().replace(',', ' ').split()
@ -504,12 +510,70 @@ def get_flags(stitch):
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)
def _string_to_floats(string):
floats = string.split(',')
return [float(num) for num in floats]
def get_origin(svg):
# The user can specify the embroidery origin by defining two guides
# named "embroidery origin" that intersect.
namedview = svg.find(inkex.addNS('namedview', 'sodipodi'))
all_guides = namedview.findall(inkex.addNS('guide', 'sodipodi'))
label_attribute = inkex.addNS('label', 'inkscape')
guides = [guide for guide in all_guides
if guide.get(label_attribute, "").startswith("embroidery origin")]
# document size used below
doc_size = list(get_doc_size(svg))
# convert the size from viewbox-relative to real-world pixels
viewbox_transform = get_viewbox_transform(svg)
simpletransform.applyTransformToPoint(simpletransform.invertTransform(viewbox_transform), doc_size)
default = [doc_size[0] / 2.0, doc_size[1] / 2.0]
simpletransform.applyTransformToPoint(viewbox_transform, default)
default = Point(*default)
if len(guides) < 2:
return default
# Find out where the guides intersect. Only pay attention to the first two.
guides = guides[:2]
lines = []
for guide in guides:
# inkscape's Y axis is reversed from SVG's, and the guide is in inkscape coordinates
position = Point(*_string_to_floats(guide.get('position')))
position.y = doc_size[1] - position.y
# This one baffles me. I think inkscape might have gotten the order of
# their vector wrong?
parts = _string_to_floats(guide.get('orientation'))
direction = Point(parts[1], parts[0])
# We have a theoretically infinite line defined by a point on the line
# and a vector direction. Shapely can only deal in concrete line
# segments, so we'll pick points really far in either direction on the
# line and call it good enough.
lines.append(shgeo.LineString((position + 100000 * direction, position - 100000 * direction)))
intersection = lines[0].intersection(lines[1])
if isinstance(intersection, shgeo.Point):
origin = [intersection.x, intersection.y]
simpletransform.applyTransformToPoint(viewbox_transform, origin)
return Point(*origin)
else:
# Either the two guides are the same line, or they're parallel.
return default
def write_embroidery_file(file_path, stitches, svg):
origin = get_origin(svg)
pattern = libembroidery.embPattern_create()
last_color = None
@ -520,9 +584,9 @@ def write_embroidery_file(file_path, stitches):
last_color = stitch.color
flags = get_flags(stitch)
libembroidery.embPattern_addStitchAbs(pattern, stitch.x - min_x, stitch.y - min_y, flags, 1)
libembroidery.embPattern_addStitchAbs(pattern, stitch.x - origin.x, stitch.y - origin.y, flags, 1)
libembroidery.embPattern_addStitchAbs(pattern, stitch.x - min_x, stitch.y - min_y, libembroidery.END, 1)
libembroidery.embPattern_addStitchAbs(pattern, stitch.x - origin.x, stitch.y - origin.y, libembroidery.END, 1)
# convert from pixels to millimeters
libembroidery.embPattern_scale(pattern, 1/PIXELS_PER_MM)

Wyświetl plik

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-02-24 20:25-0500\n"
"POT-Creation-Date: 2018-02-26 19:33-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -312,29 +312,29 @@ msgstr ""
msgid "Embroidery Simulation"
msgstr ""
#: inkstitch.py:94
#: inkstitch.py:95
#, python-format
msgid "parseLengthWithUnits: unknown unit %s"
msgstr ""
#: inkstitch.py:126
#: inkstitch.py:127
#, python-format
msgid "Unknown unit: %s"
msgstr ""
#: inkstitch.py:329
#: inkstitch.py:335
msgid "TRIM after"
msgstr ""
#: inkstitch.py:330
#: inkstitch.py:336
msgid "Trim thread after this object (for supported machines and file formats)"
msgstr ""
#: inkstitch.py:339
#: inkstitch.py:345
msgid "STOP after"
msgstr ""
#: inkstitch.py:340
#: inkstitch.py:346
msgid ""
"Add STOP instruction after this object (for supported machines and file "
"formats)"