summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLex Neva <github@lexneva.name>2016-05-16 22:08:50 -0400
committerLex Neva <github@lexneva.name>2016-05-16 22:08:50 -0400
commit4f5a719899e4086fb952e80b6d55c571d9b64842 (patch)
tree529adcf48454c9a03002e8b16e67cbea4e57172b
parent0e185129472d10210fc89ef99d0a191c21a534aa (diff)
bugfixes
-rw-r--r--embroider.py297
1 files changed, 170 insertions, 127 deletions
diff --git a/embroider.py b/embroider.py
index 5c8b4be7..bcca7042 100644
--- a/embroider.py
+++ b/embroider.py
@@ -105,6 +105,8 @@ def parse_path(node):
def flatten(path, flatness):
"""approximate a path containing beziers with a series of points"""
+ path = deepcopy(path)
+
cspsubdiv(path, flatness)
flattened = []
@@ -194,6 +196,12 @@ class Patch:
else:
self.stitches = []
+ def __add__(self, other):
+ if isinstance(other, Patch):
+ return Patch(self.color, self.sortorder, self.stitches + other.stitches)
+ else:
+ raise TypeError("Patch can only be added to another Patch")
+
def addStitch(self, stitch):
self.stitches.append(stitch)
@@ -1141,15 +1149,6 @@ class Embroider(inkex.Effect):
if len(csp[0]) != len(csp[1]):
self.fatal("satin column: object %s has two paths with an unequal number of points (%s and %s)" % (node_id, len(csp[0]), len(csp[1])))
- def pull_compensation(self, pos1, pos2, pull_compensation_px):
- # Stitches in satin tend to pull toward each other. We can compensate
- # by spreading the points out.
- midpoint = (pos2 + pos1) * 0.5
- pos1 = pos1 + (pos1 - midpoint).unit() * pull_compensation_px
- pos2 = pos2 + (pos2 - midpoint).unit() * pull_compensation_px
-
- return pos1, pos2
-
def satin_column(self, node):
# Stitch a variable-width satin column, zig-zagging between two paths.
@@ -1168,6 +1167,9 @@ class Embroider(inkex.Effect):
# fetch parameters
zigzag_spacing_px = get_float_param(node, "zigzag_spacing", self.zigzag_spacing_px)
pull_compensation_px = get_float_param(node, "pull_compensation", 0)
+ underlay_inset = get_float_param(node, "satin_underlay_inset", 0)
+ underlay_stitch_len_px = get_float_param(node, "stitch_length", self.running_stitch_len_px)
+ underlay = get_boolean_param(node, "satin_underlay", False)
# A path is a collection of tuples, each of the form:
#
@@ -1198,125 +1200,166 @@ class Embroider(inkex.Effect):
sortorder = self.get_sort_order(threadcolor, node)
patch = Patch(color=threadcolor, sortorder=sortorder)
- # Take each bezier segment in turn, drawing zigzags between the two
- # paths in that segment.
-
- remainder_path1 = []
- remainder_path2 = []
-
- for segment in xrange(1, len(path1)):
- # construct the current bezier segments
- bezier1 = (path1[segment - 1][1], # point from previous 3-tuple
- path1[segment - 1][2], # "after" control point from previous 3-tuple
- path1[segment][0], # "before" control point from this 3-tuple
- path1[segment][1], # point from this 3-tuple
- )
-
- bezier2 = (path2[segment - 1][1],
- path2[segment - 1][2],
- path2[segment][0],
- path2[segment][1],
- )
-
- # Here's what I want to be able to do. However, beziertatlength is so incredibly slow that it's unusable.
- #for stitch in xrange(num_zigzags):
- # patch.addStitch(bezierpointatt(bezier1, beziertatlength(bezier1, stitch_len1 * stitch)))
- # patch.addStitch(bezierpointatt(bezier2, beziertatlength(bezier2, stitch_len2 * (stitch + 0.5))))
-
- # Instead, flatten the beziers down to a set of line segments.
- subpath1 = remainder_path1 + flatten([[path1[segment - 1], path1[segment]]], self.options.flat)[0]
- subpath2 = remainder_path2 + flatten([[path2[segment - 1], path2[segment]]], self.options.flat)[0]
-
- len1 = shgeo.LineString(subpath1).length
- len2 = shgeo.LineString(subpath2).length
-
- subpath1 = [PyEmb.Point(*p) for p in subpath1]
- subpath2 = [PyEmb.Point(*p) for p in subpath2]
-
- # Base the number of stitches in each section on the _longest_ of
- # the two beziers. Otherwise, things could get too sparse when one
- # side is significantly longer (e.g. when going around a corner).
- # The risk here is that we poke a hole in the fabric if we try to
- # cram too many stitches on the short bezier. The user will need
- # to avoid this through careful construction of paths.
- num_zigzags = max(len1, len2) / zigzag_spacing_px
-
- stitch_len1 = len1 / num_zigzags
- stitch_len2 = len2 / num_zigzags
-
- # Now do the stitches. Each "zigzag" has a "zig" and a "zag", that
- # is, go from path1 to path2 and then back to path1.
-
- def walk(path, start_pos, start_index, distance):
- # Move <distance> pixels along <path>'s line segments.
- # <start_index> is the index of the line segment in <path> that
- # we're currently on. <start_pos> is where along that line
- # segment we are. Return a new position and index.
-
- pos = start_pos
- index = start_index
-
- if index >= len(path) - 1:
- # it's possible we'll go too far due to inaccuracy in the
- # bezier length calculation
- return start_pos, start_index
-
- while True:
- segment_end = path[index + 1]
- segment_remaining = (segment_end - pos)
- distance_remaining = segment_remaining.length()
-
- if distance_remaining > distance:
- return pos + segment_remaining.unit().mul(distance), index
- else:
- index += 1
-
- if index >= len(path) - 1:
- return segment_end, index
-
- distance -= distance_remaining
- pos = segment_end
-
- pos1 = subpath1[0]
- i1 = 0
-
- pos2 = subpath2[0]
- i2 = 0
-
-# if num_zigzags >= 1.0:
-# for stitch in xrange(int(num_zigzags) + 1):
- for stitch in xrange(int(num_zigzags)):
- # In each iteration, do a "zig" and a "zag".
- for stitch in self.pull_compensation(pos1, pos2, pull_compensation_px):
- patch.addStitch(stitch)
-
- pos2, i2 = walk(subpath2, pos2, i2, stitch_len2)
- pos1, i1 = walk(subpath1, pos1, i1, stitch_len1)
-
- if i1 < len(subpath1) - 1:
- remainder_path1 = [pos1] + subpath1[i1 + 1:]
- else:
- remainder_path1 = []
+ def offset_stitches(pos1, pos2, offset_px):
+ if pos1 == pos2:
+ # if they're the same, we don't know which direction
+ # to offset in, so we have to just return the points
+ return pos1, pos2
+ print >> sys.stderr, pos1, pos2
- if i2 < len(subpath2) - 1:
- remainder_path2 = [pos2] + subpath2[i2 + 1:]
- else:
- remainder_path2 = []
-
- remainder_path1 = [p.as_tuple() for p in remainder_path1]
- remainder_path2 = [p.as_tuple() for p in remainder_path2]
-
- # We're off by one in the algorithm above, so add one more pair of
- # stitches.
-
- for stitch in self.pull_compensation(pos1, pos2, pull_compensation_px):
- patch.addStitch(stitch)
-
- end1 = PyEmb.Point(*remainder_path1[-1])
- end2 = PyEmb.Point(*remainder_path2[-1])
- if (end1 - pos2).length() > 0.2:
- for stitch in self.pull_compensation(end1, end2, pull_compensation_px):
- patch.addStitch(stitch)
+ midpoint = (pos2 + pos1) * 0.5
+ pos1 = pos1 + (pos1 - midpoint).unit() * offset_px
+ pos2 = pos2 + (pos2 - midpoint).unit() * offset_px
+
+ return pos1, pos2
+
+ def calculate_satin(zigzag_spacing, offset):
+ # Take each bezier segment in turn, drawing zigzags between the two
+ # paths in that segment.
+
+ # This code used to construct the Patch directly, but now it
+ # returns the zig-zag stitches as two parallel lists (useful
+ # for underlay)
+ zigs = []
+ zags = []
+
+ def add_satin_stitch(pos1, pos2):
+ # Stitches in satin tend to pull toward each other. We can compensate
+ # by spreading the points out.
+ zig, zag = offset_stitches(pos1, pos2, offset)
+ zigs.append(zig)
+ zags.append(zag)
+
+ remainder_path1 = []
+ remainder_path2 = []
+
+ for segment in xrange(1, len(path1)):
+ # construct the current bezier segments
+ bezier1 = (path1[segment - 1][1], # point from previous 3-tuple
+ path1[segment - 1][2], # "after" control point from previous 3-tuple
+ path1[segment][0], # "before" control point from this 3-tuple
+ path1[segment][1], # point from this 3-tuple
+ )
+
+ bezier2 = (path2[segment - 1][1],
+ path2[segment - 1][2],
+ path2[segment][0],
+ path2[segment][1],
+ )
+
+ # Here's what I want to be able to do. However, beziertatlength is so incredibly slow that it's unusable.
+ #for stitch in xrange(num_zigzags):
+ # patch.addStitch(bezierpointatt(bezier1, beziertatlength(bezier1, stitch_len1 * stitch)))
+ # patch.addStitch(bezierpointatt(bezier2, beziertatlength(bezier2, stitch_len2 * (stitch + 0.5))))
+
+ # Instead, flatten the beziers down to a set of line segments.
+ subpath1 = remainder_path1 + flatten([[path1[segment - 1], path1[segment]]], self.options.flat)[0]
+ subpath2 = remainder_path2 + flatten([[path2[segment - 1], path2[segment]]], self.options.flat)[0]
+
+ len1 = shgeo.LineString(subpath1).length
+ len2 = shgeo.LineString(subpath2).length
+
+ subpath1 = [PyEmb.Point(*p) for p in subpath1]
+ subpath2 = [PyEmb.Point(*p) for p in subpath2]
+
+ # Base the number of stitches in each section on the _longest_ of
+ # the two beziers. Otherwise, things could get too sparse when one
+ # side is significantly longer (e.g. when going around a corner).
+ # The risk here is that we poke a hole in the fabric if we try to
+ # cram too many stitches on the short bezier. The user will need
+ # to avoid this through careful construction of paths.
+ num_zigzags = max(len1, len2) / zigzag_spacing
+
+ stitch_len1 = len1 / num_zigzags
+ stitch_len2 = len2 / num_zigzags
+
+ # Now do the stitches. Each "zigzag" has a "zig" and a "zag", that
+ # is, go from path1 to path2 and then back to path1.
+
+ def walk(path, start_pos, start_index, distance):
+ # Move <distance> pixels along <path>'s line segments.
+ # <start_index> is the index of the line segment in <path> that
+ # we're currently on. <start_pos> is where along that line
+ # segment we are. Return a new position and index.
+
+ pos = start_pos
+ index = start_index
+
+ if index >= len(path) - 1:
+ # it's possible we'll go too far due to inaccuracy in the
+ # bezier length calculation
+ return start_pos, start_index
+
+ while True:
+ segment_end = path[index + 1]
+ segment_remaining = (segment_end - pos)
+ distance_remaining = segment_remaining.length()
+
+ if distance_remaining > distance:
+ return pos + segment_remaining.unit().mul(distance), index
+ else:
+ index += 1
+
+ if index >= len(path) - 1:
+ return segment_end, index
+
+ distance -= distance_remaining
+ pos = segment_end
+
+ pos1 = subpath1[0]
+ i1 = 0
+
+ pos2 = subpath2[0]
+ i2 = 0
+
+ # if num_zigzags >= 1.0:
+ # for stitch in xrange(int(num_zigzags) + 1):
+ for stitch in xrange(int(num_zigzags)):
+ # In each iteration, do a "zig" (pos1) and a "zag" (pos2).
+
+ add_satin_stitch(pos1, pos2)
+
+ pos2, i2 = walk(subpath2, pos2, i2, stitch_len2)
+ pos1, i1 = walk(subpath1, pos1, i1, stitch_len1)
+
+ if i1 < len(subpath1) - 1:
+ remainder_path1 = [pos1] + subpath1[i1 + 1:]
+ else:
+ remainder_path1 = []
+
+ if i2 < len(subpath2) - 1:
+ remainder_path2 = [pos2] + subpath2[i2 + 1:]
+ else:
+ remainder_path2 = []
+
+ remainder_path1 = [p.as_tuple() for p in remainder_path1]
+ remainder_path2 = [p.as_tuple() for p in remainder_path2]
+
+ # We're off by one in the algorithm above, so we need one more
+ # pair of stitches. We also want to stitch at the very end to
+ # make sure we match the vectors on screen as best as possible.
+ # Try to avoid doing both if they're going to stack up too
+ # closely.
+
+ end1 = PyEmb.Point(*remainder_path1[-1])
+ end2 = PyEmb.Point(*remainder_path2[-1])
+ if (end1 - pos1).length() > 0.3 * zigzag_spacing:
+ add_satin_stitch(pos1, pos2)
+
+ add_satin_stitch(end1, end2)
+
+ return zigs, zags
+
+ if underlay:
+ forward, back = calculate_satin(underlay_stitch_len_px, -underlay_inset)
+
+ patch = Patch(color=threadcolor, sortorder=sortorder, stitches=(forward + list(reversed(back))))
+
+ left_points, right_points = calculate_satin(zigzag_spacing_px, pull_compensation_px)
+
+ for i in xrange(len(left_points)):
+ patch.addStitch(left_points[i])
+ patch.addStitch(right_points[i])
return [patch]