kopia lustrzana https://github.com/inkstitch/inkstitch
113 wiersze
5.0 KiB
Python
113 wiersze
5.0 KiB
Python
# Authors: see git history
|
|
#
|
|
# Copyright (c) 2021 Authors
|
|
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
|
|
|
|
import inkex
|
|
from shapely.geometry import Point
|
|
|
|
from ..elements.utils import iterate_nodes, nodes_to_elements
|
|
from ..i18n import _
|
|
from ..marker import has_marker
|
|
from ..svg import PIXELS_PER_MM
|
|
from ..svg.tags import EMBROIDERABLE_TAGS
|
|
from .base import InkstitchExtension
|
|
|
|
|
|
class LetteringForceLockStitches(InkstitchExtension):
|
|
'''
|
|
This extension helps font creators to add the force lock stitches attribute to the last objects of each glyph
|
|
Font creators to add forced lock stitches on glyphs with accents / spaces.
|
|
'''
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
InkstitchExtension.__init__(self, *args, **kwargs)
|
|
self.arg_parser.add_argument("--notebook")
|
|
self.arg_parser.add_argument("-s", "--satin_only", type=inkex.Boolean, dest="satin_only")
|
|
self.arg_parser.add_argument("-d", "--distance", type=inkex.Boolean, dest="distance")
|
|
self.arg_parser.add_argument("-a", "--max_distance", type=float, default=3, dest="max_distance")
|
|
self.arg_parser.add_argument("-i", "--min_distance", type=float, default=1, dest="min_distance")
|
|
self.arg_parser.add_argument("-l", "--last_element", type=inkex.Boolean, dest="last_element")
|
|
|
|
def effect(self):
|
|
if self.options.max_distance < self.options.min_distance:
|
|
inkex.errormssg(_("The maximum value is smaller than the minimum value."))
|
|
|
|
glyph_layers = self.document.xpath('.//svg:g[starts-with(@inkscape:label, "GlyphLayer")]', namespaces=inkex.NSS)
|
|
uses_glyph_layers = True
|
|
if not glyph_layers:
|
|
# they maybe want to use this method for a regular document. Let's allow it, why not.
|
|
glyph_layers = [self.document.getroot()]
|
|
uses_glyph_layers = False
|
|
else:
|
|
# Set glyph layers to be visible. We don't want them to be ignored by self.elements
|
|
self._update_layer_visibility('inline')
|
|
for layer in glyph_layers:
|
|
if uses_glyph_layers and self.options.last_element:
|
|
self._set_force_attribute_on_last_elements(layer)
|
|
if self.options.distance:
|
|
self._set_force_attribute_by_distance(layer)
|
|
|
|
if uses_glyph_layers:
|
|
# unhide glyph layers
|
|
self._update_layer_visibility('none')
|
|
|
|
def _set_force_attribute_on_last_elements(self, layer):
|
|
# find the last path that does not carry a marker or belongs to a visual command and add a trim there
|
|
last_element = None
|
|
child_nodes = list(layer.iterdescendants(EMBROIDERABLE_TAGS))
|
|
child_nodes.reverse()
|
|
for element in child_nodes:
|
|
if not has_marker(element) and not element.get_id().startswith('command_connector'):
|
|
last_element = element
|
|
break
|
|
if last_element is not None:
|
|
if self.options.satin_only and not last_element.get('inkstitch:satin_column', False):
|
|
return
|
|
last_element.set('inkstitch:force_lock_stitches', True)
|
|
|
|
def _set_force_attribute_by_distance(self, layer):
|
|
min_distance = self.options.min_distance * PIXELS_PER_MM
|
|
max_distance = self.options.max_distance * PIXELS_PER_MM
|
|
|
|
nodes = iterate_nodes(layer)
|
|
elements = nodes_to_elements(nodes)
|
|
|
|
last_stitch_group = None
|
|
next_elements = [None]
|
|
if len(elements) > 1:
|
|
next_elements = elements[1:] + next_elements
|
|
for element, next_element in zip(elements, next_elements):
|
|
distance = None
|
|
stitch_groups = element.to_stitch_groups(last_stitch_group, next_element)
|
|
if not stitch_groups:
|
|
continue
|
|
if next_element is not None:
|
|
last_stitch = stitch_groups[-1].stitches[-1]
|
|
next_stitch = next_element.first_stitch
|
|
if stitch_groups[-1].color != next_element.color:
|
|
last_stitch_group = stitch_groups[-1]
|
|
continue
|
|
if next_stitch is None:
|
|
# get nearest point
|
|
shape = next_element.shape
|
|
if not shape.is_empty:
|
|
distance = shape.distance(Point(last_stitch))
|
|
else:
|
|
distance = Point(last_stitch).distance(Point(next_stitch))
|
|
|
|
if distance is not None and distance < max_distance and distance > min_distance:
|
|
if self.options.satin_only and element.name != 'SatinColumn':
|
|
continue
|
|
element.node.set('inkstitch:force_lock_stitches', True)
|
|
|
|
last_stitch_group = stitch_groups[-1]
|
|
|
|
def _update_layer_visibility(self, display):
|
|
xpath = ".//svg:g[@inkscape:groupmode='layer']"
|
|
layers = self.document.xpath(xpath, namespaces=inkex.NSS)
|
|
for layer in layers:
|
|
display_style = 'display:%s' % display
|
|
style = inkex.Style(layer.get('style', '')) + inkex.Style(display_style)
|
|
layer.set('style', style)
|