kopia lustrzana https://github.com/inkstitch/inkstitch
676 wiersze
24 KiB
Python
676 wiersze
24 KiB
Python
from math import sqrt
|
|
from typing import Optional
|
|
|
|
from inkex import (Circle, Group, Rectangle, SvgDocumentElement, TextElement,
|
|
Transform, Use)
|
|
from inkex.tester import TestCase
|
|
from inkex.tester.svg import svg
|
|
|
|
from lib.commands import add_commands
|
|
from lib.elements import Clone, EmbroideryElement, FillStitch
|
|
from lib.svg.tags import INKSCAPE_LABEL, INKSTITCH_ATTRIBS, SVG_RECT_TAG
|
|
from lib.utils import cache_module
|
|
|
|
from .utils import element_count
|
|
|
|
|
|
def element_fill_angle(element: EmbroideryElement) -> Optional[float]:
|
|
angle = element.node.get(INKSTITCH_ATTRIBS['angle'])
|
|
if angle is not None:
|
|
angle = float(angle)
|
|
return angle
|
|
|
|
|
|
class CloneElementTest(TestCase):
|
|
# Monkey-patch the cahce to forcibly disable it: We may need to refactor this out for tests.
|
|
def setUp(self) -> None:
|
|
from pytest import MonkeyPatch
|
|
self.monkeypatch = MonkeyPatch()
|
|
self.monkeypatch.setattr(cache_module, "is_cache_disabled", lambda: True)
|
|
|
|
def tearDown(self) -> None:
|
|
self.monkeypatch.undo()
|
|
return super().tearDown()
|
|
|
|
def assertAngleAlmostEqual(self, a, b) -> None:
|
|
# Take the mod 180 of the returned angles, because e.g. -130deg and 50deg produce fills along the same angle.
|
|
# We have to use a precision of 4 decimal digits because of the precision of the matrices as they are stored in the svg trees
|
|
# generated by these tests.
|
|
self.assertAlmostEqual(a % 180, b % 180, 4)
|
|
|
|
def test_not_embroiderable(self) -> None:
|
|
root: SvgDocumentElement = svg()
|
|
text = root.add(TextElement())
|
|
text.text = "Can't embroider this!"
|
|
use = root.add(Use())
|
|
use.href = text
|
|
|
|
clone = Clone(use)
|
|
stitch_groups = clone.to_stitch_groups(None)
|
|
self.assertEqual(len(stitch_groups), 0)
|
|
|
|
def test_not_clone(self) -> None:
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
INKSTITCH_ATTRIBS["angle"]: "30"
|
|
}))
|
|
use = root.add(Use(attrib={
|
|
INKSTITCH_ATTRIBS["clone"]: "false"
|
|
}))
|
|
use.href = rect
|
|
|
|
clone = Clone(use)
|
|
stitch_groups = clone.to_stitch_groups(None)
|
|
self.assertEqual(len(stitch_groups), 0)
|
|
|
|
# These tests make sure the element cloning works as expected, using the `clone_elements` method.
|
|
|
|
def test_basic(self) -> None:
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
INKSTITCH_ATTRIBS["angle"]: "30"
|
|
}))
|
|
use = root.add(Use())
|
|
use.href = rect
|
|
|
|
clone = Clone(use)
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count())
|
|
self.assertAngleAlmostEqual(element_fill_angle(elements[0]), 30)
|
|
|
|
def test_hidden_cloned_elements_not_embroidered(self) -> None:
|
|
root = svg()
|
|
g = root.add(Group())
|
|
g.add(Rectangle(attrib={
|
|
INKSCAPE_LABEL: "NotHidden",
|
|
"width": "10",
|
|
"height": "10"
|
|
}))
|
|
g.add(Rectangle(attrib={
|
|
INKSCAPE_LABEL: "Hidden",
|
|
"width": "10",
|
|
"height": "10",
|
|
"style": "display:none"
|
|
}))
|
|
hidden_group = g.add(Group(attrib={
|
|
"style": "display:none"
|
|
}))
|
|
hidden_group.add(Rectangle(attrib={
|
|
INKSCAPE_LABEL: "ChildOfHidden",
|
|
"width": "10",
|
|
"height": "10",
|
|
}))
|
|
use = root.add(Use())
|
|
use.href = g
|
|
|
|
clone = Clone(use)
|
|
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count())
|
|
self.assertEqual(elements[0].node.get(INKSCAPE_LABEL), "NotHidden")
|
|
|
|
def test_angle_rotated(self) -> None:
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
INKSTITCH_ATTRIBS["angle"]: "30"
|
|
}))
|
|
use = root.add(Use())
|
|
use.href = rect
|
|
use.set('transform', Transform().add_rotate(20))
|
|
|
|
clone = Clone(use)
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count())
|
|
self.assertAngleAlmostEqual(element_fill_angle(elements[0]), 10)
|
|
|
|
def test_angle_flipped(self) -> None:
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
INKSTITCH_ATTRIBS["angle"]: "30"
|
|
}))
|
|
use = root.add(Use())
|
|
use.href = rect
|
|
use.set('transform', Transform().add_scale(-1, 1))
|
|
|
|
clone = Clone(use)
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count())
|
|
self.assertAngleAlmostEqual(element_fill_angle(elements[0]), -30)
|
|
|
|
def test_angle_flipped_rotated(self) -> None:
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
INKSTITCH_ATTRIBS["angle"]: "30"
|
|
}))
|
|
use = root.add(Use())
|
|
use.href = rect
|
|
use.set('transform', Transform().add_rotate(20).add_scale(-1, 1))
|
|
|
|
clone = Clone(use)
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count())
|
|
# Fill angle goes from 30 -> -30 after flip -> -50 after rotate
|
|
self.assertAngleAlmostEqual(element_fill_angle(elements[0]), -50)
|
|
|
|
def test_angle_non_uniform_scale(self) -> None:
|
|
"""
|
|
The angle isn't *as* well-defined for non-rotational scales, but we try to follow how the slope will be altered.
|
|
"""
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
INKSTITCH_ATTRIBS["angle"]: "30"
|
|
}))
|
|
use = root.add(Use())
|
|
use.href = rect
|
|
use.set('transform', Transform().add_rotate(10).add_scale(1, -sqrt(3)))
|
|
|
|
clone = Clone(use)
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count())
|
|
# Slope of the stitching goes from tan(30deg) = 1/sqrt(3) to -sqrt(3)/sqrt(3) = tan(-45deg),
|
|
# then rotated another -10 degrees to -55
|
|
self.assertAngleAlmostEqual(element_fill_angle(elements[0]), -55)
|
|
|
|
def test_angle_inherits_down_tree(self) -> None:
|
|
"""
|
|
The stitching angle of a clone is based in part on the relative transforms of the source and clone.
|
|
"""
|
|
root: SvgDocumentElement = svg()
|
|
g1 = root.add(Group())
|
|
g1.set('transform', Transform().add_rotate(3))
|
|
rect = g1.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
INKSTITCH_ATTRIBS["angle"]: "30"
|
|
}))
|
|
g2 = root.add(Group())
|
|
g2.set('transform', Transform().add_translate((20, 0)).add_rotate(-7))
|
|
use = g2.add(Use())
|
|
use.href = rect
|
|
use.set('transform', Transform().add_rotate(11))
|
|
|
|
clone = Clone(use)
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count())
|
|
# Angle goes from 30 -> 40 (g1 -> g2) -> 29 (use)
|
|
self.assertAngleAlmostEqual(element_fill_angle(elements[0]), 29)
|
|
|
|
def test_angle_not_applied_twice(self) -> None:
|
|
"""Make sure that angle changes are not applied twice to an element with both stroke and fill."""
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
"style": "stroke: skyblue; fill: red;"
|
|
}))
|
|
use = root.add(Use())
|
|
use.href = rect
|
|
use.set('transform', Transform().add_rotate(30))
|
|
|
|
clone = Clone(use)
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count()+1) # One for the stroke, one for the fill, one for the SewStack
|
|
self.assertEqual(elements[0].node, elements[1].node)
|
|
# Angle goes from 0 -> -30
|
|
self.assertAngleAlmostEqual(element_fill_angle(elements[0]), -30)
|
|
|
|
def test_angle_not_applied_to_non_fills(self) -> None:
|
|
"""Make sure that angle changes are not applied to non-fill elements."""
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
"style": "stroke: skyblue; fill-opacity: 0;"
|
|
}))
|
|
use = root.add(Use())
|
|
use.href = rect
|
|
use.set('transform', Transform().add_rotate(30))
|
|
|
|
clone = Clone(use)
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count()) # One for the stroke, one for the fill, one for the SewStack
|
|
self.assertIsNone(elements[0].get_param("angle", None)) # Angle as not set, as this isn't a fill
|
|
|
|
def test_style_inherits(self) -> None:
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10"
|
|
}))
|
|
rect.set('style', 'stroke: skyblue; fill-opacity: 0;')
|
|
use = root.add(Use())
|
|
use.href = rect
|
|
use.set('style', 'stroke: red; stroke-width: 2;')
|
|
|
|
clone = Clone(use)
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count())
|
|
style = elements[0].node.cascaded_style()
|
|
# Source style takes precedence over any attributes specified in the clone
|
|
self.assertEqual(style["stroke"], "skyblue")
|
|
self.assertEqual(style["stroke-width"], "2")
|
|
|
|
def test_transform_inherits_from_cloned_element(self) -> None:
|
|
"""
|
|
Elements cloned by cloned_elements need to inherit their transform from their href'd element and their use to match what's shown.
|
|
"""
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
INKSTITCH_ATTRIBS["angle"]: "30",
|
|
}))
|
|
rect.set('transform', Transform().add_scale(2, 2))
|
|
use = root.add(Use())
|
|
use.href = rect
|
|
use.set('transform', Transform().add_translate((5, 10)))
|
|
|
|
clone = Clone(use)
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count())
|
|
self.assertTransformEqual(
|
|
elements[0].node.composed_transform(),
|
|
Transform().add_translate((5, 10)).add_scale(2, 2))
|
|
|
|
def test_transform_inherits_from_tree(self) -> None:
|
|
root: SvgDocumentElement = svg()
|
|
g1 = root.add(Group())
|
|
g1.set('transform', Transform().add_translate((0, 5)).add_rotate(5))
|
|
rect = g1.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
INKSTITCH_ATTRIBS["angle"]: "30",
|
|
}))
|
|
rect.set('transform', Transform().add_scale(2, 2))
|
|
use = root.add(Use())
|
|
use.href = g1
|
|
use.set('transform', Transform().add_translate((5, 10)))
|
|
|
|
clone = Clone(use)
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count())
|
|
self.assertTransformEqual(
|
|
elements[0].node.composed_transform(),
|
|
Transform().add_translate((5, 10)) # use
|
|
.add_translate((0, 5)).add_rotate(5) # g1
|
|
.add_scale(2, 2), # rect
|
|
5)
|
|
|
|
def test_transform_inherits_from_tree_up_tree(self) -> None:
|
|
root: SvgDocumentElement = svg()
|
|
g1 = root.add(Group())
|
|
g1.set('transform', Transform().add_translate((0, 5)).add_rotate(5))
|
|
rect = g1.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
INKSTITCH_ATTRIBS["angle"]: "30",
|
|
}))
|
|
rect.set('transform', Transform().add_scale(2, 2))
|
|
circ = g1.add(Circle())
|
|
circ.radius = 5
|
|
g2 = root.add(Group())
|
|
g2.set('transform', Transform().add_translate((1, 2)).add_scale(0.5, 1))
|
|
use = g2.add(Use())
|
|
use.href = g1
|
|
use.set('transform', Transform().add_translate((5, 10)))
|
|
|
|
clone = Clone(use)
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count()*2) # FillStitch, SewStack, FillStitch, SewStack
|
|
self.assertTransformEqual(
|
|
elements[0].node.composed_transform(),
|
|
Transform().add_translate((1, 2)).add_scale(0.5, 1) # g2
|
|
.add_translate((5, 10)) # use
|
|
.add_translate((0, 5)).add_rotate(5) # g1
|
|
.add_scale(2, 2), # rect
|
|
5)
|
|
self.assertTransformEqual(
|
|
elements[element_count()].node.composed_transform(),
|
|
Transform().add_translate((1, 2)).add_scale(0.5, 1) # g2
|
|
.add_translate((5, 10)) # use
|
|
.add_translate((0, 5)).add_rotate(5), # g1
|
|
5)
|
|
|
|
def test_clone_fill_angle_not_specified(self) -> None:
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
INKSTITCH_ATTRIBS["angle"]: "30"
|
|
}))
|
|
use = root.add(Use())
|
|
use.href = rect
|
|
use.set('transform', Transform().add_rotate(20))
|
|
|
|
clone = Clone(use)
|
|
self.assertEqual(clone.clone_fill_angle, None)
|
|
|
|
def test_clone_fill_angle(self) -> None:
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
INKSTITCH_ATTRIBS["angle"]: "30"
|
|
}))
|
|
use = root.add(Use())
|
|
use.href = rect
|
|
use.set(INKSTITCH_ATTRIBS["angle"], 42)
|
|
use.set('transform', Transform().add_rotate(20))
|
|
|
|
clone = Clone(use)
|
|
self.assertEqual(clone.clone_fill_angle, 42)
|
|
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count())
|
|
self.assertAngleAlmostEqual(element_fill_angle(elements[0]), 42)
|
|
|
|
def test_angle_manually_flipped(self) -> None:
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
INKSTITCH_ATTRIBS["angle"]: "30"
|
|
}))
|
|
use = root.add(Use())
|
|
use.href = rect
|
|
use.set('transform', Transform().add_rotate(20))
|
|
use.set(INKSTITCH_ATTRIBS["flip_angle"], True)
|
|
|
|
clone = Clone(use)
|
|
self.assertTrue(clone.flip_angle)
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count())
|
|
self.assertAngleAlmostEqual(element_fill_angle(elements[0]), -10)
|
|
|
|
# Recursive use tests
|
|
|
|
def test_recursive_uses(self) -> None:
|
|
root: SvgDocumentElement = svg()
|
|
g1 = root.add(Group())
|
|
rect = g1.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
}))
|
|
u1 = g1.add(Use())
|
|
u1.set('transform', Transform().add_translate((20, 0)))
|
|
u1.href = rect
|
|
u2 = root.add(Use())
|
|
u2.set('transform', Transform().add_translate((0, 20)).add_scale(0.5, 0.5))
|
|
u2.href = g1
|
|
u3 = root.add(Use())
|
|
u3.set('transform', Transform().add_translate((0, 30)))
|
|
u3.href = u2
|
|
|
|
clone = Clone(u3)
|
|
with clone.clone_elements() as elements:
|
|
# There should be two elements cloned from u3, two rects, one corresponding to rect and one corresponding to u1.
|
|
# Their transforms should derive from the elements they href.
|
|
self.assertEqual(len(elements), element_count()*2)
|
|
self.assertEqual(type(elements[0]), FillStitch)
|
|
self.assertEqual(elements[0].node.tag, SVG_RECT_TAG)
|
|
self.assertTransformEqual(elements[0].node.composed_transform(),
|
|
Transform().add_translate((0, 30)) # u3
|
|
.add_translate(0, 20).add_scale(0.5, 0.5) # u2
|
|
)
|
|
|
|
self.assertEqual(type(elements[element_count()]), FillStitch)
|
|
self.assertEqual(elements[element_count()].node.tag, SVG_RECT_TAG)
|
|
self.assertTransformEqual(elements[element_count()].node.composed_transform(),
|
|
Transform().add_translate((0, 30)) # u3
|
|
.add_translate((0, 20)).add_scale(0.5, 0.5) # u2
|
|
.add_translate((20, 0)) # u1
|
|
)
|
|
|
|
def test_recursive_uses_angle(self) -> None:
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
INKSTITCH_ATTRIBS["angle"]: "30"
|
|
}))
|
|
u1 = root.add(Use())
|
|
u1.set('transform', Transform().add_rotate(60))
|
|
u1.href = rect
|
|
|
|
clone = Clone(u1)
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count())
|
|
# Angle goes from 30 -> -30
|
|
self.assertAngleAlmostEqual(element_fill_angle(elements[0]), -30)
|
|
|
|
g = root.add(Group())
|
|
g.set('transform', Transform().add_rotate(-10))
|
|
u2 = g.add(Use())
|
|
u2.href = u1
|
|
|
|
clone = Clone(u2)
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count())
|
|
# Angle goes from -30 -> -20 (u1 -> g)
|
|
self.assertAngleAlmostEqual(element_fill_angle(elements[0]), -20)
|
|
|
|
u3 = root.add(Use())
|
|
u3.set('transform', Transform().add_rotate(7))
|
|
u3.href = g
|
|
|
|
clone = Clone(u3)
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count())
|
|
# Angle goes from -20 -> -27
|
|
self.assertAngleAlmostEqual(element_fill_angle(elements[0]), -27)
|
|
|
|
# Cloning u2 directly, the relative transform of g does not apply
|
|
u4 = root.add(Use())
|
|
u4.set('transform', Transform().add_rotate(7))
|
|
u4.href = u2
|
|
|
|
clone = Clone(u4)
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count())
|
|
# Angle goes from -30 -> -37
|
|
self.assertAngleAlmostEqual(element_fill_angle(elements[0]), -37)
|
|
|
|
def test_recursive_uses_angle_with_specified_angle(self) -> None:
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
INKSTITCH_ATTRIBS["angle"]: "30"
|
|
}))
|
|
u1 = root.add(Use())
|
|
u1.set('transform', Transform().add_rotate(60))
|
|
u1.href = rect
|
|
g = root.add(Group())
|
|
g.set('transform', Transform().add_rotate(-10))
|
|
u2 = g.add(Use())
|
|
u2.href = u1
|
|
u2.set(INKSTITCH_ATTRIBS["angle"], "0")
|
|
u3 = root.add(Use())
|
|
u3.set_id('U3')
|
|
u3.set('transform', Transform().add_rotate(7))
|
|
u3.href = g
|
|
|
|
clone = Clone(u3)
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count())
|
|
# Angle goes from 0 (g -> u2) -> -7 (u3)
|
|
self.assertAngleAlmostEqual(element_fill_angle(elements[0]), -7)
|
|
|
|
# Command clone tests
|
|
|
|
def test_copies_directly_attached_commands(self) -> None:
|
|
"""
|
|
Check that commands attached to the clone target directly are applied to clones.
|
|
"""
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
}))
|
|
|
|
use = root.add(Use())
|
|
use.href = rect
|
|
use.set('transform', Transform().add_translate(10, 10))
|
|
|
|
original = FillStitch(rect)
|
|
add_commands(original, ["ending_point"])
|
|
|
|
clone = Clone(use)
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count())
|
|
cmd_orig = original.get_command("ending_point")
|
|
cmd_clone = elements[0].get_command("ending_point")
|
|
self.assertIsNotNone(cmd_clone)
|
|
self.assertAlmostEqual(cmd_orig.target_point[0]+10, cmd_clone.target_point[0], 4)
|
|
self.assertAlmostEqual(cmd_orig.target_point[1]+10, cmd_clone.target_point[1], 4)
|
|
|
|
def test_copies_indirectly_attached_commands(self) -> None:
|
|
"""
|
|
Check that commands attached to children of the clone target are copied to clones.
|
|
"""
|
|
root: SvgDocumentElement = svg()
|
|
group = root.add(Group())
|
|
rect = group.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
}))
|
|
|
|
use = root.add(Use())
|
|
use.href = group
|
|
use.set('transform', Transform().add_translate(10, 10))
|
|
|
|
original = FillStitch(rect)
|
|
add_commands(original, ["ending_point"])
|
|
|
|
clone = Clone(use)
|
|
with clone.clone_elements() as elements:
|
|
self.assertEqual(len(elements), element_count())
|
|
cmd_orig = original.get_command("ending_point")
|
|
cmd_clone = elements[0].get_command("ending_point")
|
|
self.assertIsNotNone(cmd_clone)
|
|
self.assertAlmostEqual(cmd_orig.target_point[0]+10, cmd_clone.target_point[0], 4)
|
|
self.assertAlmostEqual(cmd_orig.target_point[1]+10, cmd_clone.target_point[1], 4)
|
|
|
|
# Checks that trim_after and stop_after commands and settings in cloned elements aren't overridden
|
|
|
|
def test_trim_after(self) -> None:
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
INKSTITCH_ATTRIBS["trim_after"]: "true"
|
|
}))
|
|
|
|
use = root.add(Use())
|
|
use.href = rect
|
|
|
|
clone = Clone(use)
|
|
|
|
stitch_groups = clone.embroider(None)
|
|
self.assertGreater(len(stitch_groups), 0)
|
|
self.assertTrue(stitch_groups[-1].trim_after)
|
|
|
|
def test_trim_after_command(self) -> None:
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
}))
|
|
add_commands(FillStitch(rect), ["trim"])
|
|
|
|
use = root.add(Use())
|
|
use.href = rect
|
|
|
|
clone = Clone(use)
|
|
|
|
stitch_groups = clone.embroider(None)
|
|
self.assertGreater(len(stitch_groups), 0)
|
|
self.assertTrue(stitch_groups[-1].trim_after)
|
|
|
|
def test_trim_after_command_on_clone(self) -> None:
|
|
"""
|
|
If the clone element has a trim command, it should apply!
|
|
"""
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
}))
|
|
|
|
use = root.add(Use())
|
|
use.href = rect
|
|
|
|
clone = Clone(use)
|
|
add_commands(clone, ["trim"])
|
|
|
|
stitch_groups = clone.embroider(None)
|
|
self.assertGreater(len(stitch_groups), 0)
|
|
self.assertTrue(stitch_groups[-1].trim_after)
|
|
|
|
def test_stop_after(self) -> None:
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
INKSTITCH_ATTRIBS["stop_after"]: "true"
|
|
}))
|
|
|
|
use = root.add(Use())
|
|
use.href = rect
|
|
|
|
clone = Clone(use)
|
|
|
|
stitch_groups = clone.embroider(None)
|
|
self.assertGreater(len(stitch_groups), 0)
|
|
self.assertTrue(stitch_groups[-1].stop_after)
|
|
|
|
def test_stop_after_command(self) -> None:
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
}))
|
|
fill_stitch = FillStitch(rect)
|
|
add_commands(fill_stitch, ["stop"])
|
|
|
|
use = root.add(Use())
|
|
use.href = rect
|
|
|
|
clone = Clone(use)
|
|
|
|
stitch_groups = clone.embroider(None)
|
|
self.assertGreater(len(stitch_groups), 0)
|
|
self.assertTrue(stitch_groups[-1].stop_after)
|
|
|
|
def test_stop_after_command_on_clone(self) -> None:
|
|
"""
|
|
If the clone element has a stop command, it should still apply!
|
|
"""
|
|
root: SvgDocumentElement = svg()
|
|
rect = root.add(Rectangle(attrib={
|
|
"width": "10",
|
|
"height": "10",
|
|
}))
|
|
|
|
use = root.add(Use())
|
|
use.href = rect
|
|
|
|
clone = Clone(use)
|
|
add_commands(clone, ["stop"])
|
|
|
|
stitch_groups = clone.embroider(None)
|
|
self.assertGreater(len(stitch_groups), 0)
|
|
self.assertTrue(stitch_groups[-1].stop_after)
|