summaryrefslogtreecommitdiff
path: root/lib/elements/stroke.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/elements/stroke.py')
-rw-r--r--lib/elements/stroke.py161
1 files changed, 161 insertions, 0 deletions
diff --git a/lib/elements/stroke.py b/lib/elements/stroke.py
new file mode 100644
index 00000000..48662b6d
--- /dev/null
+++ b/lib/elements/stroke.py
@@ -0,0 +1,161 @@
+import sys
+
+from .element import param, EmbroideryElement, Patch
+from ..i18n import _
+from ..utils import cache, Point
+
+
+warned_about_legacy_running_stitch = False
+
+
+class Stroke(EmbroideryElement):
+ element_name = "Stroke"
+
+ @property
+ @param('satin_column', _('Satin stitch along paths'), type='toggle', inverse=True)
+ def satin_column(self):
+ return self.get_boolean_param("satin_column")
+
+ @property
+ def color(self):
+ return self.get_style("stroke")
+
+ @property
+ def dashed(self):
+ return self.get_style("stroke-dasharray") is not None
+
+ @property
+ @param('running_stitch_length_mm', _('Running stitch length'), unit='mm', type='float', default=1.5)
+ def running_stitch_length(self):
+ return max(self.get_float_param("running_stitch_length_mm", 1.5), 0.01)
+
+ @property
+ @param('zigzag_spacing_mm', _('Zig-zag spacing (peak-to-peak)'), unit='mm', type='float', default=0.4)
+ @cache
+ def zigzag_spacing(self):
+ return max(self.get_float_param("zigzag_spacing_mm", 0.4), 0.01)
+
+ @property
+ @param('repeats', _('Repeats'), type='int', default="1")
+ def repeats(self):
+ return self.get_int_param("repeats", 1)
+
+ @property
+ def paths(self):
+ path = self.parse_path()
+
+ if self.manual_stitch_mode:
+ return [self.strip_control_points(subpath) for subpath in path]
+ else:
+ return self.flatten(path)
+
+ @property
+ @param('manual_stitch', _('Manual stitch placement'), tooltip=_("Stitch every node in the path. Stitch length and zig-zag spacing are ignored."), type='boolean', default=False)
+ def manual_stitch_mode(self):
+ return self.get_boolean_param('manual_stitch')
+
+ def is_running_stitch(self):
+ # using stroke width <= 0.5 pixels to indicate running stitch is deprecated in favor of dashed lines
+
+ try:
+ stroke_width = float(self.get_style("stroke-width"))
+ except ValueError:
+ stroke_width = 1
+
+ if self.dashed:
+ return True
+ elif stroke_width <= 0.5 and self.get_float_param('running_stitch_length_mm', None) is not None:
+ # if they use a stroke width less than 0.5 AND they specifically set a running stitch
+ # length, then assume they intend to use the deprecated <= 0.5 method to set running
+ # stitch.
+ #
+ # Note that we use self.get_style("stroke_width") _not_ self.stroke_width above. We
+ # explicitly want the stroke width in "user units" ("document units") -- that is, what
+ # the user sees in inkscape's stroke settings.
+ #
+ # Also note that we don't use self.running_stitch_length_mm above. This is because we
+ # want to see if they set a running stitch length at all, and the property will apply
+ # a default value.
+ #
+ # Thsi is so tricky, and and intricate that's a major reason that we deprecated the
+ # 0.5 units rule.
+
+ # Warn them the first time.
+ global warned_about_legacy_running_stitch
+ if not warned_about_legacy_running_stitch:
+ warned_about_legacy_running_stitch = True
+ print >> sys.stderr, _("Legacy running stitch setting detected!\n\nIt looks like you're using a stroke " + \
+ "smaller than 0.5 units to indicate a running stitch, which is deprecated. Instead, please set " + \
+ "your stroke to be dashed to indicate running stitch. Any kind of dash will work.")
+
+ # still allow the deprecated setting to work in order to support old files
+ return True
+ else:
+ return False
+
+ def stroke_points(self, emb_point_list, zigzag_spacing, stroke_width):
+ # TODO: use inkstitch.stitches.running_stitch
+
+ patch = Patch(color=self.color)
+ p0 = emb_point_list[0]
+ rho = 0.0
+ side = 1
+ last_segment_direction = None
+
+ for repeat in xrange(self.repeats):
+ if repeat % 2 == 0:
+ order = range(1, len(emb_point_list))
+ else:
+ order = range(-2, -len(emb_point_list) - 1, -1)
+
+ for segi in order:
+ p1 = emb_point_list[segi]
+
+ # how far we have to go along segment
+ seg_len = (p1 - p0).length()
+ if (seg_len == 0):
+ continue
+
+ # vector pointing along segment
+ along = (p1 - p0).unit()
+
+ # vector pointing to edge of stroke width
+ perp = along.rotate_left() * (stroke_width * 0.5)
+
+ if stroke_width == 0.0 and last_segment_direction is not None:
+ if abs(1.0 - along * last_segment_direction) > 0.5:
+ # if greater than 45 degree angle, stitch the corner
+ rho = zigzag_spacing
+ patch.add_stitch(p0)
+
+ # iteration variable: how far we are along segment
+ while (rho <= seg_len):
+ left_pt = p0 + along * rho + perp * side
+ patch.add_stitch(left_pt)
+ rho += zigzag_spacing
+ side = -side
+
+ p0 = p1
+ last_segment_direction = along
+ rho -= seg_len
+
+ if (p0 - patch.stitches[-1]).length() > 0.1:
+ patch.add_stitch(p0)
+
+ return patch
+
+ def to_patches(self, last_patch):
+ patches = []
+
+ for path in self.paths:
+ path = [Point(x, y) for x, y in path]
+ if self.manual_stitch_mode:
+ patch = Patch(color=self.color, stitches=path, stitch_as_is=True)
+ elif self.is_running_stitch():
+ patch = self.stroke_points(path, self.running_stitch_length, stroke_width=0.0)
+ else:
+ patch = self.stroke_points(path, self.zigzag_spacing / 2.0, stroke_width=self.stroke_width)
+
+ patches.append(patch)
+
+ return patches