kopia lustrzana https://github.com/inkstitch/inkstitch
				
				
				
			satin pattern and split stitch
							rodzic
							
								
									bf064b7169
								
							
						
					
					
						commit
						1adfa87a68
					
				| 
						 | 
				
			
			@ -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,12 +75,31 @@ 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')
 | 
			
		||||
    def e_stitch(self):
 | 
			
		||||
        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()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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))
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
 | 
			
		||||
    <name>{% trans %}Apply Satin Pattern{% endtrans %}</name>
 | 
			
		||||
    <id>org.inkstitch.apply_satin_pattern.{{ locale }}</id>
 | 
			
		||||
    <param name="extension" type="string" gui-hidden="true">apply_satin_pattern</param>
 | 
			
		||||
    <effect>
 | 
			
		||||
        <object-type>all</object-type>
 | 
			
		||||
        <effects-menu>
 | 
			
		||||
            <submenu name="Ink/Stitch">
 | 
			
		||||
                <submenu name="{% trans %}Satin Tools{% endtrans %}" />
 | 
			
		||||
            </submenu>
 | 
			
		||||
        </effects-menu>
 | 
			
		||||
    </effect>
 | 
			
		||||
    <script>
 | 
			
		||||
        {{ command_tag | safe }}
 | 
			
		||||
    </script>
 | 
			
		||||
</inkscape-extension>
 | 
			
		||||
		Ładowanie…
	
		Reference in New Issue