Merge pull request #293 from inkstitch/lexelby/origin-and-stop-position

origin and stop position
pull/296/head
Lex Neva 2018-08-24 21:05:59 -04:00 zatwierdzone przez GitHub
commit 6f425f7b3e
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
11 zmienionych plików z 296 dodań i 95 usunięć

Wyświetl plik

@ -1,33 +1,42 @@
import sys
import inkex
import cubicsuperpath
import simpletransform
from .svg import apply_transforms
from .svg import apply_transforms, get_node_transform
from .svg.tags import SVG_USE_TAG, SVG_SYMBOL_TAG, CONNECTION_START, CONNECTION_END, XLINK_HREF
from .utils import cache
from .utils import cache, Point
from .i18n import _, N_
COMMANDS = {
# L10N command attached to an object
"fill_start": N_("Fill stitch starting position"),
N_("fill_start"): N_("Fill stitch starting position"),
# L10N command attached to an object
"fill_end": N_("Fill stitch ending position"),
N_("fill_end"): N_("Fill stitch ending position"),
# L10N command attached to an object
"stop": N_("Stop (pause machine) after sewing this object"),
N_("stop"): N_("Stop (pause machine) after sewing this object"),
# L10N command attached to an object
"trim": N_("Trim thread after sewing this object"),
N_("trim"): N_("Trim thread after sewing this object"),
# L10N command attached to an object
"ignore_object": N_("Ignore this object (do not stitch)"),
N_("ignore_object"): N_("Ignore this object (do not stitch)"),
# L10N command that affects entire layer
"ignore_layer": N_("Ignore layer (do not stitch any objects in this layer)")
# L10N command that affects a layer
N_("ignore_layer"): N_("Ignore layer (do not stitch any objects in this layer)"),
# L10N command that affects entire document
N_("origin"): N_("Origin for exported embroidery files"),
# L10N command that affects entire document
N_("stop_position"): N_("Jump destination for Stop commands (a.k.a. \"Frame Out position\")."),
}
OBJECT_COMMANDS = ["fill_start", "fill_end", "stop", "trim", "ignore_object"]
LAYER_COMMANDS = ["ignore_layer"]
GLOBAL_COMMANDS = ["origin", "stop_position"]
class CommandParseError(Exception):
@ -117,9 +126,18 @@ class StandaloneCommand(BaseCommand):
self.parse_symbol()
@property
@cache
def point(self):
pos = [float(self.node.get("x", 0)), float(self.node.get("y", 0))]
transform = get_node_transform(self.node)
simpletransform.applyTransformToPoint(transform, pos)
return Point(*pos)
def get_command_description(command):
return _(COMMANDS[command])
return COMMANDS[command]
def find_commands(node):
@ -144,31 +162,57 @@ def find_commands(node):
def layer_commands(layer, command):
"""Find standalone (unconnected) command symbols in this layer."""
commands = []
for global_command in global_commands(layer.getroottree().getroot(), command):
if layer in global_command.node.iterancestors():
yield global_command
for standalone_command in standalone_commands(layer.getroottree().getroot()):
def global_commands(svg, command):
"""Find standalone (unconnected) command symbols anywhere in the document."""
for standalone_command in _standalone_commands(svg):
if standalone_command.command == command:
if layer in standalone_command.node.iterancestors():
commands.append(command)
return commands
yield standalone_command
def standalone_commands(svg):
@cache
def global_command(svg, command):
"""Find a single command of the specified type.
If more than one is found, print an error and exit.
"""
commands = list(global_commands(svg, command))
if len(commands) == 1:
return commands[0]
elif len(commands) > 1:
print >> sys.stderr, _("Error: there is more than one %(command)s command in the document, but there can only be one. "
"Please remove all but one.") % dict(command=command)
# L10N This is a continuation of the previous error message, letting the user know
# what command we're talking about since we don't normally expose the actual
# command name to them. Contents of %(description)s are in a separate translation
# string.
print >> sys.stderr, _("%(command)s: %(description)s") % dict(command=command, description=_(get_command_description(command)))
sys.exit(1)
else:
return None
def _standalone_commands(svg):
"""Find all unconnected command symbols in the SVG."""
xpath = ".//svg:use[starts-with(@xlink:href, '#inkstitch_')]"
symbols = svg.xpath(xpath, namespaces=inkex.NSS)
commands = []
for symbol in symbols:
try:
commands.append(StandaloneCommand(symbol))
yield StandaloneCommand(symbol)
except CommandParseError:
pass
return commands
def is_command(node):
return CONNECTION_START in node.attrib or CONNECTION_END in node.attrib

