1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
|
# Authors: see git history
#
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
from math import atan2, degrees, radians
from inkex import CubicSuperPath, Path, Transform
from shapely import MultiLineString
from ..commands import is_command_symbol
from ..i18n import _
from ..svg.path import get_node_transform
from ..svg.tags import (EMBROIDERABLE_TAGS, INKSTITCH_ATTRIBS, SVG_USE_TAG,
XLINK_HREF)
from ..utils import cache
from .element import EmbroideryElement, param
from .validation import ObjectTypeWarning, ValidationWarning
class CloneWarning(ValidationWarning):
name = _("Clone Object")
description = _("There are one or more clone objects in this document. "
"Ink/Stitch can work with single clones, but you are limited to set a very few parameters. ")
steps_to_solve = [
_("If you want to convert the clone into a real element, follow these steps:"),
_("* Select the clone"),
_("* Run: Edit > Clone > Unlink Clone (Alt+Shift+D)")
]
class CloneSourceWarning(ObjectTypeWarning):
name = _("Clone is not embroiderable")
description = _("There are one ore more clone objects in this document. A clone must be a direct child of an embroiderable element. "
"Ink/Stitch cannot embroider clones of groups or other not embroiderable elements (text or image).")
steps_to_solve = [
_("Convert the clone into a real element:"),
_("* Select the clone."),
_("* Run: Edit > Clone > Unlink Clone (Alt+Shift+D)")
]
class Clone(EmbroideryElement):
# A clone embroidery element is linked to an embroiderable element.
# It will be ignored if the source element is not a direct child of the xlink attribute.
element_name = "Clone"
def __init__(self, *args, **kwargs):
super(Clone, self).__init__(*args, **kwargs)
@property
@param('clone', _("Clone"), type='toggle', inverse=False, default=True)
def clone(self):
return self.get_boolean_param("clone")
@property
@param('angle',
_('Custom fill angle'),
tooltip=_("This setting will apply a custom fill angle for the clone."),
unit='deg',
type='float')
@cache
def clone_fill_angle(self):
return self.get_float_param('angle') or None
@property
@param('flip_angle',
_('Flip angle'),
tooltip=_("Flip automatically calucalted angle if it appears to be wrong."),
type='boolean')
@cache
def flip_angle(self):
return self.get_boolean_param('flip_angle')
def get_cache_key_data(self, previous_stitch):
source_node = get_clone_source(self.node)
source_elements = self.clone_to_element(source_node)
return [element.get_cache_key(previous_stitch) for element in source_elements]
def clone_to_element(self, node):
from .utils import node_to_elements
return node_to_elements(node, True)
def to_stitch_groups(self, last_patch=None):
patches = []
source_node = get_clone_source(self.node)
if source_node.tag not in EMBROIDERABLE_TAGS:
return []
old_transform = source_node.get('transform', '')
source_transform = source_node.composed_transform()
source_path = Path(source_node.get_path()).transform(source_transform)
transform = Transform(source_node.get('transform', '')) @ -source_transform
transform @= self.node.composed_transform() @ Transform(source_node.get('transform', ''))
source_node.set('transform', transform)
old_angle = float(source_node.get(INKSTITCH_ATTRIBS['angle'], 0))
if self.clone_fill_angle is None:
rot = transform.add_rotate(-old_angle)
angle = self._get_rotation(rot, source_node, source_path)
if angle is not None:
source_node.set(INKSTITCH_ATTRIBS['angle'], angle)
else:
source_node.set(INKSTITCH_ATTRIBS['angle'], self.clone_fill_angle)
elements = self.clone_to_element(source_node)
for element in elements:
stitch_groups = element.to_stitch_groups(last_patch)
patches.extend(stitch_groups)
source_node.set('transform', old_transform)
source_node.set(INKSTITCH_ATTRIBS['angle'], old_angle)
return patches
def _get_rotation(self, transform, source_node, source_path):
try:
rotation = transform.rotation_degrees()
except ValueError:
source_path = CubicSuperPath(source_path)[0]
clone_path = Path(source_node.get_path()).transform(source_node.composed_transform())
clone_path = CubicSuperPath(clone_path)[0]
angle_source = atan2(source_path[1][1][1] - source_path[0][1][1], source_path[1][1][0] - source_path[0][1][0])
angle_clone = atan2(clone_path[1][1][1] - clone_path[0][1][1], clone_path[1][1][0] - clone_path[0][1][0])
angle_embroidery = radians(-float(source_node.get(INKSTITCH_ATTRIBS['angle'], 0)))
diff = angle_source - angle_embroidery
rotation = degrees(diff + angle_clone)
if self.flip_angle:
rotation = -degrees(diff - angle_clone)
return -rotation
def get_clone_style(self, style_name, node, default=None):
style = node.style[style_name] or default
return style
def center(self, source_node):
transform = get_node_transform(self.node.getparent())
center = self.node.bounding_box(transform).center
return center
@property
def shape(self):
path = self.node.get_path()
transform = Transform(self.node.composed_transform())
path = path.transform(transform)
path = path.to_superpath()
return MultiLineString(path)
def validation_warnings(self):
source_node = get_clone_source(self.node)
if source_node.tag not in EMBROIDERABLE_TAGS:
point = self.center(source_node)
yield CloneSourceWarning(point)
else:
point = self.center(source_node)
yield CloneWarning(point)
def is_clone(node):
if node.tag == SVG_USE_TAG and node.get(XLINK_HREF) and not is_command_symbol(node):
return True
return False
def is_embroiderable_clone(node):
if is_clone(node) and get_clone_source(node).tag in EMBROIDERABLE_TAGS:
return True
return False
def get_clone_source(node):
return node.href
|