Stitch selection methods (#2114)

pull/2172/head
Kaalleen 2023-03-25 19:45:36 +01:00 zatwierdzone przez GitHub
rodzic d8e80f4df8
commit 221ff2a645
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
10 zmienionych plików z 110 dodań i 129 usunięć

Wyświetl plik

@ -58,13 +58,16 @@ def param(*args, **kwargs):
class EmbroideryElement(object):
def __init__(self, node):
self.node = node
self._update_legacy_params()
def _update_legacy_params(self): # noqa: C901
# update legacy embroider_ attributes to namespaced attributes
legacy_attribs = False
for attrib in self.node.attrib:
if attrib.startswith('embroider_'):
self.replace_legacy_param(attrib)
legacy_attribs = True
# convert legacy tie setting
legacy_tie = self.get_param('ties', None)
if legacy_tie == "True":
@ -83,10 +86,36 @@ class EmbroideryElement(object):
elif legacy_fill_method == 3:
self.set_param('fill_method', 'legacy_fill')
# legacy satin method
if self.get_boolean_param('e_stitch', False) is True:
self.remove_param('e_stitch')
self.set_param('satin_method', 'e_stitch')
# default setting for fill_underlay has changed
if legacy_attribs and not self.get_param('fill_underlay', ""):
self.set_param('fill_underlay', False)
# convert legacy stroke_method
if self.get_style("stroke"):
# manual stitch
legacy_manual_stitch = self.get_boolean_param('manual_stitch', False)
if legacy_manual_stitch is True:
self.remove_param('manual_stitch')
self.set_param('stroke_method', 'manual_stitch')
# stroke_method
legacy_stroke_method = self.get_int_param('stroke_method', None)
if legacy_stroke_method == 0:
self.set_param('stroke_method', 'running_stitch')
elif legacy_stroke_method == 1:
self.set_param('stroke_method', 'ripple_stitch')
if (not self.get_param('stroke_method', None) and
self.get_param('satin_column', False) is False and
not self.node.style('stroke-dasharray')):
self.set_param('stroke_method', 'zigzag_stitch')
# if the stroke method is a zigzag-stitch but we are receiving a dashed line, set it to running stitch
if self.get_param('stroke_method', None) == 'zigzag_stitch' and self.node.style('stroke-dasharray'):
self.set_param('stroke_method', 'running_stitch')
@property
def id(self):
return self.node.get('id')
@ -193,6 +222,10 @@ class EmbroideryElement(object):
param = INKSTITCH_ATTRIBS[name]
self.node.set(param, str(value))
def remove_param(self, name):
param = INKSTITCH_ATTRIBS[name]
del self.node.attrib[param]
@cache
def _get_specified_style(self):
# We want to cache this, because it's quite expensive to generate.

Wyświetl plik

@ -19,9 +19,10 @@ from ..stitch_plan import StitchGroup
from ..stitches import running_stitch
from ..svg import line_strings_to_csp, point_lists_to_csp
from ..utils import Point, cache, cut, cut_multiple, prng
from ..utils.param import ParamOption
from ..utils.threading import check_stop_flag
from .element import PIXELS_PER_MM, EmbroideryElement, param
from .validation import ValidationError, ValidationWarning
from ..utils.threading import check_stop_flag
class TooFewPathsError(ValidationError):
@ -77,17 +78,26 @@ class SatinColumn(EmbroideryElement):
def satin_column(self):
return self.get_boolean_param("satin_column")
# I18N: "E" stitch is so named because it looks like the letter E.
_satin_methods = [ParamOption('satin_column', _('Satin Column')),
ParamOption('e_stitch', _('"E" Stitch'))]
@property
@param('e_stitch', _('"E" stitch'), type='boolean', default='false')
def e_stitch(self):
return self.get_boolean_param("e_stitch")
@param('satin_method',
_('Method'),
type='combo',
default=0,
options=_satin_methods,
sort_index=0)
def satin_method(self):
return self.get_param('satin_method', 'satin_column')
@property
@param('max_stitch_length_mm',
_('Maximum stitch length'),
tooltip=_('Maximum stitch length for split stitches.'),
type='float', unit="mm")
type='float',
unit="mm",
sort_index=1)
def max_stitch_length_px(self):
return self.get_float_param("max_stitch_length_mm") or None
@ -150,8 +160,10 @@ class SatinColumn(EmbroideryElement):
@param('short_stitch_inset',
_('Short stitch inset'),
tooltip=_('Stitches in areas with high density will be inset by this amount.'),
type='float', unit="%",
default=15)
type='float',
unit="%",
default=15,
sort_index=3)
def short_stitch_inset(self):
return self.get_float_param("short_stitch_inset", 15) / 100
@ -159,8 +171,10 @@ class SatinColumn(EmbroideryElement):
@param('short_stitch_distance_mm',
_('Short stitch distance'),
tooltip=_('Inset stitches if the distance between stitches is smaller than this.'),
type='float', unit="mm",
default=0.25)
type='float',
unit="mm",
default=0.25,
sort_index=4)
def short_stitch_distance(self):
return self.get_float_param("short_stitch_distance_mm", 0.25)
@ -174,7 +188,8 @@ class SatinColumn(EmbroideryElement):
tooltip=_('Peak-to-peak distance between zig-zags. This is double the mm/stitch measurement used by most mechanical machines.'),
unit='mm/cycle',
type='float',
default=0.4)
default=0.4,
sort_index=5)
def zigzag_spacing(self):
# peak-to-peak distance between zigzags
return max(self.get_float_param("zigzag_spacing_mm", 0.4), 0.01)
@ -187,7 +202,8 @@ class SatinColumn(EmbroideryElement):
'Two values separated by a space may be used for an aysmmetric effect.'),
unit='% (each side)',
type='float',
default=0)
default=0,
sort_index=6)
@cache
def pull_compensation_percent(self):
# pull compensation as a percentage of the width
@ -202,7 +218,8 @@ class SatinColumn(EmbroideryElement):
'Two values separated by a space may be used for an aysmmetric effect.'),
unit='mm (each side)',
type='float',
default=0)
default=0,
sort_index=7)
@cache
def pull_compensation_px(self):
# In satin stitch, the stitches have a tendency to pull together and
@ -250,7 +267,9 @@ class SatinColumn(EmbroideryElement):
_('Inset distance (fixed)'),
tooltip=_('Shrink the outline by a fixed length, to prevent the underlay from showing around the outside of the satin column.'),
group=_('Contour Underlay'),
unit='mm (each side)', type='float', default=0.4,
unit='mm (each side)',
type='float',
default=0.4,
sort_index=2)
@cache
def contour_underlay_inset_px(self):
@ -1125,7 +1144,7 @@ class SatinColumn(EmbroideryElement):
# zigzags sit on the contour walk underlay like rail ties on rails.
patch += self.do_zigzag_underlay()
if self.e_stitch:
if self.satin_method == 'e_stitch':
patch += self.do_e_stitch()
else:
patch += self.do_satin()

