|
@@ -45,19 +45,21 @@ def interpolate(p1, p2, step):
|
45
|
45
|
p.append(v)
|
46
|
46
|
return p
|
47
|
47
|
|
48
|
|
-def read_image(filename, path_steps, volume_percent, angle_d):
|
|
48
|
+def read_image(filename, path_steps, volume_percent,
|
|
49
|
+ samplerate, time,
|
|
50
|
+ angle_d, rot_steps, rot_size):
|
49
|
51
|
paths, attributes = svg2paths(filename)
|
50
|
52
|
print("paths={}".format(len(paths)))
|
51
|
53
|
for i, path in enumerate(paths):
|
52
|
54
|
print("path={} segments={}".format(i, len(path)))
|
53
|
55
|
|
54
|
56
|
# find center point to rotate around
|
55
|
|
- p0 = [paths[0][0].start.real, paths[0][0].start.imag]
|
|
57
|
+ p0 = [ paths[0][0].start.real, paths[0][0].start.imag ]
|
56
|
58
|
p_min = [p0[0], p0[1]]
|
57
|
59
|
p_max = [p0[0], p0[1]]
|
58
|
60
|
for path in paths:
|
59
|
61
|
for segment in path:
|
60
|
|
- p = [segment.end.real, segment.end.imag]
|
|
62
|
+ p = [ segment.end.real, segment.end.imag ]
|
61
|
63
|
for i in range(0, 2):
|
62
|
64
|
if p[i] < p_min[i]:
|
63
|
65
|
p_min[i] = p[i]
|
|
@@ -67,40 +69,21 @@ def read_image(filename, path_steps, volume_percent, angle_d):
|
67
|
69
|
|
68
|
70
|
# find bounding box for rotated object
|
69
|
71
|
for path in paths:
|
70
|
|
- # TODO this is not nice.
|
71
|
|
- # Doing both range(0, 360, 5) and angle_d is redundant.
|
72
|
|
- # But also, it bakes in the assumption of the animation
|
73
|
|
- # that's specified in the Makefile.
|
74
|
|
- # Ideally the animation should be handled here in the
|
75
|
|
- # script instead, with proper parameters.
|
76
|
|
-
|
77
|
|
- # find min max for all rotations
|
78
|
72
|
for segment in path:
|
79
|
|
- p_org = [segment.end.real, segment.end.imag]
|
80
|
|
- for a in range(0, 360, 5):
|
81
|
|
- p = rot_p(p_center, p_org , a)
|
|
73
|
+ a = angle_d
|
|
74
|
+ while (a < (angle_d + (rot_steps * rot_size))) or ((rot_steps <= 0) and (a == angle_d)):
|
|
75
|
+ p = rot_p(p_center, [ segment.end.real, segment.end.imag ], a)
|
82
|
76
|
for i in range(0, 2):
|
83
|
77
|
if p[i] < p_min[i]:
|
84
|
78
|
p_min[i] = p[i]
|
85
|
79
|
if p[i] > p_max[i]:
|
86
|
80
|
p_max[i] = p[i]
|
87
|
|
-
|
88
|
|
- # find min max for this specific rotation
|
89
|
|
- for segment in path:
|
90
|
|
- p = [segment.end.real, segment.end.imag]
|
91
|
|
- p = rot_p(p_center, p, angle_d)
|
92
|
|
- for i in range(0, 2):
|
93
|
|
- if p[i] < p_min[i]:
|
94
|
|
- p_min[i] = p[i]
|
95
|
|
- if p[i] > p_max[i]:
|
96
|
|
- p_max[i] = p[i]
|
|
81
|
+ a += rot_size
|
97
|
82
|
|
98
|
83
|
print("min={} max={}".format(p_min, p_max))
|
99
|
84
|
print("center={} ".format(p_center))
|
100
|
85
|
|
101
|
|
- data = bytearray()
|
102
|
|
-
|
103
|
|
- def add_point(p):
|
|
86
|
+ def add_point(p, data):
|
104
|
87
|
for i in range(0, 2):
|
105
|
88
|
v = p[i]
|
106
|
89
|
v -= p_min[i]
|
|
@@ -110,35 +93,51 @@ def read_image(filename, path_steps, volume_percent, angle_d):
|
110
|
93
|
c = int((v * 2 - 1) * (32767 / 100 * volume_percent))
|
111
|
94
|
data.extend(c.to_bytes(2, byteorder="little", signed=True))
|
112
|
95
|
|
113
|
|
- def add_segment(p1, p2):
|
|
96
|
+ def add_segment(p1, p2, data):
|
114
|
97
|
l = math.sqrt((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2)
|
115
|
98
|
ps = max(1, int(path_steps * l))
|
116
|
99
|
for step in range(0, ps):
|
117
|
100
|
p = interpolate(p1, p2, step / ps)
|
118
|
|
- add_point(p)
|
|
101
|
+ add_point(p, data)
|
119
|
102
|
|
120
|
|
- for path in paths:
|
121
|
|
- p0 = [path[0].start.real, path[0].start.imag]
|
122
|
|
- points = [rot_p(p_center, p0, angle_d)]
|
123
|
|
- for segment in path:
|
124
|
|
- p = [segment.end.real, segment.end.imag]
|
125
|
|
- p = rot_p(p_center, p, angle_d)
|
126
|
|
- points.append(p)
|
|
103
|
+ wave = bytearray()
|
|
104
|
+
|
|
105
|
+ a = angle_d
|
|
106
|
+ while (a < (angle_d + (rot_steps * rot_size))) or ((rot_steps <= 0) and (a == angle_d)):
|
|
107
|
+ data = bytearray()
|
|
108
|
+
|
|
109
|
+ for path in paths:
|
|
110
|
+ p0 = [ path[0].start.real, path[0].start.imag ]
|
|
111
|
+ points = [ rot_p(p_center, p0, a) ]
|
|
112
|
+ for segment in path:
|
|
113
|
+ p = [ segment.end.real, segment.end.imag ]
|
|
114
|
+ p = rot_p(p_center, p, a)
|
|
115
|
+ points.append(p)
|
|
116
|
+
|
|
117
|
+ # walk path forwards
|
|
118
|
+ for n in range(0, len(points) - 1):
|
|
119
|
+ add_segment(points[n], points[n + 1], data)
|
|
120
|
+
|
|
121
|
+ # walk path backwards
|
|
122
|
+ add_point(points[len(points) - 1], data)
|
|
123
|
+ for n in range(len(points) - 2, -1, -1):
|
|
124
|
+ add_segment(points[n + 1], points[n], data)
|
|
125
|
+ add_point(points[0], data)
|
127
|
126
|
|
128
|
|
- # walk path forwards
|
129
|
|
- for n in range(0, len(points) - 1):
|
130
|
|
- add_segment(points[n], points[n + 1])
|
|
127
|
+ # improve start/end of path. TODO only in virtual scope?
|
|
128
|
+ add_point(points[0], data)
|
131
|
129
|
|
132
|
|
- # walk path backwards
|
133
|
|
- add_point(points[len(points) - 1])
|
134
|
|
- for n in range(len(points) - 2, -1, -1):
|
135
|
|
- add_segment(points[n + 1], points[n])
|
136
|
|
- add_point(points[0])
|
|
130
|
+ # extend data to required length / runtime
|
|
131
|
+ samplecount = int(len(data) / 2 / 2) # stereo, int16
|
|
132
|
+ drawrate = samplerate / samplecount
|
|
133
|
+ drawcount = drawrate * (time / max(1, rot_steps))
|
137
|
134
|
|
138
|
|
- # improve start/end of path. TODO only in virtual scope?
|
139
|
|
- add_point(points[0])
|
|
135
|
+ for n in range(0, max(1, int(drawcount))):
|
|
136
|
+ wave.extend(data)
|
140
|
137
|
|
141
|
|
- return data
|
|
138
|
+ a += rot_size
|
|
139
|
+
|
|
140
|
+ return wave
|
142
|
141
|
|
143
|
142
|
def write_waveform(data, filename, samplerate):
|
144
|
143
|
with wave.open(filename, "w") as f:
|
|
@@ -166,22 +165,17 @@ def main():
|
166
|
165
|
help="Steps on interpolated paths. Defaults to 3.")
|
167
|
166
|
parser.add_argument("-r", "--rotate", dest="angle_d", default=0.0, type=float,
|
168
|
167
|
help="Angle to rotate image, in degrees. Defaults to 0 deg.")
|
|
168
|
+ parser.add_argument("-p", "--rot-steps", dest="rot_steps", default=0, type=int,
|
|
169
|
+ help="Number of steps to animate rotation. Defaults to 0.")
|
|
170
|
+ parser.add_argument("-z", "--rot-size", dest="rot_size", default=5.0, type=float,
|
|
171
|
+ help="Step size for rotation animation. Defaults to 5 deg.")
|
169
|
172
|
|
170
|
173
|
args = parser.parse_args()
|
171
|
174
|
print(args)
|
172
|
175
|
|
173
|
|
- wave = read_image(args.input, args.interpolate, args.volume, args.angle_d)
|
174
|
|
-
|
175
|
|
- samplecount = int(len(wave) / 2 / 2) # stereo, int16
|
176
|
|
- drawrate = args.samplerate / samplecount
|
177
|
|
- drawcount = drawrate * args.time
|
178
|
|
- print("len={} samples={} drawrate={:.2f} count={:.2f}".format(len(wave), samplecount, drawrate, drawcount))
|
179
|
|
-
|
180
|
|
- data = bytearray()
|
181
|
|
- for n in range(0, int(drawcount)):
|
182
|
|
- data.extend(wave)
|
183
|
|
- print("len={}".format(len(data)))
|
184
|
|
-
|
|
176
|
+ data = read_image(args.input, args.interpolate, args.volume,
|
|
177
|
+ args.samplerate, args.time,
|
|
178
|
+ args.angle_d, args.rot_steps, args.rot_size)
|
185
|
179
|
write_waveform(bytes(data), args.output, args.samplerate)
|
186
|
180
|
|
187
|
181
|
if __name__ == "__main__":
|