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
|
# 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 self.options.rungs:
if self.options.reduce_rungs and len(rungs) > 2:
rungs = rungs[0::2]
d.extend(self._rung_path(rungs))
if not self.options.smoothing:
for rail in rails:
d.append('M ' + ' '.join([str(point) for point in rail]))
else:
d.append(self._smooth_path(rails))
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 = [[], []]
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)
continue
p0 = rail[j-1]
rail_points[i].append(inkex.Vector2d(inkex.DirectedLineSegment(p0, point).point_at_ratio(0.5)))
rail_points[i].append(point)
rungs = list(zip(*rail_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):
path_commands = []
smoothing = 0.2
for rail in rails:
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))
return str(inkex.Path(path_commands))
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
|