Wyświetl plik

@ -3,8 +3,6 @@
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
import sys
import shapely.geometry
from inkex import Transform
@ -16,6 +14,7 @@ from ..stitches.running_stitch import bean_stitch, running_stitch
from ..svg import get_node_transform, parse_length_with_units
from ..threads import ThreadColor
from ..utils import Point, cache
from ..utils.param import ParamOption
from .element import EmbroideryElement, param
from .validation import ValidationWarning
@ -39,14 +38,6 @@ class MultipleGuideLineWarning(ValidationWarning):
]
class SmallZigZagWarning(ValidationWarning):
name = _("Small ZigZag")
description = _("This zig zag stitch has a stroke width smaller than 0.5 units.")
steps_to_solve = [
_("Set your stroke to be dashed to indicate running stitch. Any kind of dash will work.")
]
class Stroke(EmbroideryElement):
element_name = _("Stroke")
@ -69,46 +60,27 @@ class Stroke(EmbroideryElement):
needle = f'Cut {needle}'
return needle
@property
def dashed(self):
return self.get_style("stroke-dasharray") is not None
def update_dash(self, to_dash):
if self.dashed == to_dash:
return
if to_dash is False:
del self.node.style['stroke-dasharray']
else:
self.node.style['stroke-dasharray'] = "1,0.5"
_stroke_methods = [ParamOption('running_stitch', _("Running Stitch / Bean Stitch")),
ParamOption('ripple_stitch', _("Ripple Stitch")),
ParamOption('zigzag_stitch', _("ZigZag Stitch")),
ParamOption('manual_stitch', _("Manual Stitch"))]
@property
@param('stroke_method',
_('Method'),
type='dropdown',
type='combo',
default=0,
# 0: run/simple satin, 1: manual, 2: ripple
options=[_("Running Stitch"), _("Ripple")],
options=_stroke_methods,
sort_index=0)
def stroke_method(self):
return self.get_int_param('stroke_method', 0)
@property
@param('manual_stitch',
_('Manual stitch placement'),
tooltip=_("Stitch every node in the path. All options other than stop and trim are ignored. "
"Lock stitches will be added only if force lock stitches is checked."),
type='boolean',
default=False,
select_items=[('stroke_method', 0)],
sort_index=1)
def manual_stitch_mode(self):
return self.get_boolean_param('manual_stitch')
return self.get_param('stroke_method', 'running_stitch')
@property
@param('repeats',
_('Repeats'),
tooltip=_('Defines how many times to run down and back along the path.'),
type='int',
select_items=[('stroke_method', 'running_stitch'), ('stroke_method', 'ripple_stitch'), ('stroke_method', 'zigzag_stitch')],
default="1",
sort_index=2)
def repeats(self):
@ -123,6 +95,7 @@ class Stroke(EmbroideryElement):
'A value of 2 would quintuple each stitch, etc.\n\n'
'A pattern with various repeats can be created with a list of values separated by a space.'),
type='str',
select_items=[('stroke_method', 'running_stitch'), ('stroke_method', 'ripple_stitch')],
default=0,
sort_index=3)
def bean_stitch_repeats(self):
@ -134,6 +107,7 @@ class Stroke(EmbroideryElement):
tooltip=_('Length of stitches in running stitch mode.'),
unit='mm',
type='float',
select_items=[('stroke_method', 'running_stitch'), ('stroke_method', 'ripple_stitch')],
default=1.5,
sort_index=4)
def running_stitch_length(self):
@ -147,6 +121,7 @@ class Stroke(EmbroideryElement):
'A higher tolerance means sharp corners may be rounded.'),
unit='mm',
type='float',
select_items=[('stroke_method', 'running_stitch'), ('stroke_method', 'ripple_stitch')],
default=0.2,
sort_index=4)
def running_stitch_tolerance(self):
@ -159,7 +134,7 @@ class Stroke(EmbroideryElement):
unit='mm',
type='float',
default=0.4,
select_items=[('stroke_method', 0)],
select_items=[('stroke_method', 'zigzag_stitch')],
sort_index=5)
@cache
def zigzag_spacing(self):
@ -171,7 +146,7 @@ class Stroke(EmbroideryElement):
tooltip=_('Number of lines from start to finish'),
type='int',
default=10,
select_items=[('stroke_method', 1)],
select_items=[('stroke_method', 'ripple_stitch')],
sort_index=5)
@cache
def line_count(self):
@ -188,7 +163,7 @@ class Stroke(EmbroideryElement):
tooltip=_('Skip this number of lines at the beginning.'),
type='int',
default=0,
select_items=[('stroke_method', 1)],
select_items=[('stroke_method', 'ripple_stitch')],
sort_index=6)
@cache
def skip_start(self):
@ -200,7 +175,7 @@ class Stroke(EmbroideryElement):
tooltip=_('Skip this number of lines at the end'),
type='int',
default=0,
select_items=[('stroke_method', 1)],
select_items=[('stroke_method', 'ripple_stitch')],
sort_index=7)
@cache
def skip_end(self):
@ -224,7 +199,7 @@ class Stroke(EmbroideryElement):
tooltip=_('Increase density towards one side.'),
type='float',
default=1,
select_items=[('stroke_method', 1)],
select_items=[('stroke_method', 'ripple_stitch')],
sort_index=8)
@cache
def exponent(self):
@ -236,7 +211,7 @@ class Stroke(EmbroideryElement):
tooltip=_('Reverse exponent effect.'),
type='boolean',
default=False,
select_items=[('stroke_method', 1)],
select_items=[('stroke_method', 'ripple_stitch')],
sort_index=9)
@cache
def flip_exponent(self):
@ -248,7 +223,7 @@ class Stroke(EmbroideryElement):
tooltip=_('Flip start and end point'),
type='boolean',
default=False,
select_items=[('stroke_method', 1)],
select_items=[('stroke_method', 'ripple_stitch')],
sort_index=10)
@cache
def reverse(self):
@ -261,7 +236,7 @@ class Stroke(EmbroideryElement):
type='float',
default=0,
unit='mm',
select_items=[('stroke_method', 1)],
select_items=[('stroke_method', 'ripple_stitch')],
sort_index=11)
@cache
def grid_size(self):
@ -275,7 +250,7 @@ class Stroke(EmbroideryElement):
default=0,
# 0: xy, 1: x, 2: y, 3: none
options=["X Y", "X", "Y", _("None")],
select_items=[('stroke_method', 1)],
select_items=[('stroke_method', 'ripple_stitch')],
sort_index=12)
def scale_axis(self):
return self.get_int_param('scale_axis', 0)
@ -287,7 +262,7 @@ class Stroke(EmbroideryElement):
type='float',
unit='%',
default=100,
select_items=[('stroke_method', 1)],
select_items=[('stroke_method', 'ripple_stitch')],
sort_index=13)
def scale_start(self):
return self.get_float_param('scale_start', 100.0)
@ -299,7 +274,7 @@ class Stroke(EmbroideryElement):
type='float',
unit='%',
default=0.0,
select_items=[('stroke_method', 1)],
select_items=[('stroke_method', 'ripple_stitch')],
sort_index=14)
def scale_end(self):
return self.get_float_param('scale_end', 0.0)
@ -310,7 +285,7 @@ class Stroke(EmbroideryElement):
tooltip=_('Rotate satin guided ripple stitches'),
type='boolean',
default=True,
select_items=[('stroke_method', 1)],
select_items=[('stroke_method', 'ripple_stitch')],
sort_index=15)
@cache
def rotate_ripples(self):
@ -323,7 +298,7 @@ class Stroke(EmbroideryElement):
type='dropdown',
default=0,
options=(_("flat"), _("point")),
select_items=[('stroke_method', 1)],
select_items=[('stroke_method', 'ripple_stitch')],
sort_index=16)
@cache
def join_style(self):
@ -349,7 +324,7 @@ class Stroke(EmbroideryElement):
if len(flattened[0]) == 1:
return [[[flattened[0][0][0], flattened[0][0][1]], [flattened[0][0][0] + 1.0, flattened[0][0][1]]]]
if self.manual_stitch_mode:
if self.stroke_method == 'manual_stitch':
return [self.strip_control_points(subpath) for subpath in path]
else:
return flattened
@ -374,42 +349,6 @@ class Stroke(EmbroideryElement):
else:
return self.shape.centroid
def is_running_stitch(self):
# using stroke width <= 0.5 pixels to indicate running stitch is deprecated in favor of dashed lines
stroke_width, units = parse_length_with_units(self.get_style("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.
#
# This 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(_("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."), file=sys.stderr)
# still allow the deprecated setting to work in order to support old files
return True
else:
return False
def simple_satin(self, path, zigzag_spacing, stroke_width):
"zig-zag along the path at the specified spacing and wdith"
@ -468,11 +407,11 @@ class Stroke(EmbroideryElement):
def do_bean_repeats(self, stitches):
return bean_stitch(stitches, self.bean_stitch_repeats)
def to_stitch_groups(self, last_patch):
def to_stitch_groups(self, last_patch): # noqa: C901
patches = []
# ripple stitch
if self.stroke_method == 1:
if self.stroke_method == 'ripple_stitch':
patch = self.ripple_stitch()
if patch:
if any(self.bean_stitch_repeats):
@ -482,7 +421,7 @@ class Stroke(EmbroideryElement):
for path in self.paths:
path = [Point(x, y) for x, y in path]
# manual stitch
if self.manual_stitch_mode:
if self.stroke_method == 'manual_stitch':
if self.force_lock_stitches:
lock_stitches = self.lock_stitches
else:
@ -493,13 +432,13 @@ class Stroke(EmbroideryElement):
lock_stitches=lock_stitches,
force_lock_stitches=self.force_lock_stitches)
# running stitch
elif self.is_running_stitch():
elif self.stroke_method == 'running_stitch':
patch = self.running_stitch(path, self.running_stitch_length, self.running_stitch_tolerance)
# bean stitch
if any(self.bean_stitch_repeats):
patch.stitches = self.do_bean_repeats(patch.stitches)
# simple satin
else:
elif self.stroke_method == 'zigzag_stitch':
patch = self.simple_satin(path, self.zigzag_spacing, self.stroke_width)
if patch:
@ -541,5 +480,3 @@ class Stroke(EmbroideryElement):
yield MultipleGuideLineWarning(self._representative_point())
stroke_width, units = parse_length_with_units(self.get_style("stroke-width", "1"))
if not self.dashed and stroke_width <= 0.5:
yield SmallZigZagWarning(self._representative_point())

Wyświetl plik

@ -672,8 +672,7 @@ class Params(InkstitchExtension):
classes.append(FillStitch)
if element.get_style("stroke") is not None:
classes.append(Stroke)
if element.get_style("stroke-dasharray") is None:
classes.append(SatinColumn)
classes.append(SatinColumn)
return classes
def get_nodes_by_class(self):

Wyświetl plik

@ -22,9 +22,9 @@ class SelectElements(InkstitchExtension):
pars.add_argument("--select-running-stitch", type=Boolean, dest="running", default=False)
pars.add_argument("--select-ripples", type=Boolean, dest="ripples", default=False)
pars.add_argument("--select-zigzag", type=Boolean, dest="zigzag", default=False)
pars.add_argument("--select-manual", type=Boolean, dest="manual", default=False)
pars.add_argument("--select-polyline", type=Boolean, dest="poly", default=False)
pars.add_argument("--select-zigzag", type=Boolean, dest="zigzag", default=False)
pars.add_argument("--select-satin", type=Boolean, dest="satin", default=False)
pars.add_argument("--satin-underlay", type=str, dest="satin_underlay", default="all")
pars.add_argument("--select-e", type=Boolean, dest="e", default=False)
@ -101,16 +101,13 @@ class SelectElements(InkstitchExtension):
def _select_stroke(self, element):
select = False
method = element.stroke_method
manual = element.manual_stitch_mode
if self.options.ripples and method == 1:
if self.options.running and method == 'running_stitch':
select = True
elif self.options.manual and manual:
if self.options.ripples and method == 'ripple_stitch':
select = True
elif method == 1 or manual:
return False
elif self.options.zigzag and not element.dashed:
elif self.options.zigzag and method == 'zigzag_stitch':
select = True
elif self.options.running and element.dashed:
elif self.options.manual and method == 'manual_stitch':
select = True
return select
@ -139,13 +136,12 @@ class SelectElements(InkstitchExtension):
def _select_satin(self, element):
select = False
if not (self.options.satin or self.options.e):
return False
if not self._select_satin_underlay(element):
return False
if self.options.e and element.e_stitch:
method = element.satin_method
if self.options.satin and method == "satin_column":
select = True
elif self.options.satin and not element.e_stitch:
elif self.options.e and method == "e_stitch":
select = True
return select

Wyświetl plik

@ -92,8 +92,6 @@ class StrokeToLpeSatin(InkstitchExtension):
element.set_param('satin_column', 'true')
element.node.style['stroke-width'] = self.svg.viewport_to_unit('0.756')
# remove running_stitch dashes if they are there
element.update_dash(False)
def _process_satin_column(self, element):
current_effects = element.node.get(PATH_EFFECT, None)

Wyświetl plik

@ -50,8 +50,6 @@ class ZigzagLineToSatin(InkstitchExtension):
element.node.set('d', " ".join(d))
element.set_param('satin_column', True)
# remove dashes
element.update_dash(False)
def _get_rails_and_rungs(self, point_list):
if self.options.pattern == "sawtooth":

Wyświetl plik

@ -202,7 +202,7 @@ def color_block_to_paths(color_block, svg, destination, visual_commands):
'style': "stroke: %s; stroke-width: 0.4; fill: none;" % color,
'd': "M" + " ".join(" ".join(str(coord) for coord in point) for point in point_list),
'transform': get_correction_transform(svg),
INKSTITCH_ATTRIBS['manual_stitch']: 'true'
INKSTITCH_ATTRIBS['stroke_method']: 'manual_stitch'
})
destination.append(path)

Wyświetl plik

@ -117,6 +117,7 @@ inkstitch_attribs = [
'grid_size',
# satin column
'satin_column',
'satin_method',
'short_stitch_distance_mm',
'short_stitch_inset',
'running_stitch_length_mm',

Wyświetl plik

@ -10,9 +10,9 @@
<label>Stroke type</label>
<param indent="1" name="select-running-stitch" type="boolean" gui-text="Running Stitch">false</param>
<param indent="1" name="select-ripples" type="boolean" gui-text="Ripples">false</param>
<param indent="1" name="select-zigzag" type="boolean" gui-text="ZigZag Stitch">false</param>
<param indent="1" name="select-manual" type="boolean" gui-text="Manual Stitch">false</param>
<param indent="1" name="select-polyline" type="boolean" gui-text="Polyline">false</param>
<param indent="1" name="select-zigzag" type="boolean" gui-text="ZigZag Stitch">false</param>
<label>Satin</label>
<param indent="1" name="select-satin" type="boolean" gui-text="Satin Column">false</param>
<param indent="1" name="select-e" type="boolean" gui-text="E-Stitch">false</param>