summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/elements/satin_column.py98
-rw-r--r--lib/extensions/__init__.py2
-rw-r--r--lib/extensions/apply_satin_pattern.py39
-rw-r--r--lib/extensions/base.py7
-rw-r--r--lib/svg/tags.py2
5 files changed, 144 insertions, 4 deletions
diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py
index 778fc88a..3f5f05e5 100644
--- a/lib/elements/satin_column.py
+++ b/lib/elements/satin_column.py
@@ -12,7 +12,8 @@ from shapely import geometry as shgeo
from shapely.ops import nearest_points
from ..i18n import _
-from ..svg import line_strings_to_csp, point_lists_to_csp
+from ..svg import (PIXELS_PER_MM, apply_transforms, line_strings_to_csp,
+ point_lists_to_csp)
from ..utils import Point, cache, collapse_duplicate_point, cut
from .element import EmbroideryElement, Patch, param
from .validation import ValidationError, ValidationWarning
@@ -74,6 +75,16 @@ class SatinColumn(EmbroideryElement):
def satin_column(self):
return self.get_boolean_param("satin_column")
+ # I18N: Split stitch divides a satin column into equal with parts if the maximum stitch length is exceeded
+ @property
+ @param('split_stitch',
+ _('Split stitch'),
+ tooltip=_('Sets additional stitches if the satin column exceeds the maximum stitch length.'),
+ type='boolean',
+ default='false')
+ def split_stitch(self):
+ return self.get_boolean_param("split_stitch")
+
# I18N: "E" stitch is so named because it looks like the letter E.
@property
@param('e_stitch', _('"E" stitch'), type='boolean', default='false')
@@ -81,6 +92,15 @@ class SatinColumn(EmbroideryElement):
return self.get_boolean_param("e_stitch")
@property
+ @param('max_stitch_length_mm',
+ _('Maximum stitch length'),
+ tooltip=_('Maximum stitch length for split stitches.'),
+ type='float', unit="mm",
+ default=12.1)
+ def max_stitch_length(self):
+ return max(self.get_float_param("max_stitch_length_mm", 12.4), 0.1 * PIXELS_PER_MM)
+
+ @property
def color(self):
return self.get_style("stroke")
@@ -556,6 +576,20 @@ class SatinColumn(EmbroideryElement):
return SatinColumn(node)
+ def get_patterns(self):
+ xpath = ".//*[@inkstitch:pattern='%(id)s']" % dict(id=self.node.get('id'))
+ patterns = self.node.getroottree().getroot().xpath(xpath)
+ line_strings = []
+ for pattern in patterns:
+ d = pattern.get_path()
+ path = paths.Path(d).to_superpath()
+ path = apply_transforms(path, pattern)
+ path = self.flatten(path)
+ lines = [shgeo.LineString(p) for p in path]
+ for line in lines:
+ line_strings.append(line)
+ return shgeo.MultiLineString(line_strings)
+
@property
@cache
def center_line(self):
@@ -787,6 +821,63 @@ class SatinColumn(EmbroideryElement):
return patch
+ def do_pattern_satin(self, patterns):
+ # elements with the attribute 'inkstitch:pattern' set to this elements id will cause extra stitches to be added
+ patch = Patch(color=self.color)
+ sides = self.plot_points_on_rails(self.zigzag_spacing, self.pull_compensation)
+ for i, (left, right) in enumerate(zip(*sides)):
+ patch.add_stitch(left)
+ for point in self._get_pattern_points(left, right, patterns):
+ patch.add_stitch(point)
+ patch.add_stitch(right)
+ if not i+1 >= len(sides[0]):
+ for point in self._get_pattern_points(right, sides[0][i+1], patterns):
+ patch.add_stitch(point)
+ return patch
+
+ def do_split_stitch(self):
+ # stitches exceeding the maximum stitch length will be divided into equal parts through additional stitches
+ patch = Patch(color=self.color)
+ sides = self.plot_points_on_rails(self.zigzag_spacing, self.pull_compensation)
+ for i, (left, right) in enumerate(zip(*sides)):
+ patch.add_stitch(left)
+ points, count = self._get_split_points(left, right)
+ for point in points:
+ patch.add_stitch(point)
+ patch.add_stitch(right)
+ # it is possible that the way back has a different length from the first
+ # but it looks ugly if the points differ too much
+ # so let's make sure they have at least the same amount of divisions
+ if not i+1 >= len(sides[0]):
+ points, count = self._get_split_points(right, sides[0][i+1], count)
+ for point in points:
+ patch.add_stitch(point)
+
+ return patch
+
+ def _get_pattern_points(self, left, right, patterns):
+ points = []
+ for pattern in patterns:
+ intersection = shgeo.LineString([left, right]).intersection(pattern)
+ if isinstance(intersection, shgeo.Point):
+ points.append(Point(intersection.x, intersection.y))
+ if isinstance(intersection, shgeo.MultiPoint):
+ for point in intersection:
+ points.append(Point(point.x, point.y))
+ # sort points after their distance to left
+ points.sort(key=lambda point: point.distance(left))
+ return points
+
+ def _get_split_points(self, left, right, count=None):
+ points = []
+ distance = left.distance(right)
+ split_count = count or int(distance / self.max_stitch_length)
+ for i in range(split_count):
+ line = shgeo.LineString((left, right))
+ split_point = line.interpolate((i+1)/split_count, normalized=True)
+ points.append(Point(split_point.x, split_point.y))
+ return [points, split_count]
+
def to_patches(self, last_patch):
# Stitch a variable-width satin column, zig-zagging between two paths.
@@ -807,8 +898,13 @@ class SatinColumn(EmbroideryElement):
# zigzags sit on the contour walk underlay like rail ties on rails.
patch += self.do_zigzag_underlay()
+ patterns = self.get_patterns()
if self.e_stitch:
patch += self.do_e_stitch()
+ elif self.split_stitch:
+ patch += self.do_split_stitch()
+ elif self.get_patterns():
+ patch += self.do_pattern_satin(patterns)
else:
patch += self.do_satin()
diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py
index 25f835c3..70df7c37 100644
--- a/lib/extensions/__init__.py
+++ b/lib/extensions/__init__.py
@@ -5,6 +5,7 @@
from lib.extensions.troubleshoot import Troubleshoot
+from .apply_satin_pattern import ApplySatinPattern
from .auto_satin import AutoSatin
from .break_apart import BreakApart
from .cleanup import Cleanup
@@ -45,6 +46,7 @@ __all__ = extensions = [StitchPlanPreview,
GlobalCommands,
ConvertToSatin,
CutSatin,
+ ApplySatinPattern,
AutoSatin,
Lettering,
LetteringGenerateJson,
diff --git a/lib/extensions/apply_satin_pattern.py b/lib/extensions/apply_satin_pattern.py
new file mode 100644
index 00000000..9da81075
--- /dev/null
+++ b/lib/extensions/apply_satin_pattern.py
@@ -0,0 +1,39 @@
+# Authors: see git history
+#
+# Copyright (c) 2021 Authors
+# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
+
+import inkex
+
+from ..i18n import _
+from ..svg.tags import INKSTITCH_ATTRIBS
+from .base import InkstitchExtension
+from ..elements import SatinColumn
+
+
+class ApplySatinPattern(InkstitchExtension):
+ # Add inkstitch:pattern attribute to selected patterns. The patterns will be projected on a satin column, which must be in the selection too
+
+ def effect(self):
+ if not self.get_elements():
+ return
+
+ if not self.svg.selected or not any(isinstance(item, SatinColumn) for item in self.elements) or len(self.svg.selected) < 2:
+ inkex.errormsg(_("Please select at least one satin column and a pattern."))
+ return
+
+ if sum(isinstance(item, SatinColumn) for item in self.elements) > 1:
+ inkex.errormsg(_("Please select only one satin column."))
+ return
+
+ satin_id = self.get_satin_column().node.get('id', None)
+ patterns = self.get_patterns()
+
+ for pattern in patterns:
+ pattern.node.set(INKSTITCH_ATTRIBS['pattern'], satin_id)
+
+ def get_satin_column(self):
+ return list(filter(lambda satin: isinstance(satin, SatinColumn), self.elements))[0]
+
+ def get_patterns(self):
+ return list(filter(lambda satin: not isinstance(satin, SatinColumn), self.elements))
diff --git a/lib/extensions/base.py b/lib/extensions/base.py
index 70ca4701..f23ec5e2 100644
--- a/lib/extensions/base.py
+++ b/lib/extensions/base.py
@@ -18,7 +18,8 @@ from ..elements.clone import is_clone
from ..i18n import _
from ..svg import generate_unique_id
from ..svg.tags import (CONNECTOR_TYPE, EMBROIDERABLE_TAGS, INKSCAPE_GROUPMODE,
- NOT_EMBROIDERABLE_TAGS, SVG_DEFS_TAG, SVG_GROUP_TAG)
+ INKSTITCH_ATTRIBS, NOT_EMBROIDERABLE_TAGS,
+ SVG_DEFS_TAG, SVG_GROUP_TAG)
SVG_METADATA_TAG = inkex.addNS("metadata", "svg")
@@ -170,9 +171,9 @@ class InkstitchExtension(inkex.Effect):
if selected:
if node.tag == SVG_GROUP_TAG:
pass
- elif getattr(node, "get_path", None):
+ elif (node.tag in EMBROIDERABLE_TAGS or is_clone(node)) and not node.get(INKSTITCH_ATTRIBS['pattern']):
nodes.append(node)
- elif troubleshoot and (node.tag in NOT_EMBROIDERABLE_TAGS or node.tag in EMBROIDERABLE_TAGS or is_clone(node)):
+ elif troubleshoot and node.tag in NOT_EMBROIDERABLE_TAGS:
nodes.append(node)
return nodes
diff --git a/lib/svg/tags.py b/lib/svg/tags.py
index 5c1d892a..c8e9b67e 100644
--- a/lib/svg/tags.py
+++ b/lib/svg/tags.py
@@ -86,6 +86,8 @@ inkstitch_attribs = [
'zigzag_underlay_inset_mm',
'zigzag_underlay_spacing_mm',
'e_stitch',
+ 'pattern',
+ 'split_stitch',
'pull_compensation_mm',
'stroke_first',
# Legacy