Wyświetl plik

@ -9,6 +9,7 @@ from zip import Zip
from flip import Flip
from object_commands import ObjectCommands
from layer_commands import LayerCommands
from global_commands import GlobalCommands
from convert_to_satin import ConvertToSatin
__all__ = extensions = [Embroider,
@ -22,4 +23,5 @@ __all__ = extensions = [Embroider,
Flip,
ObjectCommands,
LayerCommands,
GlobalCommands,
ConvertToSatin]

Wyświetl plik

@ -123,7 +123,7 @@ class InkstitchExtension(inkex.Effect):
return []
if node.tag == SVG_GROUP_TAG and node.get(INKSCAPE_GROUPMODE) == "layer":
if layer_commands(node, "ignore_layer"):
if len(list(layer_commands(node, "ignore_layer"))):
return []
if element.has_style('display') and element.get_style('display') is None:

Wyświetl plik

@ -0,0 +1,12 @@
from .layer_commands import LayerCommands
from ..commands import GLOBAL_COMMANDS
# It's a bit weird subclassing this from LayerCommands, but global commands
# must still be placed in a layer. That means the two extensions
# do the same thing and the code is the same. We keep this as separate
# extensions because we want the user to understand that global commands
# affect the entire document, not just the current layer.
class GlobalCommands(LayerCommands):
COMMANDS = GLOBAL_COMMANDS

Wyświetl plik

@ -3,7 +3,7 @@ import pyembroidery
from .utils import build_environment, write_inx_file
from .outputs import pyembroidery_output_formats
from ..extensions import extensions, Input, Output
from ..commands import LAYER_COMMANDS, OBJECT_COMMANDS, COMMANDS
from ..commands import LAYER_COMMANDS, OBJECT_COMMANDS, GLOBAL_COMMANDS, COMMANDS
def layer_commands():
@ -13,6 +13,10 @@ def layer_commands():
return [(command, COMMANDS[command]) for command in LAYER_COMMANDS]
def global_commands():
return [(command, COMMANDS[command]) for command in GLOBAL_COMMANDS]
def object_commands():
return [(command, COMMANDS[command]) for command in OBJECT_COMMANDS]
@ -35,4 +39,5 @@ def generate_extension_inx_files():
write_inx_file(name, template.render(formats=pyembroidery_output_formats(),
debug_formats=pyembroidery_debug_formats(),
layer_commands=layer_commands(),
object_commands=object_commands()))
object_commands=object_commands(),
global_commands=global_commands()))

Wyświetl plik

