summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/commands.py77
-rw-r--r--lib/elements/fill_stitch.py9
-rw-r--r--lib/elements/stroke.py8
-rw-r--r--lib/extensions/commands_scale_symbols.py3
-rw-r--r--lib/extensions/gradient_blocks.py35
-rw-r--r--lib/extensions/update_svg.py25
-rw-r--r--lib/gui/tartan/main_panel.py15
-rw-r--r--lib/marker.py4
-rw-r--r--lib/tartan/svg.py45
-rw-r--r--lib/update.py64
10 files changed, 113 insertions, 172 deletions
diff --git a/lib/commands.py b/lib/commands.py
index a0d81dce..858a1d5b 100644
--- a/lib/commands.py
+++ b/lib/commands.py
@@ -18,9 +18,9 @@ from .svg import (apply_transforms, generate_unique_id,
get_correction_transform, get_document, get_node_transform)
from .svg.svg import copy_no_children, point_upwards
from .svg.tags import (CONNECTION_END, CONNECTION_START, CONNECTOR_TYPE,
- INKSCAPE_LABEL, INKSTITCH_ATTRIBS, SVG_SYMBOL_TAG,
- SVG_USE_TAG, XLINK_HREF)
+ INKSCAPE_LABEL, SVG_SYMBOL_TAG, SVG_USE_TAG, XLINK_HREF)
from .utils import Point, cache, get_bundled_dir
+from .utils.geometry import ensure_multi_polygon
COMMANDS = {
# L10N command attached to an object
@@ -62,6 +62,7 @@ COMMANDS = {
OBJECT_COMMANDS = ["starting_point", "ending_point", "target_point", "autoroute_start", "autoroute_end",
"stop", "trim", "ignore_object", "satin_cut_point"]
+HIDDEN_CONNECTOR_COMMANDS = ["starting_point", "ending_point", "autoroute_start", "autoroute_end"]
FREE_MOVEMENT_OBJECT_COMMANDS = ["autoroute_start", "autoroute_end"]
LAYER_COMMANDS = ["ignore_layer"]
GLOBAL_COMMANDS = ["origin", "stop_position"]
@@ -125,24 +126,28 @@ class Command(BaseCommand):
raise CommandParseError("connector has no path information")
neighbors = [
- (self.get_node_by_url(self.connector.get(CONNECTION_START)), path[0][0][1]),
- (self.get_node_by_url(self.connector.get(CONNECTION_END)), path[0][-1][1])
+ self.get_node_by_url(self.connector.get(CONNECTION_START)),
+ self.get_node_by_url(self.connector.get(CONNECTION_END))
]
- self.symbol_is_end = neighbors[0][0].tag != SVG_USE_TAG
+ self.symbol_is_end = neighbors[0].tag != SVG_USE_TAG
if self.symbol_is_end:
neighbors.reverse()
- if neighbors[0][0].tag != SVG_USE_TAG:
+ if neighbors[0].tag != SVG_USE_TAG:
raise CommandParseError("connector does not point to a use tag")
- self.use = neighbors[0][0]
+ self.use = neighbors[0]
- self.symbol = self.get_node_by_url(neighbors[0][0].get(XLINK_HREF))
+ self.symbol = self.get_node_by_url(neighbors[0].get(XLINK_HREF))
self.parse_symbol()
- self.target: inkex.BaseElement = neighbors[1][0]
- self.target_point = neighbors[1][1]
+ self.target: inkex.BaseElement = neighbors[1]
+
+ pos = [float(self.use.get("x", 0)), float(self.use.get("y", 0))]
+ transform = get_node_transform(self.use)
+ pos = inkex.Transform(transform).apply_to_point(pos)
+ self.target_point = pos
def __repr__(self):
return "Command('%s', %s)" % (self.command, self.target_point)
@@ -331,7 +336,9 @@ def ensure_symbol(svg, command):
path = "./*[@id='inkstitch_%s']" % command
defs = svg.defs
if defs.find(path) is None:
- defs.append(deepcopy(symbol_defs().find(path)))
+ symbol = deepcopy(symbol_defs().find(path))
+ symbol.transform = 'scale(0.2)'
+ defs.append(symbol)
def add_group(document, node, command):
@@ -373,7 +380,7 @@ def add_connector(document, symbol, command, element):
path = inkex.PathElement(attrib={
"id": generate_unique_id(document, "command_connector"),
"d": f"M {start_pos[0]},{start_pos[1]} {end_pos[0]},{end_pos[1]}",
- "style": "stroke:#000000;stroke-width:1px;stroke-opacity:0.5;fill:none;",
+ "style": "fill:none;stroke:#000000;stroke-width:1;stroke-opacity:0.5;vector-effect: non-scaling-stroke;-inkscape-stroke: hairline;",
CONNECTION_START: f"#{symbol.get('id')}",
CONNECTION_END: f"#{element.node.get('id')}",
@@ -383,6 +390,8 @@ def add_connector(document, symbol, command, element):
if command not in FREE_MOVEMENT_OBJECT_COMMANDS:
path.attrib[CONNECTOR_TYPE] = "polyline"
+ if command in HIDDEN_CONNECTOR_COMMANDS:
+ path.style['display'] = 'none'
symbol.getparent().insert(0, path)
@@ -405,60 +414,28 @@ def add_symbol(document, group, command, pos):
def get_command_pos(element, index, total):
- # Put command symbols 30 pixels out from the shape, spaced evenly around it.
-
- # get a line running 30 pixels out from the shape
+ # Put command symbols on the outline of the shape, spaced evenly around it.
- if not isinstance(element.shape.buffer(30), shgeo.MultiPolygon):
- outline = element.shape.buffer(30).exterior
+ if element.name == "Stroke":
+ shape = element.as_multi_line_string()
else:
- polygons = element.shape.buffer(30).geoms
- polygon = polygons[len(polygons)-1]
- outline = polygon.exterior
-
- # find the top center point on the outline and start there
- top_center = shgeo.Point(outline.centroid.x, outline.bounds[1])
- start_position = outline.project(top_center, normalized=True)
+ shape = element.shape
+ polygon = ensure_multi_polygon(shape.buffer(0.01)).geoms[-1]
+ outline = polygon.exterior
# pick this item's spot around the outline and perturb it a bit to avoid
# stacking up commands if they add commands multiple times
position = index / float(total)
position += random() * 0.05
- position += start_position
return outline.interpolate(position, normalized=True)
-def remove_legacy_param(element, command):
- if command == "trim" or command == "stop":
- # If they had the old "TRIM after" or "STOP after" attributes set,
- # automatically delete them. The new commands will do the same
- # thing.
- #
- # If we didn't delete these here, then things would get confusing.
- # If the user were to delete a "trim" symbol added by this extension
- # but the "embroider_trim_after" attribute is still set, then the
- # trim would keep happening.
-
- attribute = "embroider_%s_after" % command
-
- if attribute in element.node.attrib:
- del element.node.attrib[attribute]
-
- # Attributes have changed to be namespaced.
- # Let's check for them as well, they might have automatically changed.
- attribute = INKSTITCH_ATTRIBS["%s_after" % command]
-
- if attribute in element.node.attrib:
- del element.node.attrib[attribute]
-
-
def add_commands(element, commands, pos=None):
svg = get_document(element.node)
for i, command in enumerate(commands):
ensure_symbol(svg, command)
- remove_legacy_param(element, command)
group = add_group(svg, element.node, command)
position = pos
diff --git a/lib/elements/fill_stitch.py b/lib/elements/fill_stitch.py
index 03a8e154..939d42d0 100644
--- a/lib/elements/fill_stitch.py
+++ b/lib/elements/fill_stitch.py
@@ -7,7 +7,7 @@ import math
import re
import numpy as np
-from inkex import LinearGradient, Transform
+from inkex import LinearGradient
from shapely import geometry as shgeo
from shapely import set_precision
from shapely.errors import GEOSException
@@ -22,7 +22,7 @@ from ..stitches import (auto_fill, circular_fill, contour_fill, guided_fill,
legacy_fill, linear_gradient_fill, meander_fill,
tartan_fill)
from ..stitches.linear_gradient_fill import gradient_angle
-from ..svg import PIXELS_PER_MM, get_node_transform
+from ..svg import PIXELS_PER_MM
from ..svg.clip import get_clip_path
from ..svg.tags import INKSCAPE_LABEL
from ..tartan.utils import get_tartan_settings, get_tartan_stripes
@@ -1196,10 +1196,7 @@ class FillStitch(EmbroideryElement):
# get target position
command = self.get_command('target_point')
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)
- target = shgeo.Point(*pos)
+ target = shgeo.Point(*command.target_point)
else:
target = shape.centroid
stitches = circular_fill(
diff --git a/lib/elements/stroke.py b/lib/elements/stroke.py
index 14501c87..96a9307f 100644
--- a/lib/elements/stroke.py
+++ b/lib/elements/stroke.py
@@ -6,7 +6,6 @@
from math import ceil
import shapely.geometry as shgeo
-from inkex import Transform
from shapely.errors import GEOSException
from ..i18n import _
@@ -15,7 +14,7 @@ from ..stitch_plan import StitchGroup
from ..stitches.ripple_stitch import ripple_stitch
from ..stitches.running_stitch import (bean_stitch, running_stitch,
zigzag_stitch)
-from ..svg import get_node_transform, parse_length_with_units
+from ..svg import parse_length_with_units
from ..svg.clip import get_clip_path
from ..threads import ThreadColor
from ..utils import Point, cache
@@ -542,10 +541,7 @@ class Stroke(EmbroideryElement):
def get_ripple_target(self):
command = self.get_command('target_point')
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)
+ return shgeo.Point(*command.target_point)
else:
return self.shape.centroid
diff --git a/lib/extensions/commands_scale_symbols.py b/lib/extensions/commands_scale_symbols.py
index 0e5f482a..fec8daf8 100644
--- a/lib/extensions/commands_scale_symbols.py
+++ b/lib/extensions/commands_scale_symbols.py
@@ -14,7 +14,8 @@ class CommandsScaleSymbols(InkstitchExtension):
self.arg_parser.add_argument("-s", "--size", dest="size", type=int, default=100)
def effect(self):
- size = self.options.size / 100
+ # by default commands are scaled down to 0.2
+ size = 0.2 * self.options.size / 100
# scale symbols
svg = self.document.getroot()
diff --git a/lib/extensions/gradient_blocks.py b/lib/extensions/gradient_blocks.py
index b87e5f9e..8e341cc8 100644
--- a/lib/extensions/gradient_blocks.py
+++ b/lib/extensions/gradient_blocks.py
@@ -9,27 +9,23 @@ from inkex import (DirectedLineSegment, LinearGradient, PathElement, Transform,
errormsg)
from shapely import geometry as shgeo
from shapely.affinity import rotate
-from shapely.geometry import Point
-from shapely.ops import nearest_points, split
+from shapely.ops import split
-from ..commands import add_commands
from ..elements import FillStitch
from ..i18n import _
from ..svg import PIXELS_PER_MM, get_correction_transform
from ..svg.tags import INKSTITCH_ATTRIBS
-from .commands import CommandsExtension
from .duplicate_params import get_inkstitch_attributes
+from .base import InkstitchExtension
-class GradientBlocks(CommandsExtension):
+class GradientBlocks(InkstitchExtension):
'''
This will break apart fill objects with a gradient fill into solid color blocks with end_row_spacing.
'''
- COMMANDS = ['starting_point', 'ending_point']
-
def __init__(self, *args, **kwargs):
- CommandsExtension.__init__(self, *args, **kwargs)
+ InkstitchExtension.__init__(self, *args, **kwargs)
self.arg_parser.add_argument("--notebook", type=str, default=0.0)
self.arg_parser.add_argument("--options", type=str, default=0.0)
self.arg_parser.add_argument("--info", type=str, default=0.0)
@@ -64,8 +60,6 @@ class GradientBlocks(CommandsExtension):
end_row_spacing = element.row_spacing / PIXELS_PER_MM * 2
end_row_spacing = f'{end_row_spacing: .2f}'
- previous_color = None
- previous_element = None
for i, shape in enumerate(fill_shapes):
color = attributes[i]['color']
style['fill'] = color
@@ -98,29 +92,8 @@ class GradientBlocks(CommandsExtension):
block.set('inkstitch:fill_underlay_row_spacing_mm', end_row_spacing)
parent.insert(index, block)
- if previous_color == color:
- self._add_block_commands(block, previous_element)
- previous_color = color
- previous_element = block
parent.remove(element.node)
- def _add_block_commands(self, block, previous_element):
- current = FillStitch(block)
- previous = FillStitch(previous_element)
- if previous.shape.is_empty or current.shape.is_empty:
- return
- nearest = nearest_points(current.shape, previous.shape)
- pos_current = self._get_command_postion(current, nearest[0])
- pos_previous = self._get_command_postion(previous, nearest[1])
- add_commands(current, ['ending_point'], pos_current)
- add_commands(previous, ['starting_point'], pos_previous)
-
- def _get_command_postion(self, fill, point):
- center = fill.shape.centroid
- line = DirectedLineSegment((center.x, center.y), (point.x, point.y))
- pos = line.point_at_length(line.length + 20)
- return Point(pos)
-
def _element_to_path(self, shape):
coords = list(shape.exterior.coords)
for interior in shape.interiors:
diff --git a/lib/extensions/update_svg.py b/lib/extensions/update_svg.py
index f620af2d..d51c1fa1 100644
--- a/lib/extensions/update_svg.py
+++ b/lib/extensions/update_svg.py
@@ -3,9 +3,6 @@
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
-from inkex import errormsg
-
-from ..i18n import _
from ..update import update_inkstitch_document
from .base import InkstitchExtension
@@ -14,24 +11,22 @@ class UpdateSvg(InkstitchExtension):
def __init__(self, *args, **kwargs):
InkstitchExtension.__init__(self, *args, **kwargs)
+ self.arg_parser.add_argument("--update-from", type=int, default=0, dest="update_from")
# inkstitch_svg_version history:
# 1 -> v3.0.0, May 2023
# 2 -> v.3.1.0 May 2024
-
- # TODO: When there are more legacy versions than only one, this can be transformed into a user input
- self.update_from = 0
+ # 3 -> v.3.2.0 May2025
def effect(self):
if not self.svg.selection:
- errormsg(_('Please select at least one element to update. '
- 'This extension is designed to help you update copy and pasted elements from old designs.'))
-
- # set the file version to the update_from value, so that the updater knows where to start from
- # the updater will then reset it to the current version after the update has finished
- metadata = self.get_inkstitch_metadata()
- metadata['inkstitch_svg_version'] = self.update_from
-
- update_inkstitch_document(self.document, self.get_selection())
+ update_inkstitch_document(self.document, warn_unversioned=False)
+ else:
+ # set the file version to the update_from value, so that the updater knows where to start from
+ # the updater will then reset it to the current version after the update has finished
+ metadata = self.get_inkstitch_metadata()
+ metadata['inkstitch_svg_version'] = self.options.update_from
+
+ update_inkstitch_document(self.document, self.get_selection(), warn_unversioned=False)
def get_selection(self):
selection = []
diff --git a/lib/gui/tartan/main_panel.py b/lib/gui/tartan/main_panel.py
index 9df6a748..4435f473 100644
--- a/lib/gui/tartan/main_panel.py
+++ b/lib/gui/tartan/main_panel.py
@@ -215,10 +215,14 @@ class TartanMainPanel(wx.Panel):
self.save_settings()
stitch_groups = []
previous_stitch_group = None
- for element in self.elements:
+ next_elements = [None]
+ if len(self.elements) > 1:
+ next_elements = self.elements[1:] + next_elements
+ for element, next_element in zip(self.elements, next_elements):
+ check_stop_flag()
try:
# copy the embroidery element to drop the cache
- stitch_groups.extend(copy(FillStitch(element)).embroider(previous_stitch_group))
+ stitch_groups.extend(copy(FillStitch(element)).embroider(previous_stitch_group, next_element))
if stitch_groups:
previous_stitch_group = stitch_groups[-1]
except (SystemExit, ExitThread):
@@ -241,13 +245,16 @@ class TartanMainPanel(wx.Panel):
for element in self.elements:
parent = element.getparent()
embroidery_elements = nodes_to_elements(parent.iterdescendants())
- for embroidery_element in embroidery_elements:
+ next_elements = [None]
+ if len(embroidery_elements) > 1:
+ next_elements = embroidery_elements[1:] + next_elements
+ for embroidery_element, next_element in zip(embroidery_elements, next_elements):
check_stop_flag()
if embroidery_element.node == element:
continue
try:
# copy the embroidery element to drop the cache
- stitch_groups.extend(copy(embroidery_element).embroider(previous_stitch_group))
+ stitch_groups.extend(copy(embroidery_element).embroider(previous_stitch_group, next_element))
if stitch_groups:
previous_stitch_group = stitch_groups[-1]
except InkstitchException:
diff --git a/lib/marker.py b/lib/marker.py
index ac19fe74..9765fbfb 100644
--- a/lib/marker.py
+++ b/lib/marker.py
@@ -18,7 +18,9 @@ MARKER = ['anchor-line', 'pattern', 'guide-line']
def ensure_marker(svg, marker):
marker_path = ".//*[@id='inkstitch-%s-marker']" % marker
if svg.defs.find(marker_path) is None:
- svg.defs.append(deepcopy(_marker_svg().defs.find(marker_path)))
+ marker = deepcopy(_marker_svg().defs.find(marker_path))
+ marker.set('markerWidth', str(0.1))
+ svg.defs.append(marker)
@cache
diff --git a/lib/tartan/svg.py b/lib/tartan/svg.py
index 15646629..497d0199 100644
--- a/lib/tartan/svg.py
+++ b/lib/tartan/svg.py
@@ -13,10 +13,8 @@ from inkex import BaseElement, Group, Path, PathElement
from networkx import MultiGraph, is_empty
from shapely import (LineString, MultiLineString, MultiPolygon, Point, Polygon,
dwithin, minimum_bounding_radius, reverse)
-from shapely.affinity import scale
from shapely.ops import linemerge, substring
-from ..commands import add_commands
from ..elements import FillStitch
from ..stitches.auto_fill import (PathEdge, build_fill_stitch_graph,
build_travel_graph, find_stitch_path,
@@ -127,52 +125,13 @@ class TartanSvgGroup:
for color, fill_elements in fills.items():
for element in fill_elements:
group.append(element)
- if self.stitch_type == "auto_fill":
- self._add_command(element)
- else:
- element.pop('inkstitch:start')
- element.pop('inkstitch:end')
+ element.pop('inkstitch:start')
+ element.pop('inkstitch:end')
for color, stroke_elements in strokes.items():
for element in stroke_elements:
group.append(element)
- def _get_command_position(self, fill: FillStitch, point: Tuple[float, float]) -> Point:
- """
- Shift command position out of the element shape
-
- :param fill: the fill element to which to attach the command
- :param point: position where the command should point to
- """
- dimensions, center = self._get_dimensions(fill.shape)
- line = LineString([center, point])
- fact = 20 / line.length
- line = scale(line, xfact=1+fact, yfact=1+fact, origin=center)
- pos = line.coords[-1]
- return Point(pos)
-
- def _add_command(self, element: BaseElement) -> None:
- """
- Add a command to given svg element
-
- :param element: svg element to which to attach the command
- """
- if not element.style('fill'):
- return
- fill = FillStitch(element)
- if fill.shape.is_empty:
- return
- start = element.get('inkstitch:start')
- end = element.get('inkstitch:end')
- if start:
- start = start[1:-1].split(',')
- add_commands(fill, ['starting_point'], self._get_command_position(fill, (float(start[0]), float(start[1]))))
- element.pop('inkstitch:start')
- if end:
- end = end[1:-1].split(',')
- add_commands(fill, ['ending_point'], self._get_command_position(fill, (float(end[0]), float(end[1]))))
- element.pop('inkstitch:end')
-
def _route_shapes(self, routing_lines: defaultdict, outline_shape: MultiPolygon, shapes: defaultdict, weft: bool = False) -> defaultdict:
"""
Route polygons and linestrings
diff --git a/lib/update.py b/lib/update.py
index 33ae6cc7..4351beb7 100644
--- a/lib/update.py
+++ b/lib/update.py
@@ -5,13 +5,15 @@
from inkex import errormsg
-from .commands import ensure_symbol
-from .elements import EmbroideryElement
+from .commands import add_commands, ensure_symbol
+from .elements import EmbroideryElement, Stroke
from .gui.request_update_svg_version import RequestUpdate
from .i18n import _
from .metadata import InkStitchMetadata
from .svg import PIXELS_PER_MM
-from .svg.tags import EMBROIDERABLE_TAGS, INKSTITCH_ATTRIBS
+from .svg.tags import (CONNECTION_END, CONNECTION_START, EMBROIDERABLE_TAGS,
+ INKSTITCH_ATTRIBS, SVG_USE_TAG)
+from .utils import Point as InkstitchPoint
INKSTITCH_SVG_VERSION = 3
@@ -48,10 +50,10 @@ def update_inkstitch_document(svg, selection=None, warn_unversioned=True):
return
# update elements
- if selection:
- # this comes from the updater extension where we only update selected elements
+ if selection is not None:
+ # the updater extension might want to only update selected elements
for element in selection:
- update_legacy_params(EmbroideryElement(element), file_version, INKSTITCH_SVG_VERSION)
+ update_legacy_params(document, EmbroideryElement(element), file_version, INKSTITCH_SVG_VERSION)
else:
# this is the automatic update when a legacy inkstitch svg version was recognized
automatic_version_update(document, file_version, INKSTITCH_SVG_VERSION, warn_unversioned)
@@ -69,9 +71,7 @@ def automatic_version_update(document, file_version, INKSTITCH_SVG_VERSION, warn
# well then, let's update legeacy params
for element in document.iterdescendants():
if element.tag in EMBROIDERABLE_TAGS:
- update_legacy_params(EmbroideryElement(element), file_version, INKSTITCH_SVG_VERSION)
- if file_version < 3:
- update_legacy_commands(document)
+ update_legacy_params(document, EmbroideryElement(element), file_version, INKSTITCH_SVG_VERSION)
def _update_inkstitch_svg_version(svg):
@@ -80,24 +80,25 @@ def _update_inkstitch_svg_version(svg):
metadata['inkstitch_svg_version'] = INKSTITCH_SVG_VERSION
-def update_legacy_params(element, file_version, inkstitch_svg_version):
+def update_legacy_params(document, element, file_version, inkstitch_svg_version):
for version in range(file_version + 1, inkstitch_svg_version + 1):
- _update_to(version, element)
+ _update_to(document, version, element)
-def _update_to(version, element):
+def _update_to(document, version, element):
if version == 1:
_update_to_one(element)
elif version == 2:
_update_to_two(element)
elif version == 3:
- _update_to_three(element)
+ _update_to_three(document, element)
-def _update_to_three(element):
+def _update_to_three(document, element):
if element.get_boolean_param('satin_column', False):
element.set_param('start_at_nearest_point', False)
element.set_param('end_at_nearest_point', False)
+ update_legacy_commands(document, element)
def _update_to_two(element):
@@ -193,7 +194,7 @@ Update legacy commands
'''
-def update_legacy_commands(document):
+def update_legacy_commands(document, element):
'''
Changes for svg version 3
'''
@@ -208,6 +209,39 @@ def update_legacy_commands(document):
_rename_command(document, symbol, 'inkstitch_run_end', 'autoroute_end')
_rename_command(document, symbol, 'inkstitch_ripple_target', 'target_point')
+ # reposition commands
+ start = element.get_command('starting_point')
+ if start:
+ reposition_legacy_command(start)
+ end = element.get_command('ending_point')
+ if end:
+ reposition_legacy_command(end)
+
+
+def reposition_legacy_command(command):
+ connector = command.connector
+ command_group = connector.getparent()
+ element = command.target
+ command_name = command.command
+
+ # get new target position
+ path = command.parse_connector_path()
+ if len(path) == 0:
+ pass
+ neighbors = [
+ (command.get_node_by_url(command.connector.get(CONNECTION_START)), path[0][0][1]),
+ (command.get_node_by_url(command.connector.get(CONNECTION_END)), path[0][-1][1])
+ ]
+ symbol_is_end = neighbors[0][0].tag != SVG_USE_TAG
+ if symbol_is_end:
+ neighbors.reverse()
+ target_point = neighbors[1][1]
+
+ # instead of calculating the transform for the new position, we take the easy route and remove
+ # the old commands and set new ones
+ add_commands(Stroke(element), [command_name], InkstitchPoint(*target_point))
+ command_group.getparent().remove(command_group)
+
def _rename_command(document, symbol, old_name, new_name):
symbol_id = symbol.get_id()