diff options
| author | Lex Neva <lexelby@users.noreply.github.com> | 2018-05-19 14:41:50 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-05-19 14:41:50 -0400 |
| commit | 9dadd836e2f09b0216f7b40c782e2b55bcc90dee (patch) | |
| tree | 128b6f13ea682a8e5a033370ab2e13388801f100 /lib/elements/stroke.py | |
| parent | ba7288d8fcd62678bd17d8fab01d0d488d9e21e8 (diff) | |
| parent | 6fe417cd64090f028c0d07b799620eb94637bf33 (diff) | |
Merge pull request #163 from lexelby/lexelby-single-extension
single code entry point
Diffstat (limited to 'lib/elements/stroke.py')
| -rw-r--r-- | lib/elements/stroke.py | 161 |
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 |
