0
# Animation System
1
2
Animation framework for creating animated sequences of CAD assemblies with timeline-based keyframe animation. The system enables smooth movement, rotation, and transformation of CAD objects over time using three.js animation tracks.
3
4
## Capabilities
5
6
### Animation Class
7
8
Main class for creating and managing animations of CAD assemblies.
9
10
```python { .api }
11
class Animation:
12
"""
13
Create animations for CAD assemblies with support for both CadQuery and build123d assemblies.
14
15
The Animation class manages multiple animation tracks that can be applied to different
16
objects within an assembly, allowing complex coordinated movements.
17
"""
18
19
def __init__(self, assembly):
20
"""
21
Initialize animation for a CAD assembly.
22
23
Parameters:
24
assembly: CAD assembly object (CadQuery Assembly or build123d Assembly)
25
The assembly must contain named objects that can be referenced
26
by path strings in animation tracks.
27
28
Attributes:
29
tracks (list): List of animation tracks added to the animation
30
is_cadquery (bool): True if assembly is CadQuery-based
31
is_build123d (bool): True if assembly is build123d-based
32
paths (list): Available object paths within the assembly
33
"""
34
```
35
36
**Usage Examples:**
37
38
```python
39
import cadquery as cq
40
from cadquery import Assembly
41
from ocp_vscode import Animation, show
42
43
# Create CadQuery assembly
44
base = cq.Workplane().box(50, 50, 10)
45
arm = cq.Workplane().box(5, 30, 5).translate((0, 15, 7.5))
46
joint = cq.Workplane().cylinder(3, 8).translate((0, 30, 7.5))
47
48
assembly = Assembly()
49
assembly.add(base, name="base", color=cq.Color("gray"))
50
assembly.add(arm, name="arm", color=cq.Color("blue"))
51
assembly.add(joint, name="joint", color=cq.Color("red"))
52
53
# Create animation
54
anim = Animation(assembly)
55
56
# Show assembly first
57
show(assembly)
58
```
59
60
### Animation Track Management
61
62
Methods for adding and configuring animation tracks for individual objects.
63
64
```python { .api }
65
def add_track(self, path, action, times, values):
66
"""
67
Add an animation track for a specific object in the assembly.
68
69
Parameters:
70
path (str): Path or ID of the CAD object to animate.
71
For CadQuery: object name from assembly
72
For build123d: hierarchical path like "/top-level/sublevel/object"
73
74
action (str): Type of animation action to perform:
75
- "t": Position vector (3-dim array)
76
- "tx": Translation along x-axis (float distance)
77
- "ty": Translation along y-axis (float distance)
78
- "tz": Translation along z-axis (float distance)
79
- "q": Quaternion rotation (x,y,z,w tuple)
80
- "rx": Rotation around x-axis (degrees)
81
- "ry": Rotation around y-axis (degrees)
82
- "rz": Rotation around z-axis (degrees)
83
84
times (list): Array of time points (in seconds) when the object
85
should reach the corresponding values. Must be same
86
length as values array.
87
88
values (list): Array of target values corresponding to each time point.
89
Format depends on action type:
90
- "t": List of 3-tuples [(x,y,z), (x,y,z), ...]
91
- "tx"/"ty"/"tz": List of float distances [d1, d2, ...]
92
- "q": List of 4-tuples [(x,y,z,w), (x,y,z,w), ...]
93
- "rx"/"ry"/"rz": List of angles in degrees [a1, a2, ...]
94
95
Raises:
96
ValueError: If times and values have different lengths, or if path
97
doesn't exist in the assembly.
98
"""
99
100
def animate(self, speed):
101
"""
102
Send animation to viewer and start playback.
103
104
Parameters:
105
speed (float): Animation playback speed multiplier.
106
1.0 = normal speed, 2.0 = double speed, 0.5 = half speed
107
108
Note:
109
All tracks added via add_track() will be played simultaneously.
110
The viewer will interpolate between keyframes for smooth motion.
111
"""
112
```
113
114
**Usage Examples:**
115
116
```python
117
from ocp_vscode import Animation, show
118
import math
119
120
# Create assembly (using previous example)
121
anim = Animation(assembly)
122
123
# Rotating joint animation
124
rotation_times = [0.0, 1.0, 2.0, 3.0, 4.0]
125
rotation_angles = [0, 90, 180, 270, 360] # Full rotation over 4 seconds
126
127
anim.add_track("joint", "rz", rotation_times, rotation_angles)
128
129
# Arm swinging motion
130
swing_times = [0.0, 1.0, 2.0, 3.0, 4.0]
131
swing_angles = [0, 30, 0, -30, 0] # Swing back and forth
132
133
anim.add_track("arm", "ry", swing_times, swing_angles)
134
135
# Position translation of entire arm
136
move_times = [0.0, 2.0, 4.0]
137
positions = [
138
(0, 0, 0), # Start position
139
(20, 0, 10), # Move up and forward
140
(0, 0, 0) # Return to start
141
]
142
143
anim.add_track("arm", "t", move_times, positions)
144
145
# Start animation at normal speed
146
anim.animate(speed=1.0)
147
```
148
149
### Advanced Animation Patterns
150
151
Complex animation sequences can be created by combining multiple tracks and timing patterns.
152
153
```python
154
from ocp_vscode import Animation
155
import numpy as np
156
157
def create_sinusoidal_motion(duration=5.0, frequency=1.0, amplitude=10.0):
158
"""Create smooth sinusoidal motion pattern"""
159
160
# Generate time points (30 FPS equivalent)
161
times = np.linspace(0, duration, int(duration * 30))
162
163
# Calculate sinusoidal positions
164
positions = []
165
for t in times:
166
x = amplitude * np.sin(2 * np.pi * frequency * t)
167
y = amplitude * np.cos(2 * np.pi * frequency * t)
168
z = 5 * np.sin(4 * np.pi * frequency * t) # Vertical oscillation
169
positions.append((x, y, z))
170
171
return times.tolist(), positions
172
173
def create_orbital_motion(radius=20, height=0, duration=4.0):
174
"""Create circular orbital motion"""
175
176
times = np.linspace(0, duration, 60)
177
positions = []
178
179
for t in times:
180
angle = 2 * np.pi * t / duration
181
x = radius * np.cos(angle)
182
y = radius * np.sin(angle)
183
z = height
184
positions.append((x, y, z))
185
186
return times.tolist(), positions
187
188
# Usage with complex assembly
189
anim = Animation(mechanical_assembly)
190
191
# Planet-like orbital motion for satellite components
192
orbit_times, orbit_positions = create_orbital_motion(radius=30, duration=6.0)
193
anim.add_track("satellite", "t", orbit_times, orbit_positions)
194
195
# Sinusoidal oscillation for vibrating parts
196
sine_times, sine_positions = create_sinusoidal_motion(duration=6.0, frequency=2.0)
197
anim.add_track("vibrator", "t", sine_times, sine_positions)
198
199
# Synchronized rotation
200
rotation_times = np.linspace(0, 6.0, 60).tolist()
201
rotation_angles = [360 * t / 6.0 for t in rotation_times] # One full rotation
202
anim.add_track("main_shaft", "rz", rotation_times, rotation_angles)
203
204
anim.animate(speed=1.0)
205
```
206
207
### Assembly Path Resolution
208
209
Different assembly types use different path formats for object identification.
210
211
```python
212
# CadQuery Assembly paths
213
# Objects are referenced by their assigned names
214
assembly = Assembly()
215
assembly.add(part1, name="base")
216
assembly.add(part2, name="arm")
217
assembly.add(part3, name="joint")
218
219
anim = Animation(assembly)
220
anim.add_track("base", "rz", times, angles) # Reference by name
221
anim.add_track("arm", "t", times, positions) # Reference by name
222
223
# build123d Assembly paths
224
# Objects use hierarchical paths
225
anim.add_track("/main/arm/joint", "rx", times, angles) # Hierarchical path
226
anim.add_track("/base/motor/shaft", "rz", times, rotations) # Nested path
227
```
228
229
### Animation Timing and Interpolation
230
231
The animation system uses three.js keyframe interpolation for smooth motion between defined points.
232
233
```python
234
# Linear interpolation between keyframes
235
times = [0.0, 1.0, 2.0]
236
positions = [(0, 0, 0), (10, 0, 0), (10, 10, 0)]
237
# Object moves linearly: (0,0,0) -> (10,0,0) -> (10,10,0)
238
239
# Easing effects through keyframe density
240
# Dense keyframes at start/end = slow motion
241
# Sparse keyframes in middle = fast motion
242
ease_times = [0.0, 0.1, 0.2, 0.5, 1.8, 1.9, 2.0]
243
ease_angles = [0, 5, 15, 90, 345, 355, 360]
244
# Slow start, fast middle, slow end rotation
245
246
# Synchronization across multiple objects
247
sync_times = [0.0, 1.0, 2.0, 3.0]
248
249
# All objects move together
250
anim.add_track("part1", "tx", sync_times, [0, 10, 20, 30])
251
anim.add_track("part2", "ty", sync_times, [0, 5, 10, 15])
252
anim.add_track("part3", "rz", sync_times, [0, 90, 180, 270])
253
```
254
255
### Performance Considerations
256
257
Optimize animations for smooth playback and reasonable file sizes.
258
259
```python
260
# Optimal keyframe density
261
# Too few keyframes = jerky motion
262
# Too many keyframes = large data, possible lag
263
264
# Good: 30-60 keyframes for 5 second animation
265
duration = 5.0
266
frame_rate = 30 # FPS equivalent
267
times = np.linspace(0, duration, duration * frame_rate).tolist()
268
269
# Avoid: Excessive keyframe density
270
# bad_times = np.linspace(0, 5.0, 1000).tolist() # Overkill for 5 seconds
271
272
# Efficient value calculation
273
# Pre-calculate complex mathematical functions
274
angles = [360 * t / duration for t in times] # Pre-calculated rotation
275
positions = [(math.sin(t), math.cos(t), 0) for t in times] # Pre-calculated positions
276
277
# Memory-efficient track building
278
for i, (time, angle, pos) in enumerate(zip(times, angles, positions)):
279
if i == 0: # First track
280
anim.add_track("rotor", "rz", [time], [angle])
281
anim.add_track("slider", "t", [time], [pos])
282
else: # Extend existing tracks
283
# Note: This is conceptual - actual implementation adds complete tracks
284
pass
285
```
286
287
### Error Handling and Validation
288
289
The animation system validates track parameters and provides helpful error messages.
290
291
```python
292
from ocp_vscode import Animation
293
294
anim = Animation(assembly)
295
296
# Validation errors
297
try:
298
# Mismatched array lengths
299
anim.add_track("part", "tx", [0, 1, 2], [0, 10]) # Error: different lengths
300
except ValueError as e:
301
print(f"Track error: {e}")
302
303
try:
304
# Invalid path
305
anim.add_track("nonexistent_part", "rz", [0, 1], [0, 90]) # Error: path not found
306
except ValueError as e:
307
print(f"Path error: {e}")
308
309
# Valid track addition
310
anim.add_track("existing_part", "ry", [0.0, 1.0, 2.0], [0, 45, 90]) # Success
311
```