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
|
# 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 atan, degrees
import inkex
from ..commands import is_command, is_command_symbol
from ..i18n import _
from ..svg.path import get_node_transform
from ..svg.svg import find_elements
from ..svg.tags import (EMBROIDERABLE_TAGS, INKSTITCH_ATTRIBS,
SVG_POLYLINE_TAG, SVG_USE_TAG, XLINK_HREF)
from ..utils import cache
from .auto_fill import AutoFill
from .element import EmbroideryElement, param
from .fill import Fill
from .polyline import Polyline
from .satin_column import SatinColumn
from .stroke import Stroke
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', 0)
def clone_to_element(self, node):
# we need to determine if the source element is polyline, stroke, fill or satin
element = EmbroideryElement(node)
if node.tag == SVG_POLYLINE_TAG:
return [Polyline(node)]
elif element.get_boolean_param("satin_column") and self.get_clone_style("stroke", self.node):
return [SatinColumn(node)]
else:
elements = []
if element.get_style("fill", "black") and not element.get_style("stroke", 1) == "0":
if element.get_boolean_param("auto_fill", True):
elements.append(AutoFill(node))
else:
elements.append(Fill(node))
if element.get_style("stroke", self.node) is not None:
if not is_command(element.node):
elements.append(Stroke(node))
if element.get_boolean_param("stroke_first", False):
elements.reverse()
return elements
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 []
self.node.style = source_node.specified_style()
# a. a custom set fill angle
# b. calculated rotation for the cloned fill element to look exactly as it's source
param = INKSTITCH_ATTRIBS['angle']
if self.clone_fill_angle is not None:
angle = self.clone_fill_angle
else:
# clone angle
clone_mat = self.node.composed_transform()
clone_angle = degrees(atan(-clone_mat[1][0]/clone_mat[1][1]))
# source node angle
source_mat = source_node.composed_transform()
source_angle = degrees(atan(-source_mat[1][0]/source_mat[1][1]))
# source node fill angle
source_fill_angle = source_node.get(param, 0)
angle = clone_angle + float(source_fill_angle) - source_angle
self.node.set(param, str(angle))
elements = self.clone_to_element(self.node)
for element in elements:
patches.extend(element.to_stitch_groups(last_patch))
return patches
def get_clone_style(self, style_name, node, default=None):
style = inkex.styles.AttrFallbackStyle(node).get(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
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):
source_id = node.get(XLINK_HREF)[1:]
xpath = ".//*[@id='%s']" % (source_id)
source_node = find_elements(node, xpath)[0]
return source_node
|