Browse Source

do rotation animation in script instead of makefile

Thomas Buck 2 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,11 +30,7 @@ output/%.wav: %.svg
30 30
 	./render.py -s 192000 -o $@ $<
31 31
 
32 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 35
 .PHONY: clean
40 36
 clean:

+ 53
- 59
render.py View File

@@ -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__":

Loading…
Cancel
Save