kopia lustrzana https://github.com/inkstitch/inkstitch
Add ripple stitch feature (#1667)
rodzic
ca07b1b267
commit
e968f814f7
|
@ -171,7 +171,7 @@ jobs:
|
||||||
# with --no-binary argument may fix notary issues as well shapely speedups error issue
|
# with --no-binary argument may fix notary issues as well shapely speedups error issue
|
||||||
pip install -U lxml --no-binary lxml
|
pip install -U lxml --no-binary lxml
|
||||||
pip uninstall --yes shapely
|
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
|
pip install pyinstaller
|
||||||
|
|
||||||
echo "${{ env.pythonLocation }}/bin" >> $GITHUB_PATH
|
echo "${{ env.pythonLocation }}/bin" >> $GITHUB_PATH
|
||||||
|
|
|
@ -26,6 +26,9 @@ COMMANDS = {
|
||||||
# L10N command attached to an object
|
# L10N command attached to an object
|
||||||
"fill_end": N_("Fill stitch ending position"),
|
"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
|
# L10N command attached to an object
|
||||||
"run_start": N_("Auto-route running stitch starting position"),
|
"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\")."),
|
"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"]
|
FREE_MOVEMENT_OBJECT_COMMANDS = ["run_start", "run_end", "satin_start", "satin_end"]
|
||||||
LAYER_COMMANDS = ["ignore_layer"]
|
LAYER_COMMANDS = ["ignore_layer"]
|
||||||
GLOBAL_COMMANDS = ["origin", "stop_position"]
|
GLOBAL_COMMANDS = ["origin", "stop_position"]
|
||||||
|
|
|
@ -7,16 +7,30 @@ import sys
|
||||||
|
|
||||||
import shapely.geometry
|
import shapely.geometry
|
||||||
|
|
||||||
from .element import EmbroideryElement, param
|
from inkex import Transform
|
||||||
|
|
||||||
from ..i18n import _
|
from ..i18n import _
|
||||||
from ..stitch_plan import StitchGroup
|
from ..stitch_plan import StitchGroup
|
||||||
from ..stitches import bean_stitch, running_stitch
|
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 ..utils import Point, cache
|
||||||
|
from .element import EmbroideryElement, param
|
||||||
|
from .satin_column import SatinColumn
|
||||||
|
from .validation import ValidationWarning
|
||||||
|
|
||||||
warned_about_legacy_running_stitch = False
|
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):
|
class Stroke(EmbroideryElement):
|
||||||
element_name = _("Stroke")
|
element_name = _("Stroke")
|
||||||
|
|
||||||
|
@ -34,15 +48,36 @@ class Stroke(EmbroideryElement):
|
||||||
return self.get_style("stroke-dasharray") is not None
|
return self.get_style("stroke-dasharray") is not None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@param('running_stitch_length_mm',
|
@param('stroke_method',
|
||||||
_('Running stitch length'),
|
_('Method'),
|
||||||
tooltip=_('Length of stitches in running stitch mode.'),
|
type='dropdown',
|
||||||
unit='mm',
|
default=0,
|
||||||
type='float',
|
# 0: run/simple satin, 1: manual, 2: ripple
|
||||||
default=1.5,
|
options=[_("Running Stitch"), _("Ripple")],
|
||||||
sort_index=3)
|
sort_index=0)
|
||||||
def running_stitch_length(self):
|
def stroke_method(self):
|
||||||
return max(self.get_float_param("running_stitch_length_mm", 1.5), 0.01)
|
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
|
@property
|
||||||
@param(
|
@param(
|
||||||
|
@ -53,10 +88,93 @@ class Stroke(EmbroideryElement):
|
||||||
'A value of 2 would quintuple each stitch, etc. Only applies to running stitch.'),
|
'A value of 2 would quintuple each stitch, etc. Only applies to running stitch.'),
|
||||||
type='int',
|
type='int',
|
||||||
default=0,
|
default=0,
|
||||||
sort_index=2)
|
sort_index=3)
|
||||||
def bean_stitch_repeats(self):
|
def bean_stitch_repeats(self):
|
||||||
return self.get_int_param("bean_stitch_repeats", 0)
|
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
|
@property
|
||||||
@param('zigzag_spacing_mm',
|
@param('zigzag_spacing_mm',
|
||||||
_('Zig-zag spacing (peak-to-peak)'),
|
_('Zig-zag spacing (peak-to-peak)'),
|
||||||
|
@ -64,22 +182,12 @@ class Stroke(EmbroideryElement):
|
||||||
unit='mm',
|
unit='mm',
|
||||||
type='float',
|
type='float',
|
||||||
default=0.4,
|
default=0.4,
|
||||||
sort_index=3)
|
select_items=[('stroke_method', 0)],
|
||||||
|
sort_index=5)
|
||||||
@cache
|
@cache
|
||||||
def zigzag_spacing(self):
|
def zigzag_spacing(self):
|
||||||
return max(self.get_float_param("zigzag_spacing_mm", 0.4), 0.01)
|
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
|
@property
|
||||||
def paths(self):
|
def paths(self):
|
||||||
path = self.parse_path()
|
path = self.parse_path()
|
||||||
|
@ -102,18 +210,17 @@ class Stroke(EmbroideryElement):
|
||||||
@cache
|
@cache
|
||||||
def as_multi_line_string(self):
|
def as_multi_line_string(self):
|
||||||
line_strings = [shapely.geometry.LineString(path) for path in self.paths]
|
line_strings = [shapely.geometry.LineString(path) for path in self.paths]
|
||||||
|
|
||||||
return shapely.geometry.MultiLineString(line_strings)
|
return shapely.geometry.MultiLineString(line_strings)
|
||||||
|
|
||||||
@property
|
def get_ripple_target(self):
|
||||||
@param('manual_stitch',
|
command = self.get_command('ripple_target')
|
||||||
_('Manual stitch placement'),
|
if command:
|
||||||
tooltip=_("Stitch every node in the path. Stitch length and zig-zag spacing are ignored."),
|
pos = [float(command.use.get("x", 0)), float(command.use.get("y", 0))]
|
||||||
type='boolean',
|
transform = get_node_transform(command.use)
|
||||||
default=False,
|
pos = Transform(transform).apply_to_point(pos)
|
||||||
sort_index=0)
|
return Point(*pos)
|
||||||
def manual_stitch_mode(self):
|
else:
|
||||||
return self.get_boolean_param('manual_stitch')
|
return self.shape.centroid
|
||||||
|
|
||||||
def is_running_stitch(self):
|
def is_running_stitch(self):
|
||||||
# using stroke width <= 0.5 pixels to indicate running stitch is deprecated in favor of dashed lines
|
# 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)
|
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):
|
def to_stitch_groups(self, last_patch):
|
||||||
patches = []
|
patches = []
|
||||||
|
|
||||||
for path in self.paths:
|
# ripple stitch
|
||||||
path = [Point(x, y) for x, y in path]
|
if self.stroke_method == 1:
|
||||||
if self.manual_stitch_mode:
|
lines = self.as_multi_line_string()
|
||||||
patch = StitchGroup(color=self.color, stitches=path, stitch_as_is=True)
|
points = []
|
||||||
elif self.is_running_stitch():
|
if len(lines.geoms) > 1:
|
||||||
patch = self.running_stitch(path, self.running_stitch_length)
|
# if render_grid has a number use this, otherwise use running_stitch_length
|
||||||
|
length = self.render_grid or self.running_stitch_length
|
||||||
if self.bean_stitch_repeats > 0:
|
# use satin column points for satin like build ripple stitches
|
||||||
patch.stitches = bean_stitch(patch.stitches, self.bean_stitch_repeats)
|
points = SatinColumn(self.node).plot_points_on_rails(length, 0)
|
||||||
|
point_target = self.get_ripple_target()
|
||||||
else:
|
patch = StitchGroup(
|
||||||
patch = self.simple_satin(path, self.zigzag_spacing, self.stroke_width)
|
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 patch:
|
||||||
|
if self.bean_stitch_repeats > 0:
|
||||||
|
patch.stitches = self.do_bean_repeats(patch.stitches)
|
||||||
patches.append(patch)
|
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
|
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)
|
||||||
|
|
|
@ -129,6 +129,7 @@ class ParamsTab(ScrolledPanel):
|
||||||
|
|
||||||
self.update_choice_widgets((param, selection))
|
self.update_choice_widgets((param, selection))
|
||||||
self.settings_grid.Layout()
|
self.settings_grid.Layout()
|
||||||
|
self.Fit()
|
||||||
self.Layout()
|
self.Layout()
|
||||||
|
|
||||||
if event:
|
if event:
|
||||||
|
|
|
@ -9,4 +9,5 @@ from .guided_fill import guided_fill
|
||||||
from .running_stitch import *
|
from .running_stitch import *
|
||||||
|
|
||||||
# Can't put this here because we get a circular 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
|
||||||
|
|
|
@ -132,7 +132,7 @@ def autorun(elements, preserve_order=False, break_up=None, starting_point=None,
|
||||||
else:
|
else:
|
||||||
parent = elements[0].node.getparent()
|
parent = elements[0].node.getparent()
|
||||||
insert_index = parent.index(elements[0].node)
|
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)
|
add_elements_to_group(new_elements, group)
|
||||||
|
|
||||||
if trim:
|
if trim:
|
||||||
|
|
|
@ -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
|
|
@ -63,6 +63,11 @@ inkstitch_attribs = [
|
||||||
'join_style',
|
'join_style',
|
||||||
'avoid_self_crossing',
|
'avoid_self_crossing',
|
||||||
'clockwise',
|
'clockwise',
|
||||||
|
'line_count',
|
||||||
|
'skip_start',
|
||||||
|
'skip_end',
|
||||||
|
'render_grid',
|
||||||
|
'exponent',
|
||||||
'expand_mm',
|
'expand_mm',
|
||||||
'fill_underlay',
|
'fill_underlay',
|
||||||
'fill_underlay_angle',
|
'fill_underlay_angle',
|
||||||
|
@ -80,7 +85,7 @@ inkstitch_attribs = [
|
||||||
'flip',
|
'flip',
|
||||||
'expand_mm',
|
'expand_mm',
|
||||||
# stroke
|
# stroke
|
||||||
'manual_stitch',
|
'stroke_method',
|
||||||
'bean_stitch_repeats',
|
'bean_stitch_repeats',
|
||||||
'repeats',
|
'repeats',
|
||||||
'running_stitch_length_mm',
|
'running_stitch_length_mm',
|
||||||
|
@ -102,7 +107,8 @@ inkstitch_attribs = [
|
||||||
'stroke_first',
|
'stroke_first',
|
||||||
# Legacy
|
# Legacy
|
||||||
'trim_after',
|
'trim_after',
|
||||||
'stop_after'
|
'stop_after',
|
||||||
|
'manual_stitch',
|
||||||
]
|
]
|
||||||
for attrib in inkstitch_attribs:
|
for attrib in inkstitch_attribs:
|
||||||
INKSTITCH_ATTRIBS[attrib] = inkex.addNS(attrib, 'inkstitch')
|
INKSTITCH_ATTRIBS[attrib] = inkex.addNS(attrib, 'inkstitch')
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
backports.functools_lru_cache
|
backports.functools_lru_cache
|
||||||
wxPython
|
wxPython
|
||||||
networkx
|
networkx
|
||||||
shapely==1.8.0
|
shapely==1.8.2
|
||||||
lxml
|
lxml
|
||||||
appdirs
|
appdirs
|
||||||
numpy<=1.17.4
|
numpy<=1.17.4
|
||||||
|
|
|
@ -324,6 +324,22 @@
|
||||||
id="inkstitch-autorun_end-path"
|
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" />
|
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" />
|
||||||
</symbol>
|
</symbol>
|
||||||
|
<symbol
|
||||||
|
id="inkstitch_ripple_target"
|
||||||
|
style="display:inline">
|
||||||
|
<title
|
||||||
|
id="inkstitch_title9427-9">Ripple stitch target point</title>
|
||||||
|
<path
|
||||||
|
id="inkstitch_circle13166-9"
|
||||||
|
d="m 9.220113,0.07922893 c -1.9e-6,5.10672897 -4.1398241,9.24654997 -9.24655297,9.24654997 -5.10672933,0 -9.24655213,-4.139821 -9.24655403,-9.24654997 1e-7,-2.45233803 0.9741879,-4.80423503 2.7082531,-6.53830103 1.7340653,-1.734065 4.0859624,-2.708252 6.53830093,-2.708252 5.10673007,0 9.24655277,4.139823 9.24655297,9.24655303 0,0 0,0 0,0"
|
||||||
|
style="opacity:1;vector-effect:none;fill:#fafafa;fill-opacity:1;fill-rule:evenodd;stroke:#003399;stroke-width:1.06500006;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3.19500017, 3.19500017;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="opacity:1;fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;"
|
||||||
|
d="m -4.1361006,-3.9974292 8.224564,8.224564 m 0,-8.224564 -8.224564,8.224564 m 9.927927,-4.112282 h -11.63129 m 5.81564501,-5.815645 v 11.63129"
|
||||||
|
id="inkstitch_rect5371-2-9"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</symbol>
|
||||||
</defs>
|
</defs>
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata8380">
|
id="metadata8380">
|
||||||
|
@ -458,5 +474,13 @@
|
||||||
width="100%"
|
width="100%"
|
||||||
height="100%"
|
height="100%"
|
||||||
transform="translate(37.830849,113.28861)" />
|
transform="translate(37.830849,113.28861)" />
|
||||||
|
<use
|
||||||
|
xlink:href="#inkstitch_ripple_target"
|
||||||
|
id="use37099"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
transform="translate(151.1811,112.79208)" />
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Przed Szerokość: | Wysokość: | Rozmiar: 50 KiB Po Szerokość: | Wysokość: | Rozmiar: 51 KiB |
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<inkscape-extension translationdomain="inkstitch" xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
<inkscape-extension translationdomain="inkstitch" xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||||
<name>Autoroute Running Stitch</name>
|
<name>Auto-Route Running Stitch</name>
|
||||||
<id>org.inkstitch.auto_run</id>
|
<id>org.inkstitch.auto_run</id>
|
||||||
<param name="extension" type="string" gui-hidden="true">auto_run</param>
|
<param name="extension" type="string" gui-hidden="true">auto_run</param>
|
||||||
<effect>
|
<effect>
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
</effects-menu>
|
</effects-menu>
|
||||||
</effect>
|
</effect>
|
||||||
<param name="options" type="notebook">
|
<param name="options" type="notebook">
|
||||||
<page name="options" gui-text="Autoroute Running Stitch Options">
|
<page name="options" gui-text="Auto-Route Running Stitch Options">
|
||||||
<spacer />
|
<spacer />
|
||||||
<param name="break_up" type="boolean" gui-text="Add nodes at intersections">true</param>
|
<param name="break_up" type="boolean" gui-text="Add nodes at intersections">true</param>
|
||||||
<spacer />
|
<spacer />
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
<param name="trim" type="boolean" gui-text="Trim jump stitches">false</param>
|
<param name="trim" type="boolean" gui-text="Trim jump stitches">false</param>
|
||||||
</page>
|
</page>
|
||||||
<page name="help" gui-text="Help">
|
<page name="help" gui-text="Help">
|
||||||
<label appearance="header">Autoroute Running Stitch</label>
|
<label appearance="header">Auto-Route Running Stitch</label>
|
||||||
<label>Add nodes at intersections:</label>
|
<label>Add nodes at intersections:</label>
|
||||||
<spacer />
|
<spacer />
|
||||||
<label indent="1">- Enabled (automatic). Ink/Stitch will add some nodes for better routing. This is the default setting.</label>
|
<label indent="1">- Enabled (automatic). Ink/Stitch will add some nodes for better routing. This is the default setting.</label>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<effect>
|
<effect>
|
||||||
<object-type>all</object-type>
|
<object-type>all</object-type>
|
||||||
<effects-menu>
|
<effects-menu>
|
||||||
<submenu name="Ink/Stitch">
|
<submenu name="Ink/Stitch" translatable="no">
|
||||||
<submenu name="Edit" />
|
<submenu name="Edit" />
|
||||||
</submenu>
|
</submenu>
|
||||||
</effects-menu>
|
</effects-menu>
|
||||||
|
|
Ładowanie…
Reference in New Issue