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
|
# Authors: see git history
#
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
import inkex
from ..i18n import _
from .base import InkstitchExtension
class ZigzagLineToSatin(InkstitchExtension):
"""Convert a satin column into a running stitch."""
def __init__(self, *args, **kwargs):
InkstitchExtension.__init__(self, *args, **kwargs)
self.arg_parser.add_argument("--zigzag_to_satin", type=str, default=None)
self.arg_parser.add_argument("--options", type=str, default=None)
self.arg_parser.add_argument("--info", type=str, default=None)
self.arg_parser.add_argument("-s", "--smoothing", type=inkex.Boolean, default=True, dest="smoothing")
self.arg_parser.add_argument("-p", "--pattern", type=str, default="square", dest="pattern")
self.arg_parser.add_argument("-r", "--rungs", type=inkex.Boolean, default=True, dest="rungs")
self.arg_parser.add_argument("-l", "--reduce-rungs", type=inkex.Boolean, default=False, dest="reduce_rungs")
def effect(self):
if not self.svg.selection or not self.get_elements():
inkex.errormsg(_("Please select at least one stroke to convert to a satin column."))
return
for node in self.svg.selection:
d = []
point_list = list(node.get_path().end_points)
rails, rungs = self._get_rails_and_rungs(point_list)
if not self.options.smoothing:
for rail in rails:
d.append('M ' + ' '.join([str(point) for point in rail]))
else:
rails, rungs = self._smooth_path(rails, rungs)
d.append(rails)
if self.options.rungs:
if self.options.pattern != 'zigzag' and self.options.reduce_rungs and len(rungs) > 2:
rungs = rungs[0::2]
d.extend(self._rung_path(rungs))
node.set('d', " ".join(d))
node.set('inkstitch:satin_column', True)
def _get_rails_and_rungs(self, point_list):
if self.options.pattern == "sawtooth":
# sawtooth pattern: |/|/|/|
rails = [point_list[0::2], point_list[1::2]]
rungs = list(zip(point_list[1::2], point_list[:-1:2]))
return rails, rungs
elif self.options.pattern == "zigzag":
# zigzag pattern: VVVVV
rails = [point_list[0::2], point_list[1::2]]
rail_points = [[], []]
rung_points = [[], []]
for i, rail in enumerate(rails):
for j, point in enumerate(rail):
if j == 0 or point in point_list[2::len(point_list)-3]:
rail_points[i].append(point)
rung_points[i].append(point)
continue
p0 = rail[j-1]
rail_points[i].append(point)
rung_points[i].append(inkex.Vector2d(inkex.DirectedLineSegment(p0, point).point_at_ratio(0.5)))
rung_points[i].append(point)
rungs = list(zip(*rung_points))
return rail_points, rungs
else:
# square pattern: |_|▔|_|▔|
point_list = [point_list[i:i+4] for i in range(0, len(point_list), 4)]
rungs = []
rails = [[], []]
for i, points in enumerate(point_list):
if len(points) <= 1:
break
elif len(points) < 4 and len(points) > 1:
rails[0].append(points[0])
rails[1].append(points[1])
rungs.append([points[0], points[1]])
break
rails[0].extend([points[0], points[3]])
rails[1].extend([points[1], points[2]])
rungs.extend([[points[0], points[1]], [points[2], points[3]]])
return rails, rungs
def _smooth_path(self, rails, rungs):
path_commands = []
new_rungs = []
k = [1, 0]
smoothing = 0.4
has_equal_rail_point_count = len(rails[0]) == len(rails[1])
for j, rail in enumerate(rails):
r = rungs[j:len(rungs):2]
for i, point in enumerate(rail):
if i == 0:
path_commands.append(inkex.paths.Move(*point))
else:
# get the two previous points and the next point for handle calculation
if i < 2:
prev_prev = rail[i - 1]
else:
prev_prev = rail[i-2]
prev = rail[i-1]
if i > len(rail) - 2:
next = point
else:
next = rail[i+1]
# get length of handles
length = inkex.DirectedLineSegment(point, prev).length * smoothing
# get handle positions
start = inkex.DirectedLineSegment(prev_prev, point)
end = inkex.DirectedLineSegment(next, prev)
if not start.length == 0:
start = start.parallel(*prev).point_at_length(start.length - length)
else:
start = start.start
if not end.length == 0:
end = end.parallel(*point).point_at_length(end.length - length)
else:
end = end.start
# generate curves
path_commands.append(inkex.paths.Curve(*start, *end, *point))
if self.options.pattern == 'zigzag' and (has_equal_rail_point_count or i <= len(r)) and (not self.options.reduce_rungs or j == 0):
# in zigzag mode we do have alternating points on rails
# when smoothing them out, rungs may not intersect anymore
# so we need to find a spot on the smoothed rail to ensure the correct length
rung = r[i-1]
line = inkex.DirectedLineSegment(rung[0], rung[1])
point0 = line.point_at_length(-50)
point1 = line.point_at_length(line.length + 50)
new_point = inkex.bezier.linebezierintersect((point0, point1), [prev, start, end, point])
if new_point:
new_rungs.append((rung[k[j]], new_point[0]))
else:
new_rungs.append(rung)
rungs = self._update_rungs(new_rungs, rungs, r, has_equal_rail_point_count)
return str(inkex.Path(path_commands)), rungs
def _update_rungs(self, new_rungs, rungs, r, has_equal_rail_point_count):
if self.options.pattern == 'zigzag':
rungs = new_rungs
if not has_equal_rail_point_count and not self.options.reduce_rungs:
# make sure, that the last rail on canvas is also the last rail in the list
# this important when we delete the very first and very last rung
count = len(r)
rungs[count], rungs[-1] = rungs[-1], rungs[count]
return rungs
def _rung_path(self, rungs):
if len(rungs) < 3:
return []
d = []
rungs = rungs[1:-1]
for point0, point1 in rungs:
line = inkex.DirectedLineSegment(point0, point1)
point0 = line.point_at_length(-0.8)
point1 = line.point_at_length(line.length + 0.8)
d.append(f'M {point0[0]}, {point0[1]} {point1[0]}, {point1[1]}')
return d
|