diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/commands.py | 20 | ||||
| -rw-r--r-- | lib/elements/fill_stitch.py | 14 | ||||
| -rw-r--r-- | lib/elements/satin_column.py | 308 | ||||
| -rw-r--r-- | lib/elements/stroke.py | 2 | ||||
| -rw-r--r-- | lib/extensions/auto_run.py | 4 | ||||
| -rw-r--r-- | lib/extensions/auto_satin.py | 4 | ||||
| -rw-r--r-- | lib/extensions/cut_satin.py | 2 | ||||
| -rw-r--r-- | lib/extensions/gradient_blocks.py | 6 | ||||
| -rw-r--r-- | lib/extensions/redwork.py | 4 | ||||
| -rw-r--r-- | lib/svg/path.py | 3 | ||||
| -rw-r--r-- | lib/svg/tags.py | 2 | ||||
| -rw-r--r-- | lib/tartan/svg.py | 4 | ||||
| -rw-r--r-- | lib/update.py | 42 |
13 files changed, 344 insertions, 71 deletions
diff --git a/lib/commands.py b/lib/commands.py index 83798f10..a0d81dce 100644 --- a/lib/commands.py +++ b/lib/commands.py @@ -24,25 +24,19 @@ from .utils import Point, cache, get_bundled_dir COMMANDS = { # L10N command attached to an object - "fill_start": N_("Fill stitch starting position"), + "starting_point": N_("Starting position"), # L10N command attached to an object - "fill_end": N_("Fill stitch ending position"), + "ending_point": N_("Ending position"), # L10N command attached to an object - "ripple_target": N_("Target position"), + "target_point": N_("Target position"), # L10N command attached to an object - "run_start": N_("Auto-route running stitch starting position"), + "autoroute_start": N_("Auto-route starting position"), # L10N command attached to an object - "run_end": N_("Auto-route running stitch ending position"), - - # L10N command attached to an object - "satin_start": N_("Auto-route satin stitch starting position"), - - # L10N command attached to an object - "satin_end": N_("Auto-route satin stitch ending position"), + "autoroute_end": N_("Auto-route ending position"), # L10N command attached to an object "stop": N_("Stop (pause machine) after sewing this object"), @@ -66,9 +60,9 @@ COMMANDS = { "stop_position": N_("Jump destination for Stop commands (a.k.a. \"Frame Out position\")."), } -OBJECT_COMMANDS = ["fill_start", "fill_end", "ripple_target", "run_start", "run_end", "satin_start", "satin_end", +OBJECT_COMMANDS = ["starting_point", "ending_point", "target_point", "autoroute_start", "autoroute_end", "stop", "trim", "ignore_object", "satin_cut_point"] -FREE_MOVEMENT_OBJECT_COMMANDS = ["run_start", "run_end", "satin_start", "satin_end"] +FREE_MOVEMENT_OBJECT_COMMANDS = ["autoroute_start", "autoroute_end"] LAYER_COMMANDS = ["ignore_layer"] GLOBAL_COMMANDS = ["origin", "stop_position"] diff --git a/lib/elements/fill_stitch.py b/lib/elements/fill_stitch.py index 04ddeaea..9b330947 100644 --- a/lib/elements/fill_stitch.py +++ b/lib/elements/fill_stitch.py @@ -891,25 +891,25 @@ class FillStitch(EmbroideryElement): return self.shrink_or_grow_shape(shape, self.expand) def get_starting_point(self, previous_stitch_group): - # If there is a "fill_start" Command, then use that; otherwise pick + # If there is a "starting_point" Command, then use that; otherwise pick # the point closest to the end of the last stitch_group. - if self.get_command('fill_start'): - return self.get_command('fill_start').target_point + if self.get_command('starting_point'): + return self.get_command('starting_point').target_point elif previous_stitch_group: return previous_stitch_group.stitches[-1] else: return None def uses_previous_stitch(self): - if self.get_command('fill_start'): + if self.get_command('starting_point'): return False else: return True def get_ending_point(self): - if self.get_command('fill_end'): - return self.get_command('fill_end').target_point + if self.get_command('ending_point'): + return self.get_command('ending_point').target_point else: return None @@ -1177,7 +1177,7 @@ class FillStitch(EmbroideryElement): def do_circular_fill(self, shape, last_stitch_group, starting_point, ending_point): # get target position - command = self.get_command('ripple_target') + 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) diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py index 2353fc1c..49f09e22 100644 --- a/lib/elements/satin_column.py +++ b/lib/elements/satin_column.py @@ -13,12 +13,12 @@ from inkex import paths from shapely import affinity as shaffinity from shapely import geometry as shgeo from shapely import set_precision -from shapely.ops import nearest_points +from shapely.ops import nearest_points, substring from ..debug.debug import debug from ..i18n import _ from ..metadata import InkStitchMetadata -from ..stitch_plan import StitchGroup +from ..stitch_plan import Stitch, StitchGroup from ..stitches import running_stitch from ..svg import line_strings_to_csp, point_lists_to_csp from ..utils import Point, cache, cut, cut_multiple, offset_points, prng @@ -359,6 +359,46 @@ class SatinColumn(EmbroideryElement): return self.get_boolean_param('swap_satin_rails', False) @property + @param('running_stitch_length_mm', + _('Running stitch length'), + tooltip=_('Length of stitches for start and end point connections.'), + unit='mm', + type='float', + default=2.5, + sort_index=20) + def running_stitch_length(self): + return max(self.get_float_param("running_stitch_length_mm", 2.5), 0.01) + + @property + @param('running_stitch_tolerance_mm', + _('Running stitch tolerance'), + tooltip=_('Determines how hard Ink/Stitch tries to avoid stitching outside the shape.' + 'Lower numbers are less likely to stitch outside the shape but require more stitches.'), + unit='mm', + type='float', + default=0.1, + sort_index=21) + def running_stitch_tolerance(self): + return max(self.get_float_param("running_stitch_tolerance_mm", 0.2), 0.01) + + @property + @param('running_stitch_position', + _('Running Stitch Position'), + tooltip=_('Position of running stitches between the rails. 0% is along the first rail, 50% is centered, 100% is along the second rail.'), + type='float', unit='%', default=50, + sort_index=22) + def running_stitch_position(self): + return min(100, max(0, self.get_float_param("running_stitch_position", 50))) + + @property + @param('start_at_nearest_point', + _('Start at nearest point'), + tooltip=_('Start at nearest point to previous element. A start position command will overwrite this setting.'), + default=False, type='boolean', sort_index=23) + def start_at_nearest_point(self): + return self.get_boolean_param('start_at_nearest_point') + + @property @param('contour_underlay', _('Contour underlay'), type='toggle', group=_('Contour Underlay')) def contour_underlay(self): # "Contour underlay" is stitching just inside the rectangular shape @@ -772,7 +812,7 @@ class SatinColumn(EmbroideryElement): yield NotStitchableError(self.flattened_rails[0].representative_point()) def _center_walk_is_odd(self): - return self.center_walk_underlay_repeats % 2 == 1 + return self.center_walk_underlay and self.center_walk_underlay_repeats % 2 == 1 def reverse(self): """Return a new SatinColumn like this one but in the opposite direction. @@ -807,6 +847,20 @@ class SatinColumn(EmbroideryElement): return self._csp_to_satin(point_lists_to_csp(point_lists)) + def flip(self): + """Return a new SatinColumn like this one but with flipped rails. + + The path will be flattened and the new satin will contain a new XML + node that is not yet in the SVG. + """ + csp = self.path + + if len(csp) > 1: + first, second = self.rail_indices + csp[first], csp[second] = csp[second], csp[first] + + return self._csp_to_satin(csp) + def apply_transform(self): """Return a new SatinColumn like this one but with transforms applied. @@ -837,6 +891,18 @@ class SatinColumn(EmbroideryElement): if cut_points is None: cut_points = self.find_cut_points(split_point) path_lists = self._cut_rails(cut_points) + + # prevent error when split points lies at the start or end of the satin column + cleaned_path_lists = path_lists + for i, path_list in enumerate(path_lists): + if None in path_list: + cleaned_path_lists[i] = None + continue + for path in path_list: + if shgeo.LineString(path).length < self.zigzag_spacing: + cleaned_path_lists[i] = None + path_lists = cleaned_path_lists + self._assign_rungs_to_split_rails(path_lists) self._add_rungs_if_necessary(path_lists) return [self._path_list_to_satins(path_list) for path_list in path_lists] @@ -919,7 +985,8 @@ class SatinColumn(EmbroideryElement): rungs = [shgeo.LineString(self.flatten_subpath(rung)) for rung in self.rungs] for path_list in split_rails: - path_list.extend(rung for rung in rungs if path_list[0].intersects(rung) and path_list[1].intersects(rung)) + if path_list is not None: + path_list.extend(rung for rung in rungs if path_list[0].intersects(rung) and path_list[1].intersects(rung)) def _add_rungs_if_necessary(self, path_lists): """Add an additional rung to each new satin if needed. @@ -936,6 +1003,8 @@ class SatinColumn(EmbroideryElement): """ for path_list in path_lists: + if path_list is None: + continue if len(path_list) in (2, 4): # Add the rung just after the start of the satin. # If the rails have opposite directions it may end up at the end of the satin. @@ -953,7 +1022,10 @@ class SatinColumn(EmbroideryElement): path_list.append(rung) def _path_list_to_satins(self, path_list): - return self._csp_to_satin(line_strings_to_csp(path_list)) + linestrings = line_strings_to_csp(path_list) + if not linestrings: + return None + return self._csp_to_satin(linestrings) def _csp_to_satin(self, csp): node = deepcopy(self.node) @@ -1024,6 +1096,23 @@ class SatinColumn(EmbroideryElement): center_walk = [center_walk[0], center_walk[0]] return shgeo.LineString(center_walk) + @property + @cache + def offset_center_line(self): + stitches = self._get_center_line_stitches() + linestring = shgeo.LineString(stitches) + return linestring + + def _get_center_line_stitches(self): + inset_prop = -np.array([self.running_stitch_position, 100-self.running_stitch_position]) / 100 + + # Do it like contour underlay, but inset all the way to the center. + pairs = self.plot_points_on_rails(self.running_stitch_tolerance, (0, 0), inset_prop) + + points = [points[0] for points in pairs] + stitches = running_stitch.even_running_stitch(points, self.running_stitch_length, self.running_stitch_tolerance) + return stitches + def _stitch_distance(self, pos0, pos1, previous_pos0, previous_pos1): """Return the distance from one stitch to the next.""" @@ -1047,7 +1136,7 @@ class SatinColumn(EmbroideryElement): return max(abs(d0 * normal), abs(d1 * normal)) @debug.time - def plot_points_on_rails(self, spacing, offset_px=(0, 0), offset_proportional=(0, 0), use_random=False + def plot_points_on_rails(self, spacing, offset_px=(0, 0), offset_proportional=(0, 0), use_random=False, ) -> typing.List[typing.Tuple[Point, Point]]: # Take a section from each rail in turn, and plot out an equal number # of points on both rails. Return the points plotted. The points will @@ -1158,6 +1247,75 @@ class SatinColumn(EmbroideryElement): return pairs + def do_start_path(self, satins, start_point): + start_stitch_group = StitchGroup( + color=self.color, + tags=("satin_column", "satin_column_underlay"), + stitches=[Stitch(*start_point)] + ) + connector = self.offset_center_line + split_line = shgeo.LineString(self.find_cut_points(start_point)) + start = connector.project(nearest_points(split_line, connector)[1]) + + if self.end_point is None: + end = 0 + elif satins[0] is None: + if self._center_walk_is_odd(): + end = 0 + else: + end = connector.length + elif satins[1] is None: + if self._center_walk_is_odd(): + end = connector.length + else: + end = 0 + else: + if not self._center_walk_is_odd(): + end = 0 + else: + split_line = shgeo.LineString(self.find_cut_points(self.end_point)) + end = connector.project(nearest_points(split_line, connector)[1]) + + start_path = substring(connector, start, end) + stitches = [Stitch(*coord) for coord in start_path.coords] + stitch_group = StitchGroup( + color=self.color, + tags=("satin_column", "satin_column_underlay"), + stitches=stitches + ) + stitch_group = self.connect_and_add(start_stitch_group, stitch_group) + return stitch_group + + def do_end_point_connection(self): + if self._center_walk_is_odd(): + return StitchGroup() + center_line = self.offset_center_line.reverse() + stitches = [Stitch(*coord) for coord in center_line.coords] + stitch_group = StitchGroup( + color=self.color, + tags=("satin_column", "satin_column_underlay"), + stitches=stitches + ) + return stitch_group + + def _do_underlay_stitch_groups(self, i, satin, stitch_group): + if self.center_walk_underlay: + center_walk = satin.do_center_walk() + stitch_group = self.connect_and_add(stitch_group, center_walk) + + # if they just went one stitch back, it's not really necessary to add all the underlays + if i == 0 or satin.center_line.length > self.zigzag_spacing: + if self.contour_underlay: + contour = satin.do_contour_underlay() + stitch_group = self.connect_and_add(stitch_group, contour) + + if self.zigzag_underlay: + # zigzag underlay comes after contour walk underlay, so that the + # zigzags sit on the contour walk underlay like rail ties on rails. + zigzag = satin.do_zigzag_underlay() + stitch_group = self.connect_and_add(stitch_group, zigzag) + return stitch_group + def do_contour_underlay(self): # "contour walk" underlay: do stitches up one side and down the # other. if the two sides are far away, adding a running stitch to travel @@ -1196,19 +1354,12 @@ class SatinColumn(EmbroideryElement): def do_center_walk(self): # Center walk underlay is just a running stitch down and back on the # center line between the bezier curves. + repeats = self.center_walk_underlay_repeats - inset_prop = -np.array([self.center_walk_underlay_position, 100-self.center_walk_underlay_position]) / 100 - - # Do it like contour underlay, but inset all the way to the center. - pairs = self.plot_points_on_rails( - self.center_walk_underlay_stitch_tolerance, - (0, 0), inset_prop) - - points = [points[0] for points in pairs] - stitches = running_stitch.even_running_stitch(points, self.center_walk_underlay_stitch_length, self.center_walk_underlay_stitch_tolerance) + stitches = self._get_center_line_stitches() repeated_stitches = [] - for i in range(self.center_walk_underlay_repeats - 1): + for i in range(repeats - 1): if i % 2 == 0: repeated_stitches.extend(reversed(stitches)) else: @@ -1260,6 +1411,17 @@ class SatinColumn(EmbroideryElement): stitch_group.add_tags(("satin_column", "satin_column_underlay", "satin_zigzag_underlay")) return stitch_group + def _do_top_layer_stitch_group(self, satin): + if self.satin_method == 'e_stitch': + stitch_group = satin.do_e_stitch() + elif self.satin_method == 's_stitch': + stitch_group = satin.do_s_stitch() + elif self.satin_method == 'zigzag': + stitch_group = satin.do_zigzag() + else: + stitch_group = satin.do_satin() + return stitch_group + def do_satin(self): # satin: do a zigzag pattern, alternating between the paths. The # zigzag looks like this to make the satin stitches look perpendicular @@ -1410,7 +1572,7 @@ class SatinColumn(EmbroideryElement): if self._center_walk_is_odd(): stitch_group.stitches = list(reversed(stitch_group.stitches)) - stitch_group.add_tags(("satin", "s_stitch")) + stitch_group.add_tags(("satin_column", "s_stitch")) return stitch_group def do_zigzag(self): @@ -1567,51 +1729,121 @@ class SatinColumn(EmbroideryElement): stitch_group.add_stitch(end_stitch) def connect_and_add(self, stitch_group, next_stitch_group): - + if not next_stitch_group.stitches: + return stitch_group if stitch_group.stitches: self.add_running_stitches(stitch_group.stitches[-1], next_stitch_group.stitches[0], stitch_group) stitch_group += next_stitch_group return stitch_group + @property + def start_point(self): + return self._get_command_point('starting_point') + + @property + def end_point(self): + return self._get_command_point('ending_point') + + def _get_command_point(self, command): + point = self.get_command(command) + if point is not None: + point = point.target_point + return point + + def _split_satin(self): + if self.end_point is not None: + satins = self.split(self.end_point) + if self.reverse_rails == 'automatic': + self._adapt_automatic_rail_swapping(satins) + if satins[0] is None: + if not self._center_walk_is_odd(): + satins[1] = satins[1].reverse() + if self.swap_rails: + satins[1] = satins[1].flip() + satins = [None, satins[1]] + elif satins[1] is not None: + if self._center_walk_is_odd(): + satins[0] = satins[0].reverse() + else: + satins[1] = satins[1].reverse() + if self.swap_rails: + satins[0] = satins[0].flip() + else: + if self._center_walk_is_odd(): + satins[0] = satins[0].reverse() + satins = [satins[0], None] + else: + satins = [self, None] + return satins + + def _adapt_automatic_rail_swapping(self, satins): # noqa: C901 + reverse_rails = self._get_rails_to_reverse() + if reverse_rails == (False, False): + if satins[0] is not None: + satins[0].set_param('reverse_rails', 'none') + if satins[1] is not None: + satins[1].set_param('reverse_rails', 'none') + elif reverse_rails == (True, False): + if satins[0] is not None: + satins[0].set_param('reverse_rails', 'first') + if satins[1] is not None: + satins[1].set_param('reverse_rails', 'first') + elif reverse_rails == (False, True): + if satins[0] is not None: + satins[0].set_param('reverse_rails', 'second') + if satins[1] is not None: + satins[1].set_param('reverse_rails', 'second') + elif reverse_rails == (True, True): + if satins[0] is not None: + satins[0].set_param('reverse_rails', 'both') + if satins[1] is not None: + satins[1].set_param('reverse_rails', 'both') + def to_stitch_groups(self, last_stitch_group=None): # Stitch a variable-width satin column, zig-zagging between two paths. # The algorithm will draw zigzags between each consecutive pair of # beziers. The boundary points between beziers serve as "checkpoints", # allowing the user to control how the zigzags flow around corners. + satins = self._split_satin() + stitch_group = StitchGroup( color=self.color, force_lock_stitches=self.force_lock_stitches, lock_stitches=self.lock_stitches ) - if self.center_walk_underlay: - stitch_group += self.do_center_walk() + start_point = self.start_point + if start_point is None and last_stitch_group is not None and self.start_at_nearest_point: + start_point = nearest_points(shgeo.Point(*last_stitch_group.stitches[-1]), self.center_line)[1] + start_point = Point(*list(start_point.coords[0])) + if start_point is not None: + start_path = self.do_start_path(satins, start_point) + stitch_group = self.connect_and_add(stitch_group, start_path) - if self.contour_underlay: - contour = self.do_contour_underlay() - stitch_group = self.connect_and_add(stitch_group, contour) - - if self.zigzag_underlay: - # zigzag underlay comes after contour walk underlay, so that the - # zigzags sit on the contour walk underlay like rail ties on rails. - zigzag = self.do_zigzag_underlay() - stitch_group = self.connect_and_add(stitch_group, zigzag) + for i, satin in enumerate(satins): + if satin is None: + continue - if self.satin_method == 'e_stitch': - final_stitch_group = self.do_e_stitch() - elif self.satin_method == 's_stitch': - final_stitch_group = self.do_s_stitch() - elif self.satin_method == 'zigzag': - final_stitch_group = self.do_zigzag() - else: - final_stitch_group = self.do_satin() + if i > 0 and None not in satins: + end_point_connection = satin.do_end_point_connection() + stitch_group = self.connect_and_add(stitch_group, end_point_connection) - stitch_group = self.connect_and_add(stitch_group, final_stitch_group) + stitch_group = self._do_underlay_stitch_groups(i, satin, stitch_group) + final_stitch_group = self._do_top_layer_stitch_group(satin) + stitch_group = self.connect_and_add(stitch_group, final_stitch_group) if not stitch_group.stitches: return [] + if self.end_point: + ending_point_stitch_group = StitchGroup( + color=self.color, + tags=("satin_column"), + stitches=[Point(*self.end_point)] + ) + stitch_group = self.connect_and_add(stitch_group, ending_point_stitch_group) + return [stitch_group] diff --git a/lib/elements/stroke.py b/lib/elements/stroke.py index c0da08fe..ff9718c5 100644 --- a/lib/elements/stroke.py +++ b/lib/elements/stroke.py @@ -515,7 +515,7 @@ class Stroke(EmbroideryElement): return coords def get_ripple_target(self): - command = self.get_command('ripple_target') + 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) diff --git a/lib/extensions/auto_run.py b/lib/extensions/auto_run.py index 02997fd0..3b2003e4 100644 --- a/lib/extensions/auto_run.py +++ b/lib/extensions/auto_run.py @@ -35,10 +35,10 @@ class AutoRun(CommandsExtension): autorun(elements, self.options.preserve_order, break_up, starting_point, ending_point, self.options.trim) def get_starting_point(self): - return self.get_command_point("run_start") + return self.get_command_point("autoroute_start") def get_ending_point(self): - return self.get_command_point("run_end") + return self.get_command_point("autoroute_end") def get_command_point(self, command_type): command = None diff --git a/lib/extensions/auto_satin.py b/lib/extensions/auto_satin.py index 8536e4ee..d5456bcb 100644 --- a/lib/extensions/auto_satin.py +++ b/lib/extensions/auto_satin.py @@ -22,10 +22,10 @@ class AutoSatin(CommandsExtension): self.arg_parser.add_argument("-p", "--preserve_order", dest="preserve_order", type=inkex.Boolean, default=False) def get_starting_point(self): - return self.get_point("satin_start") + return self.get_point("autoroute_start") def get_ending_point(self): - return self.get_point("satin_end") + return self.get_point("autoroute_end") def get_point(self, command_type): command = None diff --git a/lib/extensions/cut_satin.py b/lib/extensions/cut_satin.py index a2c4f504..132c727f 100644 --- a/lib/extensions/cut_satin.py +++ b/lib/extensions/cut_satin.py @@ -39,6 +39,8 @@ class CutSatin(InkstitchExtension): command.connector.getparent().remove(command.connector) new_satins = satin.split(split_point) + if None in new_satins: + continue transform = get_correction_transform(satin.node) parent = satin.node.getparent() index = parent.index(satin.node) diff --git a/lib/extensions/gradient_blocks.py b/lib/extensions/gradient_blocks.py index 68e1e8e9..b87e5f9e 100644 --- a/lib/extensions/gradient_blocks.py +++ b/lib/extensions/gradient_blocks.py @@ -26,7 +26,7 @@ class GradientBlocks(CommandsExtension): This will break apart fill objects with a gradient fill into solid color blocks with end_row_spacing. ''' - COMMANDS = ['fill_start', 'fill_end'] + COMMANDS = ['starting_point', 'ending_point'] def __init__(self, *args, **kwargs): CommandsExtension.__init__(self, *args, **kwargs) @@ -112,8 +112,8 @@ class GradientBlocks(CommandsExtension): 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, ['fill_end'], pos_current) - add_commands(previous, ['fill_start'], pos_previous) + 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 diff --git a/lib/extensions/redwork.py b/lib/extensions/redwork.py index 7bc237e9..9ad2aca5 100644 --- a/lib/extensions/redwork.py +++ b/lib/extensions/redwork.py @@ -47,10 +47,10 @@ class Redwork(InkstitchExtension): self.merge_distance = self.options.merge_distance * PIXELS_PER_MM self.minimum_path_length = self.options.minimum_path_length * PIXELS_PER_MM - starting_point = self._get_starting_point('run_start') + starting_point = self._get_starting_point('autoroute_start') # as the resulting path starts and ends at same place we can also use ending point if not starting_point: - starting_point = self._get_starting_point('run_end') + starting_point = self._get_starting_point('autoroute_end') multi_line_string = self._elements_to_multi_line_string(elements) if starting_point: diff --git a/lib/svg/path.py b/lib/svg/path.py index 9d92058b..548a82f2 100644 --- a/lib/svg/path.py +++ b/lib/svg/path.py @@ -87,6 +87,9 @@ def line_strings_to_csp(line_strings): except AttributeError: pass + if line_strings is None: + return None + return point_lists_to_csp(ls.coords for ls in line_strings) diff --git a/lib/svg/tags.py b/lib/svg/tags.py index b8f04ef2..5afb2064 100644 --- a/lib/svg/tags.py +++ b/lib/svg/tags.py @@ -167,6 +167,8 @@ inkstitch_attribs = [ 'random_split_phase', 'random_split_jitter_percent', 'min_random_split_length_mm', + 'running_stitch_position', + 'start_at_nearest_point', # stitch_plan 'invisible_layers', 'layer_visibility', diff --git a/lib/tartan/svg.py b/lib/tartan/svg.py index 0fd8b579..15646629 100644 --- a/lib/tartan/svg.py +++ b/lib/tartan/svg.py @@ -166,11 +166,11 @@ class TartanSvgGroup: end = element.get('inkstitch:end') if start: start = start[1:-1].split(',') - add_commands(fill, ['fill_start'], self._get_command_position(fill, (float(start[0]), float(start[1])))) + 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, ['fill_end'], self._get_command_position(fill, (float(end[0]), float(end[1])))) + 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: diff --git a/lib/update.py b/lib/update.py index 5b092a8f..05d84272 100644 --- a/lib/update.py +++ b/lib/update.py @@ -5,6 +5,7 @@ from inkex import errormsg +from .commands import ensure_symbol from .elements import EmbroideryElement from .gui.request_update_svg_version import RequestUpdate from .i18n import _ @@ -12,7 +13,7 @@ from .metadata import InkStitchMetadata from .svg import PIXELS_PER_MM from .svg.tags import EMBROIDERABLE_TAGS, INKSTITCH_ATTRIBS -INKSTITCH_SVG_VERSION = 2 +INKSTITCH_SVG_VERSION = 3 def update_inkstitch_document(svg, selection=None): @@ -68,6 +69,8 @@ def automatic_version_update(document, file_version, INKSTITCH_SVG_VERSION): 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) def _update_inkstitch_svg_version(svg): @@ -173,3 +176,40 @@ def _replace_legacy_embroider_param(element, param): value = element.node.get(param, "").strip() element.set_param(param[10:], value) del element.node.attrib[param] + + +''' +Update legacy commands +====================== +''' + + +def update_legacy_commands(document): + ''' + Changes for svg version 3 + ''' + search_string = "//svg:symbol" + symbols = document.xpath(search_string) + for symbol in symbols: + _rename_command(document, symbol, 'inkstitch_fill_start', 'starting_point') + _rename_command(document, symbol, 'inkstitch_fill_end', 'ending_point') + _rename_command(document, symbol, 'inkstitch_satin_start', 'autoroute_start') + _rename_command(document, symbol, 'inkstitch_satin_end', 'autoroute_end') + _rename_command(document, symbol, 'inkstitch_run_start', 'autoroute_start') + _rename_command(document, symbol, 'inkstitch_run_end', 'autoroute_end') + _rename_command(document, symbol, 'inkstitch_ripple_target', 'target_point') + + +def _rename_command(document, symbol, old_name, new_name): + symbol_id = symbol.get_id() + if symbol_id.startswith(old_name): + symbol.getparent().remove(symbol) + ensure_symbol(document, new_name) + _update_command(document, symbol_id, new_name) + + +def _update_command(document, old_id, new_name): + xpath2 = f"//svg:use[@xlink:href='#{old_id}']" + elements = document.xpath(xpath2) + for element in elements: + element.set('xlink:href', f'#inkstitch_{new_name}') |
