Browse Source

do rotation animation in script instead of makefile

Thomas Buck 10 months ago
parent
commit
b2ccaeda61
2 changed files with 54 additions and 64 deletions
  1. 1
    5
      Makefile
  2. 53
    59
      render.py

+ 1
- 5
Makefile View File

30
 	./render.py -s 192000 -o $@ $<
30
 	./render.py -s 192000 -o $@ $<
31
 
31
 
32
 output/%_rotate.wav: %.svg
32
 output/%_rotate.wav: %.svg
33
-	for number in {000..360..5} ; do \
34
-	   ./render.py -s 192000 -t 0.07 -o output/$(@F)_$$number.wav -r $$number $< ; \
35
-	done
36
-	ffmpeg -f concat -safe 0 -i <( for f in output/$(@F)_*.wav; do echo "file '$$(pwd)/$$f'"; done ) $@
37
-	rm output/$(@F)_*.wav
33
+	./render.py -s 192000 -p 72 -o $@ $<
38
 
34
 
39
 .PHONY: clean
35
 .PHONY: clean
40
 clean:
36
 clean:

+ 53
- 59
render.py View File

45
         p.append(v)
45
         p.append(v)
46
     return p
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
     paths, attributes = svg2paths(filename)
51
     paths, attributes = svg2paths(filename)
50
     print("paths={}".format(len(paths)))
52
     print("paths={}".format(len(paths)))
51
     for i, path in enumerate(paths):
53
     for i, path in enumerate(paths):
52
         print("path={} segments={}".format(i, len(path)))
54
         print("path={} segments={}".format(i, len(path)))
53
 
55
 
54
     # find center point to rotate around
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
     p_min = [p0[0], p0[1]]
58
     p_min = [p0[0], p0[1]]
57
     p_max = [p0[0], p0[1]]
59
     p_max = [p0[0], p0[1]]
58
     for path in paths:
60
     for path in paths:
59
         for segment in path:
61
         for segment in path:
60
-            p = [segment.end.real, segment.end.imag]
62
+            p = [ segment.end.real, segment.end.imag ]
61
             for i in range(0, 2):
63
             for i in range(0, 2):
62
                 if p[i] < p_min[i]:
64
                 if p[i] < p_min[i]:
63
                     p_min[i] = p[i]
65
                     p_min[i] = p[i]
67
 
69
 
68
     # find bounding box for rotated object
70
     # find bounding box for rotated object
69
     for path in paths:
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
         for segment in path:
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
                 for i in range(0, 2):
76
                 for i in range(0, 2):
83
                     if p[i] < p_min[i]:
77
                     if p[i] < p_min[i]:
84
                         p_min[i] = p[i]
78
                         p_min[i] = p[i]
85
                     if p[i] > p_max[i]:
79
                     if p[i] > p_max[i]:
86
                         p_max[i] = p[i]
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
     print("min={} max={}".format(p_min, p_max))
83
     print("min={} max={}".format(p_min, p_max))
99
     print("center={} ".format(p_center))
84
     print("center={} ".format(p_center))
100
 
85
 
101
-    data = bytearray()
102
-
103
-    def add_point(p):
86
+    def add_point(p, data):
104
         for i in range(0, 2):
87
         for i in range(0, 2):
105
             v = p[i]
88
             v = p[i]
106
             v -= p_min[i]
89
             v -= p_min[i]
110
             c = int((v * 2 - 1) * (32767 / 100 * volume_percent))
93
             c = int((v * 2 - 1) * (32767 / 100 * volume_percent))
111
             data.extend(c.to_bytes(2, byteorder="little", signed=True))
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
         l = math.sqrt((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2)
97
         l = math.sqrt((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2)
115
         ps = max(1, int(path_steps * l))
98
         ps = max(1, int(path_steps * l))
116
         for step in range(0, ps):
99
         for step in range(0, ps):
117
             p = interpolate(p1, p2, step / ps)
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
 def write_waveform(data, filename, samplerate):
142
 def write_waveform(data, filename, samplerate):
144
     with wave.open(filename, "w") as f:
143
     with wave.open(filename, "w") as f:
166
                         help="Steps on interpolated paths. Defaults to 3.")
165
                         help="Steps on interpolated paths. Defaults to 3.")
167
     parser.add_argument("-r", "--rotate", dest="angle_d", default=0.0, type=float,
166
     parser.add_argument("-r", "--rotate", dest="angle_d", default=0.0, type=float,
168
                         help="Angle to rotate image, in degrees. Defaults to 0 deg.")
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
     args = parser.parse_args()
173
     args = parser.parse_args()
171
     print(args)
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
     write_waveform(bytes(data), args.output, args.samplerate)
179
     write_waveform(bytes(data), args.output, args.samplerate)
186
 
180
 
187
 if __name__ == "__main__":
181
 if __name__ == "__main__":

Loading…
Cancel
Save