kopia lustrzana https://github.com/inkstitch/inkstitch
Tie-in and tie-off (#100)
* turn inkstitch.py into a module * add running stitch library function * tie-in and tie-off * remove temporary testing codepull/114/head
rodzic
e55b8d79f9
commit
88b4ff3e66
|
@ -4,7 +4,7 @@
|
|||
<id>jonh.embroider</id>
|
||||
<dependency type="executable" location="extensions">embroider.py</dependency>
|
||||
<dependency type="executable" location="extensions">inkex.py</dependency>
|
||||
<param name="collapse_len_mm" type="float" min="0.0" max="10.0" _gui-text="Maximum collapse length (mm)">0.0</param>
|
||||
<param name="collapse_len_mm" type="float" min="0.0" max="10.0" _gui-text="Collapse length (mm)"> _gui-description="Jump stitches smaller than this will be treated as normal stitches.">3.0</param>
|
||||
<param name="hide_layers" type="boolean" _gui-text="Hide other layers" description="Hide all other top-level layers when the embroidery layer is generated, in order to make stitching discernable.">true</param>
|
||||
<param name="output_format" type="optiongroup" _gui-text="Output file format" appearance="minimal">
|
||||
<_option value="csv">Comma Separated Values Format(.CSV)</_option>
|
||||
|
|
64
embroider.py
64
embroider.py
|
@ -36,6 +36,8 @@ from pprint import pformat
|
|||
|
||||
import inkstitch
|
||||
from inkstitch import _, cache, dbg, param, EmbroideryElement, get_nodes, SVG_POLYLINE_TAG, SVG_GROUP_TAG, PIXELS_PER_MM, get_viewbox_transform
|
||||
from inkstitch.stitches import running_stitch
|
||||
from inkstitch.utils import cut_path
|
||||
|
||||
class Fill(EmbroideryElement):
|
||||
element_name = _("Fill")
|
||||
|
@ -934,7 +936,6 @@ class Stroke(EmbroideryElement):
|
|||
return self.dashed or self.width <= 0.5
|
||||
|
||||
def stroke_points(self, emb_point_list, zigzag_spacing, stroke_width):
|
||||
patch = Patch(color=self.color)
|
||||
p0 = emb_point_list[0]
|
||||
rho = 0.0
|
||||
side = 1
|
||||
|
@ -1556,7 +1557,62 @@ def process_trim(stitches, next_stitch):
|
|||
stitches[-3].trim = True
|
||||
|
||||
|
||||
def patches_to_stitches(patch_list, collapse_len_px=0):
|
||||
def add_tie(stitches, tie_path):
|
||||
color = tie_path[0].color
|
||||
|
||||
tie_path = cut_path(tie_path, 0.6)
|
||||
tie_stitches = running_stitch(tie_path, 0.3)
|
||||
tie_stitches = [inkstitch.Stitch(*stitch, color=color) for stitch in tie_stitches]
|
||||
|
||||
stitches.extend(tie_stitches[1:])
|
||||
stitches.extend(list(reversed(tie_stitches))[1:])
|
||||
|
||||
|
||||
def add_tie_off(stitches):
|
||||
if not stitches:
|
||||
return
|
||||
|
||||
add_tie(stitches, list(reversed(stitches)))
|
||||
|
||||
|
||||
def add_tie_in(stitches, upcoming_stitches):
|
||||
if not upcoming_stitches:
|
||||
return
|
||||
|
||||
add_tie(stitches, upcoming_stitches)
|
||||
|
||||
|
||||
def add_ties(original_stitches):
|
||||
"""Add tie-off before and after trims, jumps, and color changes."""
|
||||
|
||||
# we're going to copy most stitches over, adding tie in/off as needed
|
||||
stitches = []
|
||||
|
||||
need_tie_in = True
|
||||
|
||||
for i, stitch in enumerate(original_stitches):
|
||||
is_special = stitch.trim or stitch.jump or stitch.stop
|
||||
|
||||
if is_special and not need_tie_in:
|
||||
add_tie_off(stitches)
|
||||
stitches.append(stitch)
|
||||
need_tie_in = True
|
||||
elif need_tie_in and not is_special:
|
||||
stitches.append(stitch)
|
||||
add_tie_in(stitches, original_stitches[i:])
|
||||
need_tie_in = False
|
||||
else:
|
||||
stitches.append(stitch)
|
||||
|
||||
# add tie-off at the end if we ended on a normal stitch
|
||||
if not is_special:
|
||||
add_tie_off(stitches)
|
||||
|
||||
# overwrite the stitch plan with our new one that contains ties
|
||||
original_stitches[:] = stitches
|
||||
|
||||
|
||||
def patches_to_stitches(patch_list, collapse_len_px=3.0):
|
||||
stitches = []
|
||||
|
||||
last_stitch = None
|
||||
|
@ -1604,6 +1660,8 @@ def patches_to_stitches(patch_list, collapse_len_px=0):
|
|||
if patch.stop_after:
|
||||
process_stop_after(stitches)
|
||||
|
||||
add_ties(stitches)
|
||||
|
||||
return stitches
|
||||
|
||||
def stitches_to_polylines(stitches):
|
||||
|
@ -1677,7 +1735,7 @@ class Embroider(inkex.Effect):
|
|||
inkex.Effect.__init__(self)
|
||||
self.OptionParser.add_option("-c", "--collapse_len_mm",
|
||||
action="store", type="float",
|
||||
dest="collapse_length_mm", default=0.0,
|
||||
dest="collapse_length_mm", default=3.0,
|
||||
help="max collapse length (mm)")
|
||||
self.OptionParser.add_option("--hide_layers",
|
||||
action="store", type="choice",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
from running_stitch import *
|
|
@ -0,0 +1,62 @@
|
|||
""" Utility functions to produce running stitches. """
|
||||
|
||||
|
||||
def running_stitch(points, stitch_length):
|
||||
"""Generate running stitch along a path.
|
||||
|
||||
Given a path and a stitch length, walk along the path in increments of the
|
||||
stitch length. If sharp corners are encountered, an extra stitch will be
|
||||
added at the corner to avoid rounding the corner. The starting and ending
|
||||
point are always stitched.
|
||||
|
||||
The path is described by a set of line segments, each connected to the next.
|
||||
The line segments are described by a sequence of points.
|
||||
"""
|
||||
|
||||
if len(points) < 2:
|
||||
return []
|
||||
|
||||
output = [points[0]]
|
||||
segment_start = points[0]
|
||||
last_segment_direction = None
|
||||
|
||||
# This tracks the distance we've travelled along the current segment so
|
||||
# far. Each time we make a stitch, we add the stitch_length to this
|
||||
# value. If we fall off the end of the current segment, we carry over
|
||||
# the remainder to the next segment.
|
||||
distance = 0.0
|
||||
|
||||
for segment_end in points[1:]:
|
||||
segment = segment_end - segment_start
|
||||
segment_length = segment.length()
|
||||
segment_direction = segment.unit()
|
||||
|
||||
# corner detection
|
||||
if last_segment_direction:
|
||||
cos_angle_between = segment_direction * last_segment_direction
|
||||
|
||||
# This checks whether the corner is sharper than 45 degrees.
|
||||
if cos_angle_between < 0.5:
|
||||
# Only add the corner point if it's more than 0.1mm away to
|
||||
# avoid a double-stitch.
|
||||
if (segment_start - output[-1]).length() > 0.1:
|
||||
# add a stitch at the corner
|
||||
output.append(segment_start)
|
||||
|
||||
# next stitch needs to be stitch_length along this segment
|
||||
distance = stitch_length
|
||||
|
||||
while distance < segment_length:
|
||||
output.append(segment_start + distance * segment_direction)
|
||||
distance += stitch_length
|
||||
|
||||
# prepare for the next segment
|
||||
segment_start = segment_end
|
||||
last_segment_direction = segment_direction
|
||||
distance -= segment_length
|
||||
|
||||
# stitch the last point unless we're already almos there
|
||||
if (segment_start - points[-1]).length() > 0.1:
|
||||
output.append(segment_start)
|
||||
|
||||
return output
|
|
@ -0,0 +1 @@
|
|||
from geometry import *
|
|
@ -0,0 +1,41 @@
|
|||
from .. import Point as InkstitchPoint
|
||||
from shapely.geometry import LineString, Point as ShapelyPoint
|
||||
|
||||
def cut(line, distance):
|
||||
""" Cuts a LineString in two at a distance from its starting point.
|
||||
|
||||
This is an example in the Shapely documentation.
|
||||
"""
|
||||
if distance <= 0.0 or distance >= line.length:
|
||||
return [LineString(line)]
|
||||
coords = list(line.coords)
|
||||
for i, p in enumerate(coords):
|
||||
pd = line.project(ShapelyPoint(p))
|
||||
if pd == distance:
|
||||
return [
|
||||
LineString(coords[:i+1]),
|
||||
LineString(coords[i:])]
|
||||
if pd > distance:
|
||||
cp = line.interpolate(distance)
|
||||
return [
|
||||
LineString(coords[:i] + [(cp.x, cp.y)]),
|
||||
LineString([(cp.x, cp.y)] + coords[i:])]
|
||||
|
||||
|
||||
def cut_path(points, length):
|
||||
"""Return a subsection of at the start of the path that is length units long.
|
||||
|
||||
Given a path denoted by a set of points, walk along it until we've travelled
|
||||
the specified length and return a new path up to that point.
|
||||
|
||||
If the original path isn't that long, just return it as is.
|
||||
"""
|
||||
|
||||
if len(points) < 2:
|
||||
return points
|
||||
|
||||
path = LineString(points)
|
||||
subpath, rest = cut(path, length)
|
||||
|
||||
return [InkstitchPoint(*point) for point in subpath.coords]
|
||||
|
267
messages.po
267
messages.po
|
@ -1,267 +0,0 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-02-24 20:39-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"
|
||||
"Language: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=CHARSET\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "Fill"
|
||||
msgstr ""
|
||||
|
||||
msgid "Manually routed fill stitching"
|
||||
msgstr ""
|
||||
|
||||
msgid "Angle of lines of stitches"
|
||||
msgstr ""
|
||||
|
||||
msgid "Flip fill (start right-to-left)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Spacing between rows"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum fill stitch length"
|
||||
msgstr ""
|
||||
|
||||
msgid "Stagger rows this many times before repeating"
|
||||
msgstr ""
|
||||
|
||||
msgid "Auto-Fill"
|
||||
msgstr ""
|
||||
|
||||
msgid "Automatically routed fill stitching"
|
||||
msgstr ""
|
||||
|
||||
msgid "Running stitch length (traversal between sections)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Underlay"
|
||||
msgstr ""
|
||||
|
||||
msgid "AutoFill Underlay"
|
||||
msgstr ""
|
||||
|
||||
msgid "Fill angle (default: fill angle + 90 deg)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Row spacing (default: 3x fill row spacing)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Max stitch length"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Unable to autofill. This most often happens because your shape is made up "
|
||||
"of multiple sections that aren't connected."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Unexpected error while generating fill stitches. Please send your SVG file "
|
||||
"to lexelby@github."
|
||||
msgstr ""
|
||||
|
||||
msgid "Satin stitch along paths"
|
||||
msgstr ""
|
||||
|
||||
msgid "Running stitch length"
|
||||
msgstr ""
|
||||
|
||||
msgid "Zig-zag spacing (peak-to-peak)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Repeats"
|
||||
msgstr ""
|
||||
|
||||
msgid "Satin Column"
|
||||
msgstr ""
|
||||
|
||||
msgid "Custom satin column"
|
||||
msgstr ""
|
||||
|
||||
msgid "Pull compensation"
|
||||
msgstr ""
|
||||
|
||||
msgid "Contour underlay"
|
||||
msgstr ""
|
||||
|
||||
msgid "Contour Underlay"
|
||||
msgstr ""
|
||||
|
||||
msgid "Stitch length"
|
||||
msgstr ""
|
||||
|
||||
msgid "Contour underlay inset amount"
|
||||
msgstr ""
|
||||
|
||||
msgid "Center-walk underlay"
|
||||
msgstr ""
|
||||
|
||||
msgid "Center-Walk Underlay"
|
||||
msgstr ""
|
||||
|
||||
msgid "Zig-zag underlay"
|
||||
msgstr ""
|
||||
|
||||
msgid "Zig-zag Underlay"
|
||||
msgstr ""
|
||||
|
||||
msgid "Zig-Zag spacing (peak-to-peak)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Inset amount (default: half of contour underlay inset)"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"One or more rails crosses itself, and this is not allowed. Please split "
|
||||
"into multiple satin columns."
|
||||
msgstr ""
|
||||
|
||||
msgid "satin column: One or more of the rungs doesn't intersect both rails."
|
||||
msgstr ""
|
||||
|
||||
msgid "Each rail should intersect both rungs once."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"satin column: One or more of the rungs intersects the rails more than once."
|
||||
msgstr ""
|
||||
|
||||
#, python-format
|
||||
msgid "satin column: object %s has a fill (but should not)"
|
||||
msgstr ""
|
||||
|
||||
#, python-format
|
||||
msgid ""
|
||||
"satin column: object %(id)s has two paths with an unequal number of points "
|
||||
"(%(length1)d and %(length2)d)"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
"\n"
|
||||
"Seeing a 'no such option' message? Please restart Inkscape to fix."
|
||||
msgstr ""
|
||||
|
||||
msgid "No embroiderable paths selected."
|
||||
msgstr ""
|
||||
|
||||
msgid "No embroiderable paths found in document."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Tip: use Path -> Object to Path to convert non-paths before embroidering."
|
||||
msgstr ""
|
||||
|
||||
msgid "Embroidery"
|
||||
msgstr ""
|
||||
|
||||
msgid "These settings will be applied to 1 object."
|
||||
msgstr ""
|
||||
|
||||
#, python-format
|
||||
msgid "These settings will be applied to %d objects."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Some settings had different values across objects. Select a value from the "
|
||||
"dropdown or enter a new one."
|
||||
msgstr ""
|
||||
|
||||
#, python-format
|
||||
msgid "Disabling this tab will disable the following %d tabs."
|
||||
msgstr ""
|
||||
|
||||
msgid "Disabling this tab will disable the following tab."
|
||||
msgstr ""
|
||||
|
||||
#, python-format
|
||||
msgid "Enabling this tab will disable %s and vice-versa."
|
||||
msgstr ""
|
||||
|
||||
msgid "Inkscape objects"
|
||||
msgstr ""
|
||||
|
||||
msgid "Embroidery Params"
|
||||
msgstr ""
|
||||
|
||||
msgid "Presets"
|
||||
msgstr ""
|
||||
|
||||
msgid "Load"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add"
|
||||
msgstr ""
|
||||
|
||||
msgid "Overwrite"
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
msgid "Use Last Settings"
|
||||
msgstr ""
|
||||
|
||||
msgid "Apply and Quit"
|
||||
msgstr ""
|
||||
|
||||
msgid "Preview"
|
||||
msgstr ""
|
||||
|
||||
msgid "Internal Error"
|
||||
msgstr ""
|
||||
|
||||
msgid "Please enter or select a preset name first."
|
||||
msgstr ""
|
||||
|
||||
msgid "Preset"
|
||||
msgstr ""
|
||||
|
||||
#, python-format
|
||||
msgid "Preset \"%s\" not found."
|
||||
msgstr ""
|
||||
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Preset \"%s\" already exists. Please use another name or press \"Overwrite\""
|
||||
msgstr ""
|
||||
|
||||
msgid "Embroidery Simulation"
|
||||
msgstr ""
|
||||
|
||||
#, python-format
|
||||
msgid "parseLengthWithUnits: unknown unit %s"
|
||||
msgstr ""
|
||||
|
||||
#, python-format
|
||||
msgid "Unknown unit: %s"
|
||||
msgstr ""
|
||||
|
||||
msgid "TRIM after"
|
||||
msgstr ""
|
||||
|
||||
msgid "Trim thread after this object (for supported machines and file formats)"
|
||||
msgstr ""
|
||||
|
||||
msgid "STOP after"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Add STOP instruction after this object (for supported machines and file "
|
||||
"formats)"
|
||||
msgstr ""
|
Ładowanie…
Reference in New Issue