kopia lustrzana https://github.com/inkstitch/inkstitch
Add transform extension which also transforms the fill angles (#3657)
rodzic
0f1c83f8ef
commit
bc0943c6c8
|
@ -311,7 +311,7 @@ class FillStitch(EmbroideryElement):
|
|||
type='float',
|
||||
sort_index=21,
|
||||
select_items=[('fill_method', 'tartan_fill')],
|
||||
default=45)
|
||||
default=-45)
|
||||
@cache
|
||||
def tartan_angle(self):
|
||||
return self.get_float_param('tartan_angle', -45)
|
||||
|
|
|
@ -72,6 +72,7 @@ from .stroke_to_satin import StrokeToSatin
|
|||
from .tartan import Tartan
|
||||
from .test_swatches import TestSwatches
|
||||
from .thread_list import ThreadList
|
||||
from .transform_elements import TransformElements
|
||||
from .troubleshoot import Troubleshoot
|
||||
from .unlink_clone import UnlinkClone
|
||||
from .update_svg import UpdateSvg
|
||||
|
@ -148,6 +149,7 @@ extensions = [
|
|||
Tartan,
|
||||
TestSwatches,
|
||||
ThreadList,
|
||||
TransformElements,
|
||||
Troubleshoot,
|
||||
UnlinkClone,
|
||||
UpdateSvg,
|
||||
|
|
|
@ -49,7 +49,7 @@ class JumpToStroke(InkstitchExtension):
|
|||
next_elements = self.elements[1:] + next_elements
|
||||
for element, next_element in zip(self.elements, next_elements):
|
||||
layer, group = self._get_element_layer_and_group(element)
|
||||
stitch_groups = element.to_stitch_groups(last_stitch_group, next_element)
|
||||
stitch_groups = element.embroider(last_stitch_group, next_element)
|
||||
multiple = not self.options.merge_subpaths and stitch_groups
|
||||
|
||||
if multiple:
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
# Authors: see git history
|
||||
#
|
||||
# Copyright (c) 2025 Authors
|
||||
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
|
||||
|
||||
import json
|
||||
from typing import Optional
|
||||
|
||||
from inkex import (Boolean, PathElement, ShapeElement, Transform, Vector2d,
|
||||
errormsg)
|
||||
|
||||
from ..i18n import _
|
||||
from ..tartan.utils import get_tartan_settings
|
||||
from .base import InkstitchExtension
|
||||
|
||||
|
||||
class TransformElements(InkstitchExtension):
|
||||
'''
|
||||
This will apply transformations while also transforming fill angles.
|
||||
'''
|
||||
def __init__(self, *args, **kwargs):
|
||||
InkstitchExtension.__init__(self, *args, **kwargs)
|
||||
self.arg_parser.add_argument("--notebook")
|
||||
self.arg_parser.add_argument("-r", "--rotate", type=float, default=0, dest="rotate")
|
||||
self.arg_parser.add_argument("-f", "--flip-horizontally", type=Boolean, default=False, dest="horizontal_flip")
|
||||
self.arg_parser.add_argument("-v", "--flip-vertically", type=Boolean, default=False, dest="vertical_flip")
|
||||
|
||||
def effect(self) -> None:
|
||||
if not self.svg.selection:
|
||||
errormsg(_("Please select one or more elements."))
|
||||
return
|
||||
selection_center = self.svg.selection.bounding_box().center
|
||||
|
||||
nodes = self.get_nodes()
|
||||
for node in nodes:
|
||||
parent_transform = node.composed_transform() @ -node.transform
|
||||
if self.options.rotate != 0:
|
||||
self.rotate_node(node, parent_transform, selection_center)
|
||||
if self.options.horizontal_flip:
|
||||
self.flip_node_horizontally(node, parent_transform, selection_center)
|
||||
if self.options.vertical_flip:
|
||||
self.flip_node_vertically(node, parent_transform, selection_center)
|
||||
|
||||
# Apply transform to path elements, simply because it's possible and nicer
|
||||
if isinstance(node, PathElement):
|
||||
node.apply_transform()
|
||||
|
||||
def flip_node_vertically(self, node: ShapeElement, parent_transform: Transform, center: Vector2d) -> None:
|
||||
node.transform = (
|
||||
-parent_transform @
|
||||
Transform(f'translate({center[0], center[1]}) scale(1, -1) translate({-center[0], -center[1]})') @
|
||||
node.composed_transform()
|
||||
)
|
||||
self.adapt_fill_angle(node, -1)
|
||||
|
||||
def flip_node_horizontally(self, node: ShapeElement, parent_transform: Transform, center: Vector2d) -> None:
|
||||
node.transform = (
|
||||
-parent_transform @
|
||||
Transform(f'translate({center[0], center[1]}) scale(-1, 1) translate({-center[0], -center[1]})') @
|
||||
node.composed_transform()
|
||||
)
|
||||
self.adapt_fill_angle(node, -1)
|
||||
|
||||
def rotate_node(self, node: ShapeElement, parent_transform: Transform, center: Vector2d) -> None:
|
||||
node.transform = (
|
||||
-parent_transform @
|
||||
Transform(f'rotate({self.options.rotate}, {center[0]}, {center[1]})') @
|
||||
node.composed_transform()
|
||||
)
|
||||
self.adapt_fill_angle(node, None, self.options.rotate)
|
||||
|
||||
def adapt_fill_angle(self, node: ShapeElement, multiplier: Optional[int] = None, rotation: Optional[float] = None) -> None:
|
||||
if not node.style("fill", "black"):
|
||||
return
|
||||
|
||||
self._apply_angle(node, "inkstitch:fill_underlay_angle", None, multiplier, rotation)
|
||||
if node.get('inkstitch:fill_method', None) == "tartan_fill":
|
||||
if rotation is None:
|
||||
self._apply_angle(node, "inkstitch:tartan_angle", "-45", multiplier, rotation)
|
||||
# Also rotate tartan pattern rotation setting
|
||||
self._rotate_tartan_pattern(node, multiplier, rotation)
|
||||
elif node.get('inkstitch:fill_method', None) == "meander_fill":
|
||||
self._apply_angle(node, "inkstitch:meander_angle", "0", multiplier, rotation)
|
||||
else:
|
||||
self._apply_angle(node, "inkstitch:angle", "0", multiplier, rotation)
|
||||
|
||||
def _apply_angle(self, node: ShapeElement, attrib: str, default: Optional[str], multiplier: Optional[int], rotation: Optional[float]) -> None:
|
||||
angle_string = node.get(attrib, default)
|
||||
if angle_string is None:
|
||||
return
|
||||
|
||||
try:
|
||||
angle = float(angle_string)
|
||||
except ValueError:
|
||||
return
|
||||
|
||||
if multiplier is not None:
|
||||
angle *= multiplier
|
||||
elif rotation is not None:
|
||||
angle -= rotation
|
||||
node.set(attrib, str(angle))
|
||||
|
||||
def _rotate_tartan_pattern(self, node: ShapeElement, multiplier: Optional[int], rotation: Optional[float]) -> None:
|
||||
settings = get_tartan_settings(node)
|
||||
tartan_rotation = settings['rotate']
|
||||
|
||||
if multiplier is not None:
|
||||
tartan_rotation *= multiplier
|
||||
elif rotation is not None:
|
||||
tartan_rotation += rotation
|
||||
settings['rotate'] = tartan_rotation
|
||||
node.set("inkstitch:tartan", json.dumps(settings))
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension translationdomain="inkstitch" xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Transform</name>
|
||||
<id>org.{{ id_inkstitch }}.transform_elements</id>
|
||||
<param name="extension" type="string" gui-hidden="true">transform_elements</param>
|
||||
|
||||
<param name="notebook" type="notebook">
|
||||
<page name="options" gui-text="Options">
|
||||
<param name="rotate" type="float" precision="2" min="-180" max="180" appearance="full"
|
||||
gui-text="Rotate">0</param>
|
||||
<param name="flip-horizontally" type="bool" gui-text="Flip horizontally">false</param>
|
||||
<param name="flip-vertically" type="bool" gui-text="Flip vertically">false</param>
|
||||
</page>
|
||||
<page name="info" gui-text="Help">
|
||||
<label>
|
||||
This extension applies transformations while also adjusting the fill angles
|
||||
</label>
|
||||
<spacer />
|
||||
<label>More information on our website</label>
|
||||
<label appearance="url">https://inkstitch.org/docs/edit/#transform</label>
|
||||
</page>
|
||||
</param>
|
||||
|
||||
<effect>
|
||||
<object-type>all</object-type>
|
||||
<icon>{{ icon_path }}inx/break_apart_fill.svg</icon>
|
||||
<menu-tip>Break apart and repair (broken) fill shapes</menu-tip>
|
||||
<effects-menu>
|
||||
<submenu name="{{ menu_inkstitch }}" translatable="no">
|
||||
<submenu name="Edit" />
|
||||
</submenu>
|
||||
</effects-menu>
|
||||
</effect>
|
||||
|
||||
<script>
|
||||
{{ command_tag | safe }}
|
||||
</script>
|
||||
</inkscape-extension>
|
Ładowanie…
Reference in New Issue