diff options
Diffstat (limited to 'lib/extensions')
| -rw-r--r-- | lib/extensions/__init__.py | 4 | ||||
| -rw-r--r-- | lib/extensions/auto_satin.py | 108 | ||||
| -rw-r--r-- | lib/extensions/base.py | 29 | ||||
| -rw-r--r-- | lib/extensions/commands.py | 91 | ||||
| -rw-r--r-- | lib/extensions/cut_satin.py | 3 | ||||
| -rw-r--r-- | lib/extensions/layer_commands.py | 16 | ||||
| -rw-r--r-- | lib/extensions/object_commands.py | 86 |
7 files changed, 234 insertions, 103 deletions
diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py index 56cd774b..f70c0135 100644 --- a/lib/extensions/__init__.py +++ b/lib/extensions/__init__.py @@ -12,6 +12,7 @@ from layer_commands import LayerCommands from global_commands import GlobalCommands from convert_to_satin import ConvertToSatin from cut_satin import CutSatin +from auto_satin import AutoSatin __all__ = extensions = [Embroider, Install, @@ -26,4 +27,5 @@ __all__ = extensions = [Embroider, LayerCommands, GlobalCommands, ConvertToSatin, - CutSatin] + CutSatin, + AutoSatin] diff --git a/lib/extensions/auto_satin.py b/lib/extensions/auto_satin.py new file mode 100644 index 00000000..e5e9c40b --- /dev/null +++ b/lib/extensions/auto_satin.py @@ -0,0 +1,108 @@ +import sys + +import inkex + +from ..elements import SatinColumn +from ..i18n import _ +from ..stitches.auto_satin import auto_satin +from ..svg import get_correction_transform +from ..svg.tags import SVG_GROUP_TAG, INKSCAPE_LABEL +from .commands import CommandsExtension + + +class AutoSatin(CommandsExtension): + COMMANDS = ["trim"] + + def __init__(self, *args, **kwargs): + CommandsExtension.__init__(self, *args, **kwargs) + + self.OptionParser.add_option("-p", "--preserve_order", dest="preserve_order", type="inkbool", default=False) + + def get_starting_point(self): + return self.get_point("satin_start") + + def get_ending_point(self): + return self.get_point("satin_end") + + def get_point(self, command_type): + command = None + for satin in self.elements: + this_command = satin.get_command(command_type) + if command is not None and this_command: + inkex.errormsg(_("Please ensure that at most one start and end command is attached to the selected satin columns.")) + sys.exit(0) + elif this_command: + command = this_command + + if command is not None: + return command.target_point + + def effect(self): + if not self.check_selection(): + return + + group = self.create_group() + new_elements, trim_indices = self.auto_satin() + + # The ordering is careful here. Some of the original satins may have + # been used unmodified. That's why we remove all of the original + # satins _first_ before adding new_elements back into the SVG. + self.remove_original_satins() + self.add_elements(group, new_elements) + + self.add_trims(new_elements, trim_indices) + + def check_selection(self): + if not self.get_elements(): + return + + if not self.selected: + # L10N auto-route satin columns extension + inkex.errormsg(_("Please select one or more satin columns.")) + return False + + return True + + def create_group(self): + first = self.elements[0].node + parent = first.getparent() + insert_index = parent.index(first) + group = inkex.etree.Element(SVG_GROUP_TAG, { + "transform": get_correction_transform(parent, child=True) + }) + parent.insert(insert_index, group) + + return group + + def auto_satin(self): + starting_point = self.get_starting_point() + ending_point = self.get_ending_point() + return auto_satin(self.elements, self.options.preserve_order, starting_point, ending_point) + + def remove_original_satins(self): + for element in self.elements: + for command in element.commands: + command.connector.getparent().remove(command.connector) + command.use.getparent().remove(command.use) + element.node.getparent().remove(element.node) + + def add_elements(self, group, new_elements): + for i, element in enumerate(new_elements): + if isinstance(element, SatinColumn): + element.node.set("id", self.uniqueId("autosatin")) + + # L10N Label for a satin column created by Auto-Route Satin Columns extension + element.node.set(INKSCAPE_LABEL, _("AutoSatin %d") % (i + 1)) + else: + element.node.set("id", self.uniqueId("autosatinrun")) + + # L10N Label for running stitch (underpathing) created by Auto-Route Satin Columns extension + element.node.set(INKSCAPE_LABEL, _("AutoSatin Running Stitch %d") % (i + 1)) + + group.append(element.node) + + def add_trims(self, new_elements, trim_indices): + if self.options.trim and trim_indices: + self.ensure_symbol("trim") + for i in trim_indices: + self.add_commands(new_elements[i], ["trim"]) diff --git a/lib/extensions/base.py b/lib/extensions/base.py index 25de441f..b9bba617 100644 --- a/lib/extensions/base.py +++ b/lib/extensions/base.py @@ -1,14 +1,15 @@ -import inkex -import re -import json -from copy import deepcopy from collections import MutableMapping +from copy import deepcopy +import json +import re + +import inkex from stringcase import snakecase -from ..svg.tags import SVG_GROUP_TAG, INKSCAPE_GROUPMODE, SVG_DEFS_TAG, EMBROIDERABLE_TAGS, SVG_POLYLINE_TAG -from ..elements import AutoFill, Fill, Stroke, SatinColumn, Polyline, EmbroideryElement from ..commands import is_command, layer_commands +from ..elements import AutoFill, Fill, Stroke, SatinColumn, Polyline, EmbroideryElement from ..i18n import _ +from ..svg.tags import SVG_GROUP_TAG, INKSCAPE_GROUPMODE, SVG_DEFS_TAG, EMBROIDERABLE_TAGS, SVG_POLYLINE_TAG SVG_METADATA_TAG = inkex.addNS("metadata", "svg") @@ -21,7 +22,7 @@ def strip_namespace(tag): <<< namedview """ - match = re.match('^\{[^}]+\}(.+)$', tag) + match = re.match(r'^\{[^}]+\}(.+)$', tag) if match: return match.group(1) @@ -211,8 +212,20 @@ class InkstitchExtension(inkex.Effect): return svg_filename + def uniqueId(self, prefix, make_new_id=True): + """Override inkex.Effect.uniqueId with a nicer naming scheme.""" + i = 1 + while True: + new_id = "%s%d" % (prefix, i) + if new_id not in self.doc_ids: + break + i += 1 + self.doc_ids[new_id] = 1 + + return new_id + def parse(self): - """Override inkex.Effect to add Ink/Stitch xml namespace""" + """Override inkex.Effect.parse to add Ink/Stitch xml namespace""" # SVG parsers don't actually look for anything at this URL. They just # care that it's unique. That defines a "namespace" of element and diff --git a/lib/extensions/commands.py b/lib/extensions/commands.py index fb6f7874..07b450e1 100644 --- a/lib/extensions/commands.py +++ b/lib/extensions/commands.py @@ -1,13 +1,21 @@ import os import inkex from copy import deepcopy +from random import random + from .base import InkstitchExtension from ..utils import get_bundled_dir, cache -from ..svg.tags import SVG_DEFS_TAG +from ..commands import get_command_description +from ..i18n import _ +from ..svg.tags import SVG_DEFS_TAG, SVG_PATH_TAG, CONNECTION_START, CONNECTION_END, \ + CONNECTOR_TYPE, INKSCAPE_LABEL, SVG_GROUP_TAG, SVG_USE_TAG, XLINK_HREF +from ..svg import get_correction_transform class CommandsExtension(InkstitchExtension): + """Base class for extensions that manipulate commands.""" + def __init__(self, *args, **kwargs): InkstitchExtension.__init__(self, *args, **kwargs) for command in self.COMMANDS: @@ -37,3 +45,84 @@ class CommandsExtension(InkstitchExtension): path = "./*[@id='inkstitch_%s']" % command if self.defs.find(path) is None: self.defs.append(deepcopy(self.symbol_defs.find(path))) + + def add_connector(self, symbol, element): + # I'd like it if I could position the connector endpoint nicely but inkscape just + # moves it to the element's center immediately after the extension runs. + start_pos = (symbol.get('x'), symbol.get('y')) + end_pos = element.shape.centroid + + path = inkex.etree.Element(SVG_PATH_TAG, + { + "id": self.uniqueId("connector"), + "d": "M %s,%s %s,%s" % (start_pos[0], start_pos[1], end_pos.x, end_pos.y), + "style": "stroke:#000000;stroke-width:1px;stroke-opacity:0.5;fill:none;", + CONNECTION_START: "#%s" % symbol.get('id'), + CONNECTION_END: "#%s" % element.node.get('id'), + CONNECTOR_TYPE: "polyline", + + # l10n: the name of the line that connects a command to the object it applies to + INKSCAPE_LABEL: _("connector") + } + ) + + symbol.getparent().insert(0, path) + + def get_command_pos(self, 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 + outline = element.shape.buffer(30).exterior + + # pick this item's spot arond the outline and perturb it a bit to avoid + # stacking up commands if they run the extension multiple times + position = index / float(total) + position += random() * 0.1 + + return outline.interpolate(position, normalized=True) + + def remove_legacy_param(self, 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] + + def add_commands(self, element, commands): + for i, command in enumerate(commands): + self.remove_legacy_param(element, command) + + pos = self.get_command_pos(element, i, len(commands)) + + group = inkex.etree.SubElement(element.node.getparent(), SVG_GROUP_TAG, + { + "id": self.uniqueId("group"), + INKSCAPE_LABEL: _("Ink/Stitch Command") + ": %s" % get_command_description(command), + "transform": get_correction_transform(element.node) + } + ) + + symbol = inkex.etree.SubElement(group, SVG_USE_TAG, + { + "id": self.uniqueId("use"), + XLINK_HREF: "#inkstitch_%s" % command, + "height": "100%", + "width": "100%", + "x": str(pos.x), + "y": str(pos.y), + + # l10n: the name of a command symbol (example: scissors icon for trim command) + INKSCAPE_LABEL: _("command marker"), + } + ) + + self.add_connector(symbol, element) diff --git a/lib/extensions/cut_satin.py b/lib/extensions/cut_satin.py index 0bef794e..b776a68c 100644 --- a/lib/extensions/cut_satin.py +++ b/lib/extensions/cut_satin.py @@ -3,6 +3,7 @@ import inkex from .base import InkstitchExtension from ..i18n import _ from ..elements import SatinColumn +from ..svg import get_correction_transform class CutSatin(InkstitchExtension): @@ -29,9 +30,11 @@ class CutSatin(InkstitchExtension): command.connector.getparent().remove(command.connector) new_satins = satin.split(split_point) + transform = get_correction_transform(satin.node) parent = satin.node.getparent() index = parent.index(satin.node) parent.remove(satin.node) for new_satin in new_satins: + new_satin.node.set('transform', transform) parent.insert(index, new_satin.node) index += 1 diff --git a/lib/extensions/layer_commands.py b/lib/extensions/layer_commands.py index dbafc39f..60a5fab2 100644 --- a/lib/extensions/layer_commands.py +++ b/lib/extensions/layer_commands.py @@ -35,12 +35,12 @@ class LayerCommands(CommandsExtension): inkex.etree.SubElement(self.current_layer, SVG_USE_TAG, { - "id": self.uniqueId("use"), - INKSCAPE_LABEL: _("Ink/Stitch Command") + ": %s" % get_command_description(command), - XLINK_HREF: "#inkstitch_%s" % command, - "height": "100%", - "width": "100%", - "x": str(i * 20), - "y": "-10", - "transform": correction_transform + "id": self.uniqueId("use"), + INKSCAPE_LABEL: _("Ink/Stitch Command") + ": %s" % get_command_description(command), + XLINK_HREF: "#inkstitch_%s" % command, + "height": "100%", + "width": "100%", + "x": str(i * 20), + "y": "-10", + "transform": correction_transform }) diff --git a/lib/extensions/object_commands.py b/lib/extensions/object_commands.py index e678890d..47fb361d 100644 --- a/lib/extensions/object_commands.py +++ b/lib/extensions/object_commands.py @@ -1,97 +1,13 @@ import inkex -from random import random from .commands import CommandsExtension -from ..commands import OBJECT_COMMANDS, get_command_description +from ..commands import OBJECT_COMMANDS from ..i18n import _ -from ..svg.tags import SVG_PATH_TAG, CONNECTION_START, CONNECTION_END, CONNECTOR_TYPE, INKSCAPE_LABEL, SVG_GROUP_TAG, SVG_USE_TAG, XLINK_HREF -from ..svg import get_correction_transform class ObjectCommands(CommandsExtension): COMMANDS = OBJECT_COMMANDS - def add_connector(self, symbol, element): - # I'd like it if I could position the connector endpoint nicely but inkscape just - # moves it to the element's center immediately after the extension runs. - start_pos = (symbol.get('x'), symbol.get('y')) - end_pos = element.shape.centroid - - path = inkex.etree.Element(SVG_PATH_TAG, - { - "id": self.uniqueId("connector"), - "d": "M %s,%s %s,%s" % (start_pos[0], start_pos[1], end_pos.x, end_pos.y), - "style": "stroke:#000000;stroke-width:1px;stroke-opacity:0.5;fill:none;", - CONNECTION_START: "#%s" % symbol.get('id'), - CONNECTION_END: "#%s" % element.node.get('id'), - CONNECTOR_TYPE: "polyline", - - # l10n: the name of the line that connects a command to the object it applies to - INKSCAPE_LABEL: _("connector") - } - ) - - symbol.getparent().insert(0, path) - - def get_command_pos(self, 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 - outline = element.shape.buffer(30).exterior - - # pick this item's spot arond the outline and perturb it a bit to avoid - # stacking up commands if they run the extension multiple times - position = index / float(total) - position += random() * 0.1 - - return outline.interpolate(position, normalized=True) - - def remove_legacy_param(self, 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] - - def add_commands(self, element, commands): - for i, command in enumerate(commands): - self.remove_legacy_param(element, command) - - pos = self.get_command_pos(element, i, len(commands)) - - group = inkex.etree.SubElement(element.node.getparent(), SVG_GROUP_TAG, - { - "id": self.uniqueId("group"), - INKSCAPE_LABEL: _("Ink/Stitch Command") + ": %s" % get_command_description(command), - "transform": get_correction_transform(element.node) - } - ) - - symbol = inkex.etree.SubElement(group, SVG_USE_TAG, - { - "id": self.uniqueId("use"), - XLINK_HREF: "#inkstitch_%s" % command, - "height": "100%", - "width": "100%", - "x": str(pos.x), - "y": str(pos.y), - - # l10n: the name of a command symbol (example: scissors icon for trim command) - INKSCAPE_LABEL: _("command marker"), - } - ) - - self.add_connector(symbol, element) - def effect(self): if not self.get_elements(): return |
