diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 44a686436..3610ec6b9 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -171,7 +171,7 @@ jobs:
# with --no-binary argument may fix notary issues as well shapely speedups error issue
pip install -U lxml --no-binary lxml
pip uninstall --yes shapely
- pip install -v -U Shapely --no-binary Shapely
+ pip install -v -U Shapely==1.8.1 --no-binary Shapely
pip install pyinstaller
echo "${{ env.pythonLocation }}/bin" >> $GITHUB_PATH
diff --git a/lib/commands.py b/lib/commands.py
index 1d2357592..a7affb6d8 100644
--- a/lib/commands.py
+++ b/lib/commands.py
@@ -26,6 +26,9 @@ COMMANDS = {
# L10N command attached to an object
"fill_end": N_("Fill stitch ending position"),
+ # L10N command attached to an object
+ "ripple_target": N_("Ripple stitch target position"),
+
# L10N command attached to an object
"run_start": N_("Auto-route running stitch starting position"),
@@ -60,7 +63,8 @@ COMMANDS = {
"stop_position": N_("Jump destination for Stop commands (a.k.a. \"Frame Out position\")."),
}
-OBJECT_COMMANDS = ["fill_start", "fill_end", "run_start", "run_end", "satin_start", "satin_end", "stop", "trim", "ignore_object", "satin_cut_point"]
+OBJECT_COMMANDS = ["fill_start", "fill_end", "ripple_target", "run_start", "run_end", "satin_start", "satin_end",
+ "stop", "trim", "ignore_object", "satin_cut_point"]
FREE_MOVEMENT_OBJECT_COMMANDS = ["run_start", "run_end", "satin_start", "satin_end"]
LAYER_COMMANDS = ["ignore_layer"]
GLOBAL_COMMANDS = ["origin", "stop_position"]
diff --git a/lib/elements/stroke.py b/lib/elements/stroke.py
index 7113bf3fd..40741caa7 100644
--- a/lib/elements/stroke.py
+++ b/lib/elements/stroke.py
@@ -7,16 +7,30 @@ import sys
import shapely.geometry
-from .element import EmbroideryElement, param
+from inkex import Transform
+
from ..i18n import _
from ..stitch_plan import StitchGroup
from ..stitches import bean_stitch, running_stitch
-from ..svg import parse_length_with_units
+from ..stitches.ripple_stitch import ripple_stitch
+from ..svg import get_node_transform, parse_length_with_units
from ..utils import Point, cache
+from .element import EmbroideryElement, param
+from .satin_column import SatinColumn
+from .validation import ValidationWarning
warned_about_legacy_running_stitch = False
+class IgnoreSkipValues(ValidationWarning):
+ name = _("Ignore skip")
+ description = _("Skip values are ignored, because there was no line left to embroider.")
+ steps_to_solve = [
+ _('* Reduce values of Skip first and last lines or'),
+ _('* Increase number of lines accordinly in the params dialog.'),
+ ]
+
+
class Stroke(EmbroideryElement):
element_name = _("Stroke")
@@ -34,15 +48,36 @@ class Stroke(EmbroideryElement):
return self.get_style("stroke-dasharray") is not None
@property
- @param('running_stitch_length_mm',
- _('Running stitch length'),
- tooltip=_('Length of stitches in running stitch mode.'),
- unit='mm',
- type='float',
- default=1.5,
- sort_index=3)
- def running_stitch_length(self):
- return max(self.get_float_param("running_stitch_length_mm", 1.5), 0.01)
+ @param('stroke_method',
+ _('Method'),
+ type='dropdown',
+ default=0,
+ # 0: run/simple satin, 1: manual, 2: ripple
+ options=[_("Running Stitch"), _("Ripple")],
+ 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 other options are ignored."),
+ type='boolean',
+ default=False,
+ select_items=[('stroke_method', 0)],
+ sort_index=1)
+ def manual_stitch_mode(self):
+ return self.get_boolean_param('manual_stitch')
+
+ @property
+ @param('repeats',
+ _('Repeats'),
+ tooltip=_('Defines how many times to run down and back along the path.'),
+ type='int',
+ default="1",
+ sort_index=2)
+ def repeats(self):
+ return max(1, self.get_int_param("repeats", 1))
@property
@param(
@@ -53,10 +88,93 @@ class Stroke(EmbroideryElement):
'A value of 2 would quintuple each stitch, etc. Only applies to running stitch.'),
type='int',
default=0,
- sort_index=2)
+ sort_index=3)
def bean_stitch_repeats(self):
return self.get_int_param("bean_stitch_repeats", 0)
+ @property
+ @param('running_stitch_length_mm',
+ _('Running stitch length'),
+ tooltip=_('Length of stitches in running stitch mode.'),
+ unit='mm',
+ type='float',
+ default=1.5,
+ sort_index=4)
+ def running_stitch_length(self):
+ return max(self.get_float_param("running_stitch_length_mm", 1.5), 0.01)
+
+ @property
+ @param('line_count',
+ _('Number of lines'),
+ tooltip=_('Number of lines from start to finish'),
+ type='int',
+ default=10,
+ select_items=[('stroke_method', 1)],
+ sort_index=5)
+ @cache
+ def line_count(self):
+ return max(self.get_int_param("line_count", 10), 1)
+
+ @property
+ @param('skip_start',
+ _('Skip first lines'),
+ tooltip=_('Skip this number of lines at the beginning.'),
+ type='int',
+ default=0,
+ select_items=[('stroke_method', 1)],
+ sort_index=6)
+ @cache
+ def skip_start(self):
+ return abs(self.get_int_param("skip_start", 0))
+
+ @property
+ @param('skip_end',
+ _('Skip last lines'),
+ tooltip=_('Skip this number of lines at the end'),
+ type='int',
+ default=0,
+ select_items=[('stroke_method', 1)],
+ sort_index=7)
+ @cache
+ def skip_end(self):
+ return abs(self.get_int_param("skip_end", 0))
+
+ @property
+ @param('flip',
+ _('Flip'),
+ tooltip=_('Flip outer to inner'),
+ type='boolean',
+ default=False,
+ select_items=[('stroke_method', 1)],
+ sort_index=8)
+ @cache
+ def flip(self):
+ return self.get_boolean_param("flip", False)
+
+ @property
+ @param('render_grid',
+ _('Grid distance'),
+ tooltip=_('Render as grid. Works only with satin type ripple stitches.'),
+ type='float',
+ default=0,
+ select_items=[('stroke_method', 1)],
+ sort_index=8)
+ @cache
+ def render_grid(self):
+ return abs(self.get_float_param("render_grid", 0))
+
+ @property
+ @param('exponent',
+ _('Line distance exponent'),
+ tooltip=_('Increse density towards one side.'),
+ type='float',
+ default=1,
+ select_items=[('stroke_method', 1)],
+ sort_index=9)
+ @cache
+ def exponent(self):
+ return max(self.get_float_param("exponent", 1), 0.1)
+
@property
@param('zigzag_spacing_mm',
_('Zig-zag spacing (peak-to-peak)'),
@@ -64,22 +182,12 @@ class Stroke(EmbroideryElement):
unit='mm',
type='float',
default=0.4,
- sort_index=3)
+ select_items=[('stroke_method', 0)],
+ sort_index=5)
@cache
def zigzag_spacing(self):
return max(self.get_float_param("zigzag_spacing_mm", 0.4), 0.01)
- @property
- @param('repeats',
- _('Repeats'),
- tooltip=_('Defines how many times to run down and back along the path.'),
- type='int',
- default="1",
- sort_index=1)
- def repeats(self):
- repeats = self.get_int_param("repeats", 1)
- return max(1, repeats)
-
@property
def paths(self):
path = self.parse_path()
@@ -102,18 +210,17 @@ class Stroke(EmbroideryElement):
@cache
def as_multi_line_string(self):
line_strings = [shapely.geometry.LineString(path) for path in self.paths]
-
return shapely.geometry.MultiLineString(line_strings)
- @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,
- sort_index=0)
- def manual_stitch_mode(self):
- return self.get_boolean_param('manual_stitch')
+ def get_ripple_target(self):
+ command = self.get_command('ripple_target')
+ if command:
+ pos = [float(command.use.get("x", 0)), float(command.use.get("y", 0))]
+ transform = get_node_transform(command.use)
+ pos = Transform(transform).apply_to_point(pos)
+ return Point(*pos)
+ 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
@@ -199,23 +306,61 @@ class Stroke(EmbroideryElement):
return StitchGroup(self.color, stitches)
+ def do_bean_repeats(self, stitches):
+ return bean_stitch(stitches, self.bean_stitch_repeats)
+
def to_stitch_groups(self, last_patch):
patches = []
- for path in self.paths:
- path = [Point(x, y) for x, y in path]
- if self.manual_stitch_mode:
- patch = StitchGroup(color=self.color, stitches=path, stitch_as_is=True)
- elif self.is_running_stitch():
- patch = self.running_stitch(path, self.running_stitch_length)
-
- if self.bean_stitch_repeats > 0:
- patch.stitches = bean_stitch(patch.stitches, self.bean_stitch_repeats)
-
- else:
- patch = self.simple_satin(path, self.zigzag_spacing, self.stroke_width)
-
+ # ripple stitch
+ if self.stroke_method == 1:
+ lines = self.as_multi_line_string()
+ points = []
+ if len(lines.geoms) > 1:
+ # if render_grid has a number use this, otherwise use running_stitch_length
+ length = self.render_grid or self.running_stitch_length
+ # use satin column points for satin like build ripple stitches
+ points = SatinColumn(self.node).plot_points_on_rails(length, 0)
+ point_target = self.get_ripple_target()
+ patch = StitchGroup(
+ color=self.color,
+ tags=["ripple_stitch"],
+ stitches=ripple_stitch(
+ self.as_multi_line_string(),
+ point_target,
+ self.line_count,
+ points,
+ self.running_stitch_length,
+ self.repeats,
+ self.flip,
+ self.skip_start,
+ self.skip_end,
+ self.render_grid,
+ self.exponent))
if patch:
+ if self.bean_stitch_repeats > 0:
+ patch.stitches = self.do_bean_repeats(patch.stitches)
patches.append(patch)
+ else:
+ for path in self.paths:
+ path = [Point(x, y) for x, y in path]
+ # manual stitch
+ if self.manual_stitch_mode:
+ patch = StitchGroup(color=self.color, stitches=path, stitch_as_is=True)
+ # running stitch
+ elif self.is_running_stitch():
+ patch = self.running_stitch(path, self.running_stitch_length)
+ if self.bean_stitch_repeats > 0:
+ patch.stitches = self.do_bean_repeats(patch.stitches)
+ # simple satin
+ else:
+ patch = self.simple_satin(path, self.zigzag_spacing, self.stroke_width)
+
+ if patch:
+ patches.append(patch)
return patches
+
+ def validation_warnings(self):
+ if self.stroke_method == 1 and self.skip_start + self.skip_end >= self.line_count:
+ yield IgnoreSkipValues(self.shape.centroid)
diff --git a/lib/extensions/params.py b/lib/extensions/params.py
index e50d97d00..b60183e51 100644
--- a/lib/extensions/params.py
+++ b/lib/extensions/params.py
@@ -129,6 +129,7 @@ class ParamsTab(ScrolledPanel):
self.update_choice_widgets((param, selection))
self.settings_grid.Layout()
+ self.Fit()
self.Layout()
if event:
diff --git a/lib/stitches/__init__.py b/lib/stitches/__init__.py
index 8b2738bc7..b0ff64fce 100644
--- a/lib/stitches/__init__.py
+++ b/lib/stitches/__init__.py
@@ -9,4 +9,5 @@ from .guided_fill import guided_fill
from .running_stitch import *
# Can't put this here because we get a circular import :(
-#from auto_satin import auto_satin
+# from .auto_satin import auto_satin
+# from .ripple_stitch import ripple_stitch
diff --git a/lib/stitches/auto_run.py b/lib/stitches/auto_run.py
index 847a1bcd7..91a998497 100644
--- a/lib/stitches/auto_run.py
+++ b/lib/stitches/auto_run.py
@@ -132,7 +132,7 @@ def autorun(elements, preserve_order=False, break_up=None, starting_point=None,
else:
parent = elements[0].node.getparent()
insert_index = parent.index(elements[0].node)
- group = create_new_group(parent, insert_index, _("Auto-Run"))
+ group = create_new_group(parent, insert_index, _("Auto-Route"))
add_elements_to_group(new_elements, group)
if trim:
diff --git a/lib/stitches/ripple_stitch.py b/lib/stitches/ripple_stitch.py
new file mode 100644
index 000000000..88d1b8d03
--- /dev/null
+++ b/lib/stitches/ripple_stitch.py
@@ -0,0 +1,173 @@
+from collections import defaultdict
+
+from shapely.geometry import LineString, Point
+
+from ..utils.geometry import line_string_to_point_list
+from .running_stitch import running_stitch
+
+
+def ripple_stitch(lines, target, line_count, points, max_stitch_length, repeats, flip, skip_start, skip_end, render_grid, exponent):
+ '''
+ Ripple stitch is allowed to cross itself and doesn't care about an equal distance of lines
+ It is meant to be used with light (not dense) stitching
+ It will ignore holes in a closed shape. Closed shapes will be filled with a spiral
+ Open shapes will be stitched back and forth.
+ If there is only one (open) line or a closed shape the target point will be used.
+ If more sublines are present interpolation will take place between the first two.
+ '''
+
+ # sort geoms by size
+ lines = sorted(lines.geoms, key=lambda linestring: linestring.length, reverse=True)
+ outline = lines[0]
+
+ # ignore skip_start and skip_end if both toghether are greater or equal to line_count
+ if skip_start + skip_end >= line_count:
+ skip_start = skip_end = 0
+
+ if is_closed(outline):
+ rippled_line = do_circular_ripple(outline, target, line_count, repeats, flip, max_stitch_length, skip_start, skip_end, exponent)
+ else:
+ rippled_line = do_linear_ripple(lines, points, target, line_count - 1, repeats, flip, skip_start, skip_end, render_grid, exponent)
+
+ return running_stitch(line_string_to_point_list(rippled_line), max_stitch_length)
+
+
+def do_circular_ripple(outline, target, line_count, repeats, flip, max_stitch_length, skip_start, skip_end, exponent):
+ # for each point generate a line going to the target point
+ lines = target_point_lines_normalized_distances(outline, target, flip, max_stitch_length)
+
+ # create a list of points for each line
+ points = get_interpolation_points(lines, line_count, exponent, "circular")
+
+ # connect the lines to a spiral towards the target
+ coords = []
+ for i in range(skip_start, line_count - skip_end):
+ for j in range(len(lines)):
+ coords.append(Point(points[j][i].x, points[j][i].y))
+
+ coords = repeat_coords(coords, repeats)
+
+ return LineString(coords)
+
+
+def do_linear_ripple(lines, points, target, line_count, repeats, flip, skip_start, skip_end, render_grid, exponent):
+ if len(lines) == 1:
+ helper_lines = target_point_lines(lines[0], target, flip)
+ else:
+ helper_lines = []
+ for start, end in zip(points[0], points[1]):
+ if flip:
+ helper_lines.append(LineString([end, start]))
+ else:
+ helper_lines.append(LineString([start, end]))
+
+ # get linear points along the lines
+ points = get_interpolation_points(helper_lines, line_count, exponent)
+
+ # go back and forth along the lines - flip direction of every second line
+ coords = []
+ for i in range(skip_start, len(points[0]) - skip_end):
+ for j in range(len(helper_lines)):
+ k = j
+ if i % 2 != 0:
+ k = len(helper_lines) - j - 1
+ coords.append(Point(points[k][i].x, points[k][i].y))
+
+ # add helper lines as a grid
+ # for now only add this to satin type ripples, otherwise it could become to dense at the target point
+ if len(lines) > 1 and render_grid:
+ coords.extend(do_grid(helper_lines, line_count - skip_end))
+
+ coords = repeat_coords(coords, repeats)
+
+ return LineString(coords)
+
+
+def do_grid(lines, num_lines):
+ coords = []
+ if num_lines % 2 == 0:
+ lines = reversed(lines)
+ for i, line in enumerate(lines):
+ line_coords = list(line.coords)
+ if (i % 2 == 0 and num_lines % 2 == 0) or (i % 2 != 0 and num_lines % 2 != 0):
+ coords.extend(reversed(line_coords))
+ else:
+ coords.extend(line_coords)
+ return coords
+
+
+def line_length(line):
+ return line.length
+
+
+def is_closed(line):
+ coords = line.coords
+ return Point(*coords[0]).distance(Point(*coords[-1])) < 0.05
+
+
+def target_point_lines(outline, target, flip):
+ lines = []
+ for point in outline.coords:
+ if flip:
+ lines.append(LineString([point, target]))
+ else:
+ lines.append(LineString([target, point]))
+ return lines
+
+
+def target_point_lines_normalized_distances(outline, target, flip, max_stitch_length):
+ lines = []
+ outline = running_stitch(line_string_to_point_list(outline), max_stitch_length)
+ for point in outline:
+ if flip:
+ lines.append(LineString([target, point]))
+ else:
+ lines.append(LineString([point, target]))
+ return lines
+
+
+def get_interpolation_points(lines, line_count, exponent, method="linear"):
+ new_points = defaultdict(list)
+ count = len(lines) - 1
+ for i, line in enumerate(lines):
+ steps = get_steps(line, line_count, exponent)
+ distance = -1
+ points = []
+ for j in range(line_count):
+ length = line.length * steps[j]
+ if method == "circular":
+ if distance == -1:
+ # the first line makes sure, it is going to be a spiral
+ distance = (line.length * steps[j+1]) * (i / count)
+ else:
+ distance += length - (line.length * steps[j-1])
+ else:
+ distance = line.length * steps[j]
+ points.append(line.interpolate(distance))
+ if method == "linear":
+ points.append(Point(*line.coords[-1]))
+ new_points[i] = points
+ return new_points
+
+
+def get_steps(line, total_lines, exponent):
+ # get_steps is scribbled from the inkscape interpolate extension
+ # (https://gitlab.com/inkscape/extensions/-/blob/master/interp.py)
+ steps = [
+ ((i + 1) / (total_lines)) ** exponent
+ for i in range(total_lines - 1)
+ ]
+ return [0] + steps + [1]
+
+
+def repeat_coords(coords, repeats):
+ final_coords = []
+ for i in range(repeats):
+ if i % 2 == 1:
+ # reverse every other pass
+ this_coords = coords[::-1]
+ else:
+ this_coords = coords[:]
+
+ final_coords.extend(this_coords)
+ return final_coords
diff --git a/lib/svg/tags.py b/lib/svg/tags.py
index d78ba678e..ce57de4fa 100644
--- a/lib/svg/tags.py
+++ b/lib/svg/tags.py
@@ -63,6 +63,11 @@ inkstitch_attribs = [
'join_style',
'avoid_self_crossing',
'clockwise',
+ 'line_count',
+ 'skip_start',
+ 'skip_end',
+ 'render_grid',
+ 'exponent',
'expand_mm',
'fill_underlay',
'fill_underlay_angle',
@@ -80,7 +85,7 @@ inkstitch_attribs = [
'flip',
'expand_mm',
# stroke
- 'manual_stitch',
+ 'stroke_method',
'bean_stitch_repeats',
'repeats',
'running_stitch_length_mm',
@@ -102,7 +107,8 @@ inkstitch_attribs = [
'stroke_first',
# Legacy
'trim_after',
- 'stop_after'
+ 'stop_after',
+ 'manual_stitch',
]
for attrib in inkstitch_attribs:
INKSTITCH_ATTRIBS[attrib] = inkex.addNS(attrib, 'inkstitch')
diff --git a/requirements.txt b/requirements.txt
index 4ff2cc0a1..b64be645d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,7 +8,7 @@
backports.functools_lru_cache
wxPython
networkx
-shapely==1.8.0
+shapely==1.8.2
lxml
appdirs
numpy<=1.17.4
diff --git a/symbols/inkstitch.svg b/symbols/inkstitch.svg
index 0f0ff15c1..2a80a0527 100644
--- a/symbols/inkstitch.svg
+++ b/symbols/inkstitch.svg
@@ -324,6 +324,22 @@
id="inkstitch-autorun_end-path"
d="M -1.5977169,-4.8605484 c -0.00319,1.523e-4 -0.00643,6.284e-4 -0.00955,0.00139 l -1.738218,0.6114515 c -0.044131,0.013274 -0.04771,0.07406 -0.00546,0.092381 l 1.7286718,0.6522048 c 0.042543,0.011464 0.077832,-0.033916 0.055977,-0.072004 -0.075631,-0.1076723 -0.1279528,-0.2256474 -0.1583813,-0.3478501 l 5.3580083,0.0693 C 3.7283489,-3.6574881 3.9289854,-3.5209456 4.163113,-3.51807 4.4929661,-3.51396 4.7679522,-3.7755203 4.7721242,-4.1036793 4.7763142,-4.4318766 4.5079699,-4.70559 4.178155,-4.7096844 3.9431472,-4.7126044 3.7385104,-4.5784741 3.6388043,-4.38223 l -5.3566689,-0.065225 c 0.033509,-0.1204507 0.089391,-0.2361787 0.1679695,-0.3396805 0.019712,-0.034678 -0.00793,-0.077126 -0.047786,-0.073375 z m -2.6203001,0.2119742 c -0.3227531,0.0041 -0.5844387,0.2625539 -0.5884958,0.5856093 -0.00295,0.2329983 0.129063,0.4383262 0.3236143,0.5394287 l -0.065546,5.2964025 c -0.1188053,-0.03344 -0.233209,-0.088553 -0.3359005,-0.1657553 -0.018028,-0.014169 -0.043442,-0.014169 -0.061451,0 -0.016535,0.013426 -0.022582,0.035878 -0.015004,0.055721 l 0.6130875,1.7310434 c 0.013338,0.043896 0.074426,0.047476 0.092855,0.00543 l 0.6608737,-1.7201931 c 0.021243,-0.048142 -0.037912,-0.091505 -0.077832,-0.057054 -0.1085476,0.075489 -0.2269701,0.1273635 -0.3495646,0.1576047 l 0.069641,-5.2950505 c 0.1995845,-0.095713 0.3397855,-0.2989653 0.3427327,-0.5326301 0.00412,-0.3281781 -0.2641734,-0.5964641 -0.5939692,-0.6005585 -0.00519,-7.62e-5 -0.00991,-7.62e-5 -0.015023,0 z m 8.4425639,1.3302136 c -0.020458,-1.333e-4 -0.038925,0.012283 -0.046428,0.03125 l -0.6608735,1.714742 c -0.021243,0.04818 0.037931,0.091524 0.077851,0.057054 0.1072273,-0.074556 0.2244441,-0.1271727 0.3454502,-0.1576044 L 3.8750009,3.6234841 C 3.6760287,3.7194257 3.5366315,3.9202022 3.5336461,4.15341 3.529436,4.4816071 3.7964416,4.7512453 4.1262565,4.7553397 4.4560331,4.759449 4.726962,4.4979085 4.731134,4.1697112 4.7341354,3.934523 4.5996979,3.7276145 4.4020654,3.6275594 L 4.4703294,-1.66612 c 0.1197239,0.033383 0.2338597,0.08802 0.3372785,0.1657745 0.039309,0.032355 0.096166,-0.00834 0.077832,-0.055721 L 4.2709744,-3.2871101 c -0.00748,-0.018949 -0.02595,-0.031384 -0.046428,-0.031251 z M 1.5114302,3.6180947 c -0.037451,6.856e-4 -0.060627,0.040868 -0.042351,0.073356 0.075823,0.1079391 0.1280103,0.2267139 0.1584006,0.3492023 L -3.7305473,3.971372 c -0.096108,-0.1988532 -0.2989079,-0.338138 -0.5338966,-0.3410517 -0.3297958,-0.0041 -0.5994042,0.2615256 -0.6035188,0.5896847 -0.00413,0.3281972 0.2627956,0.6019488 0.5926104,0.6060241 0.2345678,0.00292 0.4394534,-0.1304295 0.5393508,-0.3261024 l 5.3580084,0.063873 C 1.5885549,4.684593 1.5314859,4.7996924 1.4526979,4.90348 1.4299049,4.942272 1.4668789,4.9887 1.5100529,4.975503 L 3.2496085,4.3640511 c 0.043002,-0.015959 0.043002,-0.076441 0,-0.0924 L 1.5250561,3.6194659 c -0.00446,-0.00112 -0.00905,-0.00154 -0.013665,-0.00139 Z" />
+
+ Ripple stitch target point
+
+
+
@@ -458,5 +474,13 @@
width="100%"
height="100%"
transform="translate(37.830849,113.28861)" />
+
diff --git a/templates/auto_run.xml b/templates/auto_run.xml
index eb732571a..4a524b2dc 100644
--- a/templates/auto_run.xml
+++ b/templates/auto_run.xml
@@ -1,6 +1,6 @@
- Autoroute Running Stitch
+ Auto-Route Running Stitch
org.inkstitch.auto_run
auto_run
@@ -12,7 +12,7 @@
-
+
true
@@ -21,7 +21,7 @@
false
-
+
diff --git a/templates/selection_to_guide_line.xml b/templates/selection_to_guide_line.xml
index 677e62c44..1a3ac0f95 100644
--- a/templates/selection_to_guide_line.xml
+++ b/templates/selection_to_guide_line.xml
@@ -6,7 +6,7 @@
all
-
+