@ -1,10 +1,9 @@
import pyembroidery
import inkex
import simpletransform
import shapely.geometry as shgeo
from .utils import Point
from .svg import PIXELS_PER_MM, get_doc_size, get_viewbox_transform
from .commands import global_command
def get_command(stitch):
@ -26,60 +25,32 @@ def _string_to_floats(string):
def get_origin(svg):
# The user can specify the embroidery origin by defining two guides
# named "embroidery origin" that intersect.
origin_command = global_command(svg, "origin")
namedview = svg.find(inkex.addNS('namedview', 'sodipodi'))
all_guides = namedview.findall(inkex.addNS('guide', 'sodipodi'))
label_attribute = inkex.addNS('label', 'inkscape')
guides = [guide for guide in all_guides
if guide.get(label_attribute, "").startswith("embroidery origin")]
# document size used below
doc_size = list(get_doc_size(svg))
# convert the size from viewbox-relative to real-world pixels
viewbox_transform = get_viewbox_transform(svg)
simpletransform.applyTransformToPoint(simpletransform.invertTransform(viewbox_transform), doc_size)
default = [doc_size[0] / 2.0, doc_size[1] / 2.0]
simpletransform.applyTransformToPoint(viewbox_transform, default)
default = Point(*default)
if len(guides) < 2:
return default
# Find out where the guides intersect. Only pay attention to the first two.
guides = guides[:2]
lines = []
for guide in guides:
# inkscape's Y axis is reversed from SVG's, and the guide is in inkscape coordinates
position = Point(*_string_to_floats(guide.get('position')))
position.y = doc_size[1] - position.y
# This one baffles me. I think inkscape might have gotten the order of
# their vector wrong?
parts = _string_to_floats(guide.get('orientation'))
direction = Point(parts[1], parts[0])
# We have a theoretically infinite line defined by a point on the line
# and a vector direction. Shapely can only deal in concrete line
# segments, so we'll pick points really far in either direction on the
# line and call it good enough.
lines.append(shgeo.LineString((position + 100000 * direction, position - 100000 * direction)))
intersection = lines[0].intersection(lines[1])
if isinstance(intersection, shgeo.Point):
origin = [intersection.x, intersection.y]
simpletransform.applyTransformToPoint(viewbox_transform, origin)
return Point(*origin)
if origin_command:
return origin_command.point
else:
# Either the two guides are the same line, or they're parallel.
# default: center of the canvas
doc_size = list(get_doc_size(svg))
# convert the size from viewbox-relative to real-world pixels
viewbox_transform = get_viewbox_transform(svg)
simpletransform.applyTransformToPoint(simpletransform.invertTransform(viewbox_transform), doc_size)
default = [doc_size[0] / 2.0, doc_size[1] / 2.0]
simpletransform.applyTransformToPoint(viewbox_transform, default)
default = Point(*default)
return default
def jump_to_stop_point(pattern, svg):
stop_position = global_command(svg, "stop_position")
if stop_position:
pattern.add_stitch_absolute(pyembroidery.JUMP, stop_position.point.x, stop_position.point.y)
def write_embroidery_file(file_path, stitch_plan, svg):
origin = get_origin(svg)
@ -89,6 +60,8 @@ def write_embroidery_file(file_path, stitch_plan, svg):
pattern.add_thread(color_block.color.pyembroidery_thread)
for stitch in color_block:
if stitch.stop:
jump_to_stop_point(pattern, svg)
command = get_command(stitch)
pattern.add_stitch_absolute(command, stitch.x, stitch.y)

Wyświetl plik

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2018-08-24 20:45-0400\n"
"POT-Creation-Date: 2018-08-24 20:56-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,35 +18,94 @@ msgstr ""
"Generated-By: Babel 2.5.3\n"
#. command attached to an object
#: lib/commands.py:11
#: lib/commands.py:13
msgid "fill_start"
msgstr ""
#: lib/commands.py:13
msgid "Fill stitch starting position"
msgstr ""
#. command attached to an object
#: lib/commands.py:14
#: lib/commands.py:16
msgid "fill_end"
msgstr ""
#: lib/commands.py:16
msgid "Fill stitch ending position"
msgstr ""
#. command attached to an object
#: lib/commands.py:17
#: lib/commands.py:19
msgid "stop"
msgstr ""
#: lib/commands.py:19
msgid "Stop (pause machine) after sewing this object"
msgstr ""
#. command attached to an object
#: lib/commands.py:20
#: lib/commands.py:22
msgid "trim"
msgstr ""
#: lib/commands.py:22
msgid "Trim thread after sewing this object"
msgstr ""
#. command attached to an object
#: lib/commands.py:23
#: lib/commands.py:25
msgid "ignore_object"
msgstr ""
#: lib/commands.py:25
msgid "Ignore this object (do not stitch)"
msgstr ""
#. command that affects entire layer
#: lib/commands.py:26
#. command that affects a layer
#: lib/commands.py:28
msgid "ignore_layer"
msgstr ""
#: lib/commands.py:28
msgid "Ignore layer (do not stitch any objects in this layer)"
msgstr ""
#. command that affects entire document
#: lib/commands.py:31
msgid "origin"
msgstr ""
#: lib/commands.py:31
msgid "Origin for exported embroidery files"
msgstr ""
#. command that affects entire document
#: lib/commands.py:34
msgid "stop_position"
msgstr ""
#: lib/commands.py:34
msgid "Jump destination for Stop commands (a.k.a. \"Frame Out position\")."
msgstr ""
#: lib/commands.py:190
#, python-format
msgid ""
"Error: there is more than one %(command)s command in the document, but "
"there can only be one. Please remove all but one."
msgstr ""
#. This is a continuation of the previous error message, letting the user know
#. what command we're talking about since we don't normally expose the actual
#. command name to them. Contents of %(description)s are in a separate
#. translation
#. string.
#: lib/commands.py:197
#, python-format
msgid "%(command)s: %(description)s"
msgstr ""
#: lib/elements/auto_fill.py:11
msgid "Auto-Fill"
msgstr ""
@ -1117,9 +1176,10 @@ msgstr ""
#. to your language's word for its language, e.g. "Español" for the spanish
#. translation.
#: templates/convert_to_satin.inx:12 templates/embroider.inx:24
#: templates/flip.inx:12 templates/install.inx:12
#: templates/layer_commands.inx:16 templates/object_commands.inx:15
#: templates/params.inx:12 templates/print.inx:12 templates/simulate.inx:12
#: templates/flip.inx:12 templates/global_commands.inx:16
#: templates/install.inx:12 templates/layer_commands.inx:16
#: templates/object_commands.inx:15 templates/params.inx:12
#: templates/print.inx:12 templates/simulate.inx:12
msgid "English"
msgstr ""
@ -1165,6 +1225,20 @@ msgstr ""
msgid "Flip Satin Columns"
msgstr ""
#: templates/global_commands.inx:3
msgid "Add Commands"
msgstr ""
#: templates/global_commands.inx:7
msgid "These commands affect the entire embroidery design."
msgstr ""
#. Inkscape submenu under Extensions -> Ink/Stitch
#: templates/global_commands.inx:18 templates/layer_commands.inx:17
#: templates/object_commands.inx:16
msgid "Commands"
msgstr ""
#: templates/input.inx:11
#, python-format
msgid "convert %(file_extension)s file to Ink/Stitch manual-stitch paths"
@ -1183,7 +1257,7 @@ msgid "Commands will be added to the currently-selected layer."
msgstr ""
#: templates/object_commands.inx:3
msgid "Attach Commands"
msgid "Attach Commands to Selected Objects"
msgstr ""
#: templates/output.inx:11

