summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dbus/select_elements.py2
-rw-r--r--electron/src/renderer/assets/js/simulator.js8
-rw-r--r--lib/elements/stroke.py38
-rw-r--r--lib/extensions/lettering.py7
-rw-r--r--lib/extensions/params.py9
-rw-r--r--lib/extensions/select_elements.py8
-rw-r--r--lib/extensions/stroke_to_lpe_satin.py93
-rw-r--r--lib/gui/simulator.py5
-rw-r--r--templates/select_elements.xml10
-rw-r--r--templates/stroke_to_lpe_satin.xml2
10 files changed, 138 insertions, 44 deletions
diff --git a/dbus/select_elements.py b/dbus/select_elements.py
index 2b663aed..ad016751 100644
--- a/dbus/select_elements.py
+++ b/dbus/select_elements.py
@@ -49,7 +49,7 @@ class DBusActions:
# start dbus
dbus = DBusActions()
# give it some time to start
-sleep(0.2)
+sleep(0.5)
# clear previous selection
dbus.run_action('select-clear', None)
# select with the list of ids
diff --git a/electron/src/renderer/assets/js/simulator.js b/electron/src/renderer/assets/js/simulator.js
index 0e60e1c2..bb290838 100644
--- a/electron/src/renderer/assets/js/simulator.js
+++ b/electron/src/renderer/assets/js/simulator.js
@@ -611,11 +611,11 @@ export default {
this.svg.on('zoom', this.resizeCursor)
this.resizeCursor()
+ inkStitch.get('page_specs').then(response => {
+ this.page_specs = response.data
+ this.generatePage()
+ })
this.start()
})
- inkStitch.get('page_specs').then(response => {
- this.page_specs = response.data
- this.generatePage()
- })
}
}
diff --git a/lib/elements/stroke.py b/lib/elements/stroke.py
index 258bf737..fd5983d5 100644
--- a/lib/elements/stroke.py
+++ b/lib/elements/stroke.py
@@ -3,6 +3,8 @@
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
+from math import ceil
+
import shapely.geometry
from inkex import Transform
@@ -104,7 +106,7 @@ class Stroke(EmbroideryElement):
@property
@param('running_stitch_length_mm',
_('Running stitch length'),
- tooltip=_('Length of stitches in running stitch mode.'),
+ tooltip=_('Length of stitches. Stitches can be shorter according to the stitch tolerance setting.'),
unit='mm',
type='float',
select_items=[('stroke_method', 'running_stitch'), ('stroke_method', 'ripple_stitch')],
@@ -115,7 +117,7 @@ class Stroke(EmbroideryElement):
@property
@param('running_stitch_tolerance_mm',
- _('Running stitch tolerance'),
+ _('Stitch tolerance'),
tooltip=_('All stitches must be within this distance from the path. ' +
'A lower tolerance means stitches will be closer together. ' +
'A higher tolerance means sharp corners may be rounded.'),
@@ -128,6 +130,20 @@ class Stroke(EmbroideryElement):
return max(self.get_float_param("running_stitch_tolerance_mm", 0.2), 0.01)
@property
+ @param('max_stitch_length_mm',
+ _('Max stitch length'),
+ tooltip=_('Split stitches longer than this.'),
+ unit='mm',
+ type='float',
+ select_items=[('stroke_method', 'manual_stitch')],
+ sort_index=4)
+ def max_stitch_length(self):
+ max_length = self.get_float_param("max_stitch_length_mm", None)
+ if not max_length or max_length <= 0:
+ return
+ return max_length
+
+ @property
@param('zigzag_spacing_mm',
_('Zig-zag spacing (peak-to-peak)'),
tooltip=_('Length of stitches in zig-zag mode.'),
@@ -396,6 +412,21 @@ class Stroke(EmbroideryElement):
return StitchGroup(self.color, repeated_stitches, lock_stitches=self.lock_stitches, force_lock_stitches=self.force_lock_stitches)
+ def apply_max_stitch_length(self, path):
+ # apply max distances
+ max_len_path = [path[0]]
+ for points in zip(path[:-1], path[1:]):
+ line = shapely.geometry.LineString(points)
+ dist = line.length
+ if dist > self.max_stitch_length:
+ num_subsections = ceil(dist / self.max_stitch_length)
+ additional_points = [Point(coord.x, coord.y)
+ for coord in [line.interpolate((i/num_subsections), normalized=True)
+ for i in range(1, num_subsections + 1)]]
+ max_len_path.extend(additional_points)
+ max_len_path.append(points[1])
+ return max_len_path
+
def ripple_stitch(self):
return StitchGroup(
color=self.color,
@@ -422,6 +453,9 @@ class Stroke(EmbroideryElement):
path = [Point(x, y) for x, y in path]
# manual stitch
if self.stroke_method == 'manual_stitch':
+ if self.max_stitch_length:
+ path = self.apply_max_stitch_length(path)
+
if self.force_lock_stitches:
lock_stitches = self.lock_stitches
else:
diff --git a/lib/extensions/lettering.py b/lib/extensions/lettering.py
index a396765b..b304a6f9 100644
--- a/lib/extensions/lettering.py
+++ b/lib/extensions/lettering.py
@@ -31,9 +31,11 @@ class LetteringFrame(wx.Frame):
DEFAULT_FONT = "small_font"
def __init__(self, *args, **kwargs):
- # begin wxGlade: MyFrame.__init__
self.group = kwargs.pop('group')
self.cancel_hook = kwargs.pop('on_cancel', None)
+ self.metadata = kwargs.pop('metadata', [])
+
+ # begin wxGlade: MyFrame.__init__
wx.Frame.__init__(self, None, wx.ID_ANY,
_("Ink/Stitch Lettering")
)
@@ -492,8 +494,9 @@ class Lettering(CommandsExtension):
return group
def effect(self):
+ metadata = self.get_inkstitch_metadata()
app = wx.App()
- frame = LetteringFrame(group=self.get_or_create_group(), on_cancel=self.cancel)
+ frame = LetteringFrame(group=self.get_or_create_group(), on_cancel=self.cancel, metadata=metadata)
# position left, center
current_screen = wx.Display.GetFromPoint(wx.GetMousePosition())
diff --git a/lib/extensions/params.py b/lib/extensions/params.py
index a85b16f7..fb13c223 100644
--- a/lib/extensions/params.py
+++ b/lib/extensions/params.py
@@ -478,9 +478,11 @@ class SettingsFrame(wx.Frame):
lc = wx.Locale()
lc.Init(wx.LANGUAGE_DEFAULT)
- # begin wxGlade: MyFrame.__init__
self.tabs_factory = kwargs.pop('tabs_factory', [])
self.cancel_hook = kwargs.pop('on_cancel', None)
+ self.metadata = kwargs.pop('metadata', [])
+
+ # begin wxGlade: MyFrame.__init__
wx.Frame.__init__(self, None, wx.ID_ANY,
_("Embroidery Params")
)
@@ -786,8 +788,11 @@ class Params(InkstitchExtension):
def effect(self):
try:
app = wx.App()
+ metadata = self.get_inkstitch_metadata()
frame = SettingsFrame(
- tabs_factory=self.create_tabs, on_cancel=self.cancel)
+ tabs_factory=self.create_tabs,
+ on_cancel=self.cancel,
+ metadata=metadata)
# position left, center
current_screen = wx.Display.GetFromPoint(wx.GetMousePosition())
diff --git a/lib/extensions/select_elements.py b/lib/extensions/select_elements.py
index 8fa9ca9d..896e04b0 100644
--- a/lib/extensions/select_elements.py
+++ b/lib/extensions/select_elements.py
@@ -21,6 +21,7 @@ class SelectElements(InkstitchExtension):
pars.add_argument("--info", type=str, dest="info")
pars.add_argument("--select-running-stitch", type=Boolean, dest="running", default=False)
+ pars.add_argument("--running-stitch-condition", type=str, dest="running_stitch_condition", default="all")
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)
@@ -101,7 +102,7 @@ class SelectElements(InkstitchExtension):
def _select_stroke(self, element):
select = False
method = element.stroke_method
- if self.options.running and method == 'running_stitch':
+ if self.options.running and method == 'running_stitch' and self._running_condition(element):
select = True
if self.options.ripples and method == 'ripple_stitch':
select = True
@@ -130,6 +131,11 @@ class SelectElements(InkstitchExtension):
select = True
return select
+ def _running_condition(self, element):
+ element_id = element.node.get_id() or ''
+ conditions = {'all': True, 'autorun-top': element_id.startswith('autorun'), 'autorun-underpath': element_id.startswith('underpath')}
+ return conditions[self.options.running_stitch_condition]
+
def _select_fill_underlay(self, element):
underlay = {'all': True, 'no': not element.fill_underlay, 'yes': element.fill_underlay}
return underlay[self.options.fill_underlay]
diff --git a/lib/extensions/stroke_to_lpe_satin.py b/lib/extensions/stroke_to_lpe_satin.py
index 4052207a..d162539b 100644
--- a/lib/extensions/stroke_to_lpe_satin.py
+++ b/lib/extensions/stroke_to_lpe_satin.py
@@ -26,6 +26,7 @@ class StrokeToLpeSatin(InkstitchExtension):
self.arg_parser.add_argument("-l", "--length", type=float, default=15, dest="length")
self.arg_parser.add_argument("-t", "--stretched", type=inkex.Boolean, default=False, dest="stretched")
self.arg_parser.add_argument("-r", "--rungs", type=inkex.Boolean, default=False, dest="add_rungs")
+ self.arg_parser.add_argument("-s", "--path-specific", type=inkex.Boolean, default=True, dest="path_specific")
def effect(self):
if not self.svg.selection or not self.get_elements():
@@ -52,48 +53,79 @@ class StrokeToLpeSatin(InkstitchExtension):
pattern_path = pattern_obj.get_path(self.options.add_rungs, min_width, max_width, length, self.svg.unit)
pattern_node_type = pattern_obj.node_types
+ if not self.options.path_specific:
+ lpe = self._create_lpe_element(pattern, pattern_path, pattern_node_type)
+
+ for element in self.elements:
+ if self.options.path_specific:
+ lpe = self._create_lpe_element(pattern, pattern_path, pattern_node_type, element)
+ if isinstance(element, SatinColumn):
+ self._process_satin_column(element, lpe)
+ elif isinstance(element, Stroke):
+ self._process_stroke(element, lpe)
+
+ def _create_lpe_element(self, pattern, pattern_path, pattern_node_type, element=None):
+ # define id for the lpe path
+ if not element:
+ lpe_id = f'inkstitch-effect-{pattern}'
+ else:
+ lpe_id = f'inkstitch-effect-{pattern}-{element.id}'
+
+ # it is possible, that there is already a path effect with this id, if so, use it
+ previous_lpe = self.svg.getElementById(lpe_id)
+ if previous_lpe is not None:
+ return previous_lpe
+
# the lpe 'pattern along path' has two options to repeat the pattern, get user input
copy_type = 'repeated' if self.options.stretched is False else 'repeated_stretched'
+ lpe = inkex.PathEffect(attrib={'id': lpe_id,
+ 'effect': "skeletal",
+ 'is_visible': "true",
+ 'lpeversion': "1",
+ 'pattern': pattern_path,
+ 'copytype': copy_type,
+ 'prop_scale': "1",
+ 'scale_y_rel': "false",
+ 'spacing': "0",
+ 'normal_offset': "0",
+ 'tang_offset': "0",
+ 'prop_units': "false",
+ 'vertical_pattern': "false",
+ 'hide_knot': "false",
+ 'fuse_tolerance': "0.02",
+ 'pattern-nodetypes': pattern_node_type})
# add the path effect element to the defs section
- self.lpe = inkex.PathEffect(attrib={'id': f'inkstitch-effect-{pattern}',
- 'effect': "skeletal",
- 'is_visible': "true",
- 'lpeversion': "1",
- 'pattern': pattern_path,
- 'copytype': copy_type,
- 'prop_scale': "1",
- 'scale_y_rel': "false",
- 'spacing': "0",
- 'normal_offset': "0",
- 'tang_offset': "0",
- 'prop_units': "false",
- 'vertical_pattern': "false",
- 'hide_knot': "false",
- 'fuse_tolerance': "0.02",
- 'pattern-nodetypes': pattern_node_type})
- self.svg.defs.add(self.lpe)
+ self.svg.defs.add(lpe)
+ return lpe
- for element in self.elements:
- if isinstance(element, SatinColumn):
- self._process_satin_column(element)
- elif isinstance(element, Stroke):
- self._process_stroke(element)
+ def _process_stroke(self, element, lpe):
+ element = self._ensure_path_element(element, lpe)
- def _process_stroke(self, element):
previous_effects = element.node.get(PATH_EFFECT, None)
if not previous_effects:
- element.node.set(PATH_EFFECT, self.lpe.get_id(as_url=1))
+ element.node.set(PATH_EFFECT, lpe.get_id(as_url=1))
element.node.set(ORIGINAL_D, element.node.get('d', ''))
else:
- url = previous_effects + ';' + self.lpe.get_id(as_url=1)
+ url = previous_effects + ';' + lpe.get_id(as_url=1)
element.node.set(PATH_EFFECT, url)
element.node.pop('d')
element.set_param('satin_column', 'true')
element.node.style['stroke-width'] = self.svg.viewport_to_unit('0.756')
- def _process_satin_column(self, element):
+ def _ensure_path_element(self, element, lpe):
+ # elements other than paths (rectangle, circles, etc.) can be handled by inkscape for lpe
+ # but they are way easier to handle for us if we turn them into paths
+ if element.node.TAG == 'path':
+ return element
+
+ path_element = element.node.to_path_element()
+ parent = element.node.getparent()
+ parent.replace(element.node, path_element)
+ return Stroke(path_element)
+
+ def _process_satin_column(self, element, lpe):
current_effects = element.node.get(PATH_EFFECT, None)
# there are possibly multiple path effects, let's check if inkstitch-effect is among them
if not current_effects or 'inkstitch-effect' not in current_effects:
@@ -106,10 +138,11 @@ class StrokeToLpeSatin(InkstitchExtension):
inkstitch_effect = current_effects[inkstitch_effect_position][1:]
# get the path effect element
old_effect_element = self.svg.getElementById(inkstitch_effect)
- # remove the old inkstitch-effect
- old_effect_element.getparent().remove(old_effect_element)
- # update the path effect link
- current_effects[inkstitch_effect_position] = self.lpe.get_id(as_url=1)
+ # remove the old inkstitch-effect if it is path specific
+ if inkstitch_effect.endswith(element.id):
+ old_effect_element.getparent().remove(old_effect_element)
+ # update path effect link
+ current_effects[inkstitch_effect_position] = lpe.get_id(as_url=1)
element.node.set(PATH_EFFECT, ';'.join(current_effects))
element.node.pop('d')
diff --git a/lib/gui/simulator.py b/lib/gui/simulator.py
index d9f51b48..2c1ccc2a 100644
--- a/lib/gui/simulator.py
+++ b/lib/gui/simulator.py
@@ -805,7 +805,10 @@ class SimulatorPreview(Thread):
return
if patches and not self.refresh_needed.is_set():
- stitch_plan = stitch_groups_to_stitch_plan(patches)
+ metadata = self.parent.metadata
+ collapse_len = metadata['collapse_len_mm']
+ min_stitch_len = metadata['min_stitch_len_mm']
+ stitch_plan = stitch_groups_to_stitch_plan(patches, collapse_len=collapse_len, min_stitch_len=min_stitch_len)
# GUI stuff needs to happen in the main thread, so we ask the main
# thread to call refresh_simulator().
diff --git a/templates/select_elements.xml b/templates/select_elements.xml
index fee1f467..a4ed148a 100644
--- a/templates/select_elements.xml
+++ b/templates/select_elements.xml
@@ -8,7 +8,15 @@
<page name="stitch-type" gui-text="Select options">
<label appearance="header">Select Stitch Type</label>
<label>Stroke type</label>
+ <hbox>
<param indent="1" name="select-running-stitch" type="boolean" gui-text="Running Stitch">false</param>
+ <param indent="1" name="running-stitch-condition" type="optiongroup" appearance="combo" gui-text="Select"
+ gui-description="Only select specific running stitches">
+ <option value="all">All</option>
+ <option value="autorun-top">Auto-Run Top Stitching</option>
+ <option value="autorun-underpath">Auto-Run Underpath</option>
+ </param>
+ </hbox>
<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>
@@ -53,7 +61,7 @@
<label>If this isn't working for you, you may need to insert your path to a python executable manualy.</label>
<spacer />
<label>* Windows: Open the "Command Prompt" and type "where python". Copy the path and paste it here.</label>
- <label>* Linux: Open the command line and type "which python". Copy the path and paste it here.</label>
+ <label>* Linux: Open the command line and type "which python3". Copy the path and paste it here.</label>
<label>* macOS: doesn't work, sorry</label>
<param name="python-path" type="string" gui-text="Python Path"></param>
</page>
diff --git a/templates/stroke_to_lpe_satin.xml b/templates/stroke_to_lpe_satin.xml
index 6e493d08..e0c480a7 100644
--- a/templates/stroke_to_lpe_satin.xml
+++ b/templates/stroke_to_lpe_satin.xml
@@ -20,6 +20,8 @@
<param name="length" type="float" precision="1" min="0.1" max="100" gui-text="Pattern Length (mm)">15</param>
<param name="stretched" type="boolean" gui-text="Stretched">false</param>
<param name="rungs" type="boolean" gui-text="Add rungs">true</param>
+ <param name="path-specific" type="boolean" gui-text="Path specific"
+ description="When disabled, changes on the path effect will apply to all elements with the same path effect.">true</param>
</page>
<page name="info" gui-text="Help">
<label appearance="header">This extension converts a stroke into a satin column using the path effect "pattern along path".</label>