0
# Vector Fields
1
2
ManimGL provides comprehensive tools for visualizing vector fields, essential for physics simulations and mathematical demonstrations. The vector field system includes static field visualization, time-dependent fields, streamline generation, and animated flow visualization using numerical integration and sophisticated rendering techniques.
3
4
## Capabilities
5
6
### Static Vector Field Visualization
7
8
Create visual representations of vector fields as arrays of arrows showing direction and magnitude at sample points.
9
10
```python { .api }
11
class VectorField(VMobject):
12
def __init__(
13
self,
14
func: Callable[[VectArray], VectArray],
15
coordinate_system: CoordinateSystem,
16
density: float = 2.0,
17
magnitude_range: Optional[Tuple[float, float]] = None,
18
color: Optional[ManimColor] = None,
19
color_map_name: Optional[str] = "3b1b_colormap",
20
color_map: Optional[Callable[[Sequence[float]], Vect4Array]] = None,
21
stroke_opacity: float = 1.0,
22
stroke_width: float = 3,
23
tip_width_ratio: float = 4,
24
tip_len_to_width: float = 0.01,
25
max_vect_len: float | None = None,
26
max_vect_len_to_step_size: float = 0.8,
27
flat_stroke: bool = False,
28
norm_to_opacity_func=None,
29
**kwargs
30
):
31
"""
32
Create a visual vector field representation.
33
34
Parameters:
35
- func: Vectorized function mapping coordinates to vectors
36
- coordinate_system: Axes or NumberPlane for coordinate mapping
37
- density: Sampling density for vector placement
38
- magnitude_range: Optional range for magnitude scaling
39
- color_map_name: Built-in color map for magnitude coloring
40
- stroke_width: Arrow thickness
41
- tip_width_ratio: Arrow head width relative to shaft
42
- max_vect_len: Maximum display length for vectors
43
- max_vect_len_to_step_size: Scaling factor for vector length
44
"""
45
46
def update_vectors(self):
47
"""Recompute and redraw all vector arrows based on function."""
48
49
def set_sample_coords(self, sample_coords):
50
"""Update the grid of sample points for vector placement."""
51
52
def set_stroke_width(self, width):
53
"""Adjust arrow thickness with proper tip scaling."""
54
```
55
56
### Time-Dependent Vector Fields
57
58
Handle dynamic vector fields that evolve over time for physics simulations and animated demonstrations.
59
60
```python { .api }
61
class TimeVaryingVectorField(VectorField):
62
def __init__(
63
self,
64
time_func: Callable[[VectArray, float], VectArray],
65
coordinate_system: CoordinateSystem,
66
**kwargs
67
):
68
"""
69
Create a time-dependent vector field.
70
71
Parameters:
72
- time_func: Function taking (coordinates, time) returning vectors
73
- coordinate_system: Coordinate system for field mapping
74
- kwargs: Additional VectorField parameters
75
"""
76
77
def increment_time(self, dt):
78
"""Advance the internal time counter by dt."""
79
```
80
81
### Streamline Generation
82
83
Generate smooth curves that follow vector field flow using numerical integration for fluid dynamics and field line visualization.
84
85
```python { .api }
86
class StreamLines(VGroup):
87
def __init__(
88
self,
89
func: Callable[[VectArray], VectArray],
90
coordinate_system: CoordinateSystem,
91
density: float = 1.0,
92
n_repeats: int = 1,
93
noise_factor: float | None = None,
94
solution_time: float = 3,
95
dt: float = 0.05,
96
arc_len: float = 3,
97
max_time_steps: int = 200,
98
n_samples_per_line: int = 10,
99
cutoff_norm: float = 15,
100
stroke_width: float = 1.0,
101
stroke_color: ManimColor = WHITE,
102
stroke_opacity: float = 1,
103
color_by_magnitude: bool = True,
104
magnitude_range: Tuple[float, float] = (0, 2.0),
105
taper_stroke_width: bool = False,
106
color_map: str = "3b1b_colormap",
107
**kwargs
108
):
109
"""
110
Generate streamlines following vector field flow.
111
112
Parameters:
113
- func: Vector field function
114
- coordinate_system: Coordinate system for integration
115
- density: Sampling density for streamline starting points
116
- n_repeats: Multiple lines per sample point
117
- noise_factor: Random offset for varied starting points
118
- solution_time: Integration time duration
119
- dt: Time step for numerical integration
120
- max_time_steps: Maximum integration steps
121
- cutoff_norm: Stop integration if field norm exceeds this
122
- color_by_magnitude: Color lines by local field strength
123
- taper_stroke_width: Varying line thickness along streamline
124
"""
125
126
def draw_lines(self):
127
"""Use ODE solver to generate streamline paths."""
128
129
def get_sample_coords(self):
130
"""Get starting points with optional noise."""
131
```
132
133
### Animated Flow Visualization
134
135
Create animated streamlines with flowing effects to show dynamic field behavior and flow patterns.
136
137
```python { .api }
138
class AnimatedStreamLines(VGroup):
139
def __init__(
140
self,
141
stream_lines: StreamLines,
142
lag_range: float = 4,
143
rate_multiple: float = 1.0,
144
line_anim_config: dict = {"rate_func": linear, "time_width": 1.0},
145
**kwargs
146
):
147
"""
148
Animate streamlines with flowing effects.
149
150
Parameters:
151
- stream_lines: StreamLines object to animate
152
- lag_range: Stagger animation start times
153
- rate_multiple: Animation speed multiplier
154
- line_anim_config: Animation configuration for flow effects
155
"""
156
```
157
158
### Object Movement Along Fields
159
160
Utility functions for making objects follow vector field flow in real-time simulations.
161
162
```python { .api }
163
def move_along_vector_field(
164
mobject: Mobject,
165
func: Callable[[Vect3], Vect3]
166
) -> Mobject:
167
"""
168
Add updater to move mobject along vector field flow.
169
170
Parameters:
171
- mobject: Object to move
172
- func: Vector field function
173
174
Returns:
175
Modified mobject with movement updater
176
"""
177
178
def move_points_along_vector_field(
179
mobject: Mobject,
180
func: Callable,
181
coordinate_system: CoordinateSystem
182
) -> Mobject:
183
"""
184
Move all points of a mobject along vector field.
185
186
Parameters:
187
- mobject: Object whose points will move
188
- func: Vector field function
189
- coordinate_system: Coordinate system for mapping
190
191
Returns:
192
Modified mobject with point movement updater
193
"""
194
```
195
196
### Field Analysis Utilities
197
198
Helper functions for vector field analysis and coordinate system integration.
199
200
```python { .api }
201
def get_sample_coords(
202
coordinate_system: CoordinateSystem,
203
density: float = 1.0
204
) -> np.ndarray:
205
"""
206
Generate sample coordinates for vector field evaluation.
207
208
Parameters:
209
- coordinate_system: Coordinate system to sample
210
- density: Sampling density
211
212
Returns:
213
Array of coordinate points for field evaluation
214
"""
215
216
def vectorize(pointwise_function: Callable) -> Callable:
217
"""
218
Convert pointwise function to vectorized form for efficient evaluation.
219
220
Parameters:
221
- pointwise_function: Function operating on single points
222
223
Returns:
224
Vectorized function operating on arrays
225
"""
226
227
def ode_solution_points(
228
function,
229
state0,
230
time,
231
dt: float = 0.01
232
) -> np.ndarray:
233
"""
234
Solve ODE system to generate solution points.
235
236
Parameters:
237
- function: Derivative function for ODE system
238
- state0: Initial state
239
- time: Time duration for solution
240
- dt: Time step
241
242
Returns:
243
Array of solution points
244
"""
245
```
246
247
## Usage Examples
248
249
### Electromagnetic Field Visualization
250
251
```python
252
from manimlib import *
253
254
class ElectricField(Scene):
255
def construct(self):
256
plane = NumberPlane(x_range=[-4, 4], y_range=[-3, 3])
257
self.add(plane)
258
259
# Define electric field from point charges
260
def electric_field(coords):
261
x, y = coords.T
262
263
# Positive charge at (-1, 0)
264
r1_squared = (x + 1)**2 + y**2 + 0.1 # Small offset to avoid singularity
265
E1_x = (x + 1) / r1_squared**(3/2)
266
E1_y = y / r1_squared**(3/2)
267
268
# Negative charge at (1, 0)
269
r2_squared = (x - 1)**2 + y**2 + 0.1
270
E2_x = -(x - 1) / r2_squared**(3/2)
271
E2_y = -y / r2_squared**(3/2)
272
273
# Total field
274
Ex = E1_x + E2_x
275
Ey = E1_y + E2_y
276
277
return np.array([Ex, Ey]).T
278
279
# Create vector field
280
field = VectorField(
281
electric_field,
282
plane,
283
density=1.5,
284
stroke_width=2,
285
max_vect_len=0.6
286
)
287
288
# Add charges
289
positive_charge = Circle(radius=0.1, color=RED, fill_opacity=1)
290
negative_charge = Circle(radius=0.1, color=BLUE, fill_opacity=1)
291
positive_charge.move_to(plane.c2p(-1, 0))
292
negative_charge.move_to(plane.c2p(1, 0))
293
294
# Labels
295
plus_label = Text("+", font_size=24, color=WHITE).move_to(positive_charge)
296
minus_label = Text("−", font_size=24, color=WHITE).move_to(negative_charge)
297
298
self.play(ShowCreation(field), run_time=3)
299
self.play(
300
ShowCreation(positive_charge),
301
ShowCreation(negative_charge),
302
Write(plus_label),
303
Write(minus_label)
304
)
305
306
# Add field lines
307
field_lines = StreamLines(
308
electric_field,
309
plane,
310
density=0.8,
311
stroke_width=1.5,
312
color_by_magnitude=True
313
)
314
315
self.play(ShowCreation(field_lines), run_time=4)
316
self.wait(2)
317
```
318
319
### Fluid Flow Simulation
320
321
```python
322
class FluidFlow(Scene):
323
def construct(self):
324
plane = NumberPlane(x_range=[-3, 3], y_range=[-2, 2])
325
self.add(plane)
326
327
# Define fluid velocity field (vortex + uniform flow)
328
def velocity_field(coords):
329
x, y = coords.T
330
331
# Vortex component
332
vortex_x = -y
333
vortex_y = x
334
335
# Uniform flow component
336
uniform_x = np.ones_like(x) * 0.5
337
uniform_y = np.zeros_like(y)
338
339
# Combine components
340
vx = vortex_x + uniform_x
341
vy = vortex_y + uniform_y
342
343
return np.array([vx, vy]).T
344
345
# Create streamlines
346
streamlines = StreamLines(
347
velocity_field,
348
plane,
349
density=1.5,
350
n_repeats=2,
351
noise_factor=0.1,
352
stroke_width=2,
353
color_by_magnitude=True,
354
magnitude_range=(0, 3)
355
)
356
357
# Animate the flow
358
animated_lines = AnimatedStreamLines(
359
streamlines,
360
lag_range=2,
361
rate_multiple=1.5
362
)
363
364
self.add(animated_lines)
365
366
# Add some particles that follow the flow
367
particles = VGroup(*[
368
Dot(radius=0.05, color=YELLOW).move_to(
369
plane.c2p(
370
3 * (np.random.random() - 0.5),
371
2 * (np.random.random() - 0.5)
372
)
373
)
374
for _ in range(20)
375
])
376
377
# Make particles follow the field
378
for particle in particles:
379
move_along_vector_field(particle, lambda p: velocity_field(np.array([plane.p2c(p)[:2]]))[0])
380
381
self.add(particles)
382
self.wait(10)
383
```
384
385
### Phase Space Visualization
386
387
```python
388
class PhasePortrait(Scene):
389
def construct(self):
390
plane = NumberPlane(x_range=[-3, 3], y_range=[-3, 3])
391
plane.add_coordinates()
392
self.add(plane)
393
394
# Simple harmonic oscillator phase space
395
def harmonic_flow(coords):
396
x, y = coords.T # x = position, y = velocity
397
dx_dt = y # dx/dt = velocity
398
dy_dt = -x # dy/dt = -x (acceleration)
399
400
return np.array([dx_dt, dy_dt]).T
401
402
# Create vector field
403
field = VectorField(
404
harmonic_flow,
405
plane,
406
density=1.2,
407
stroke_width=1.5,
408
max_vect_len=0.4,
409
color_map_name="viridis"
410
)
411
412
# Create phase trajectories
413
trajectories = StreamLines(
414
harmonic_flow,
415
plane,
416
density=0.6,
417
stroke_width=2,
418
solution_time=2*PI, # One period
419
color_by_magnitude=False,
420
stroke_color=YELLOW
421
)
422
423
self.play(ShowCreation(field), run_time=2)
424
self.play(ShowCreation(trajectories), run_time=3)
425
426
# Add labels
427
x_label = Text("Position", font_size=24).next_to(plane.x_axis, DOWN)
428
y_label = Text("Velocity", font_size=24).next_to(plane.y_axis, LEFT)
429
y_label.rotate(PI/2)
430
431
title = Text("Harmonic Oscillator Phase Portrait", font_size=32).to_edge(UP)
432
433
self.play(Write(title), Write(x_label), Write(y_label))
434
self.wait()
435
```
436
437
### Time-Dependent Magnetic Field
438
439
```python
440
class TimeVaryingField(Scene):
441
def construct(self):
442
plane = NumberPlane(x_range=[-2, 2], y_range=[-2, 2])
443
self.add(plane)
444
445
# Time-varying magnetic field (rotating)
446
def rotating_field(coords, t):
447
x, y = coords.T
448
449
# Rotating uniform field
450
field_strength = 1.0
451
omega = 1.0 # Angular frequency
452
453
Bx = field_strength * np.cos(omega * t) * np.ones_like(x)
454
By = field_strength * np.sin(omega * t) * np.ones_like(y)
455
456
return np.array([Bx, By]).T
457
458
# Create time-varying field
459
field = TimeVaryingVectorField(
460
rotating_field,
461
plane,
462
density=1.5,
463
stroke_width=3,
464
max_vect_len=0.8
465
)
466
467
# Add field indicator
468
field_arrow = Arrow(ORIGIN, RIGHT, color=RED, buff=0)
469
field_arrow.to_edge(UP + RIGHT)
470
471
def update_indicator(arrow):
472
t = field.time
473
direction = np.array([np.cos(t), np.sin(t), 0])
474
arrow.become(Arrow(ORIGIN, direction, color=RED, buff=0))
475
arrow.to_edge(UP + RIGHT)
476
477
field_arrow.add_updater(update_indicator)
478
479
# Labels
480
title = Text("Rotating Magnetic Field", font_size=32).to_edge(UP + LEFT)
481
time_label = Text("t = 0.0", font_size=24).to_edge(DOWN + RIGHT)
482
483
def update_time_label(label):
484
t = field.time
485
label.become(Text(f"t = {t:.1f}", font_size=24))
486
label.to_edge(DOWN + RIGHT)
487
488
time_label.add_updater(update_time_label)
489
490
self.add(field, field_arrow, title, time_label)
491
self.wait(8) # Let field rotate
492
```
493
494
### Interactive Field Exploration
495
496
```python
497
from manimlib.mobject.interactive import LinearNumberSlider
498
499
class InteractiveField(Scene):
500
def setup(self):
501
# Create parameter controls
502
self.strength_slider = LinearNumberSlider(
503
value=1.0, min_value=0.1, max_value=3.0, step=0.1
504
)
505
self.rotation_slider = LinearNumberSlider(
506
value=0.0, min_value=-PI, max_value=PI, step=0.1
507
)
508
509
self.strength_slider.to_edge(DOWN).shift(UP * 0.5)
510
self.rotation_slider.to_edge(DOWN)
511
512
self.add(self.strength_slider, self.rotation_slider)
513
514
def construct(self):
515
plane = NumberPlane(x_range=[-2, 2], y_range=[-2, 2])
516
517
# Interactive field function
518
def interactive_field(coords):
519
x, y = coords.T
520
strength = self.strength_slider.get_value()
521
angle = self.rotation_slider.get_value()
522
523
# Rotated uniform field
524
cos_a, sin_a = np.cos(angle), np.sin(angle)
525
field_x = strength * cos_a * np.ones_like(x)
526
field_y = strength * sin_a * np.ones_like(y)
527
528
return np.array([field_x, field_y]).T
529
530
# Create responsive field
531
field = VectorField(
532
interactive_field,
533
plane,
534
density=1.5,
535
stroke_width=2
536
)
537
538
# Add updater to redraw field when parameters change
539
field.add_updater(lambda f: f.update_vectors())
540
541
# Labels
542
strength_label = Text("Field Strength", font_size=20)
543
rotation_label = Text("Field Rotation", font_size=20)
544
545
strength_label.next_to(self.strength_slider, LEFT)
546
rotation_label.next_to(self.rotation_slider, LEFT)
547
548
self.add(plane, field, strength_label, rotation_label)
549
self.wait(15) # Interactive exploration time
550
```
551
552
## Advanced Features
553
554
### Performance Optimization
555
556
```python
557
# Vectorized field functions for efficiency
558
def optimized_field(coords):
559
# Use numpy operations on entire arrays
560
x, y = coords.T
561
return np.stack([np.sin(x) * np.cos(y), np.cos(x) * np.sin(y)], axis=1)
562
563
# Custom color mapping for large fields
564
def custom_color_map(magnitudes):
565
# Efficient color mapping using numpy
566
normalized = magnitudes / np.max(magnitudes)
567
return plt.cm.plasma(normalized)
568
```
569
570
### Coordinate System Integration
571
572
```python
573
# Works with any coordinate system
574
polar_plane = PolarPlane()
575
field_polar = VectorField(radial_field, polar_plane)
576
577
# 3D coordinate systems
578
axes_3d = ThreeDAxes()
579
# Note: Vector fields are primarily 2D, but can work with projections
580
```
581
582
The vector field system in ManimGL provides comprehensive tools for visualizing mathematical fields and physics simulations, from static field visualization to complex time-dependent flow animations with numerical integration and sophisticated rendering.