File diff suppressed because one or more lines are too long

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 19 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 34 KiB

Wyświetl plik

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>{% trans %}Add Commands{% endtrans %}</name>
<id>org.inkstitch.global_commands.{{ locale }}</id>
<dependency type="executable" location="extensions">inkstitch.py</dependency>
<dependency type="executable" location="extensions">inkex.py</dependency>
<param name="description" type="description">{% trans %}These commands affect the entire embroidery design.{% endtrans %}</param>
{% for command, description in global_commands %}
<param name="{{ command }}" type="boolean" _gui-text="{{ _(description) }}">false</param>
{% endfor %}
<param name="extension" type="string" gui-hidden="true">global_commands</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="Ink/Stitch">
<submenu name="{% trans %}English{% endtrans %}">
{# L10N Inkscape submenu under Extensions -> Ink/Stitch #}
<submenu name="{% trans %}Commands{% endtrans %}" />
</submenu>
</submenu>
</effects-menu>
</effect>
<script>
<command reldir="extensions" interpreter="python">inkstitch.py</command>
</script>
</inkscape-extension>

Wyświetl plik

@ -13,7 +13,9 @@
<object-type>all</object-type>
<effects-menu>
<submenu name="Ink/Stitch">
<submenu name="{% trans %}English{% endtrans %}" />
<submenu name="{% trans %}English{% endtrans %}">
<submenu name="{% trans %}Commands{% endtrans %}" />
</submenu>
</submenu>
</effects-menu>
</effect>

Wyświetl plik

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>{% trans %}Attach Commands{% endtrans %}</name>
<name>{% trans %}Attach Commands to Selected Objects{% endtrans %}</name>
<id>org.inkstitch.commands.{{ locale }}</id>
<dependency type="executable" location="extensions">inkstitch.py</dependency>
<dependency type="executable" location="extensions">inkex.py</dependency>
@ -12,7 +12,9 @@
<object-type>all</object-type>
<effects-menu>
<submenu name="Ink/Stitch">
<submenu name="{% trans %}English{% endtrans %}" />
<submenu name="{% trans %}English{% endtrans %}">
<submenu name="{% trans %}Commands{% endtrans %}" />
</submenu>
</submenu>
</effects-menu>
</effect>