0
# Camera System
1
2
Flexible camera system supporting 2D and 3D perspectives, camera movement, zooming, and multi-camera setups for complex visual presentations. Cameras control what portion of the mathematical space is rendered and how it appears on screen, providing the viewing framework for all Manim animations.
3
4
## Capabilities
5
6
### Base Camera
7
8
The fundamental camera class providing core rendering functionality and viewport management for 2D scenes.
9
10
```python { .api }
11
class Camera:
12
"""
13
Base camera class handling viewport management and rendering coordination.
14
15
Controls what region of mathematical space is visible, pixel resolution,
16
and rendering parameters for converting mathematical objects to screen pixels.
17
"""
18
19
def __init__(
20
background_image: str = None,
21
frame_center: np.ndarray = ORIGIN,
22
image_mode: str = "RGBA",
23
n_channels: int = 4,
24
pixel_array_dtype: str = "uint8",
25
cairo_line_width_multiple: float = 0.01,
26
use_z_index: bool = True,
27
background: np.ndarray = None,
28
pixel_height: int = None,
29
pixel_width: int = None,
30
frame_height: float = None,
31
frame_width: float = None,
32
frame_rate: float = None,
33
background_color: str = None,
34
background_opacity: float = None,
35
**kwargs
36
) -> None:
37
"""
38
Parameters:
39
- background_image: Path to background image file
40
- frame_center: Center point of camera viewport in scene coordinates
41
- image_mode: Color mode ("RGBA", "RGB", etc.)
42
- n_channels: Number of color channels
43
- pixel_array_dtype: Data type for pixel array ("uint8", "float64")
44
- cairo_line_width_multiple: Scaling factor for Cairo line rendering
45
- use_z_index: Whether to use z-index for depth sorting
46
- background: Custom background array
47
- pixel_height: Vertical resolution in pixels
48
- pixel_width: Horizontal resolution in pixels
49
- frame_height: Viewport height in Manim units
50
- frame_width: Viewport width in Manim units
51
- frame_rate: Animation frame rate
52
- background_color: Background color
53
- background_opacity: Background opacity
54
"""
55
56
# Core rendering methods
57
def capture_mobjects(
58
mobjects: list[Mobject],
59
**kwargs
60
) -> None:
61
"""
62
Render mobjects to the camera's pixel array.
63
64
Converts mathematical mobjects to screen pixels using appropriate
65
rendering methods for each mobject type.
66
67
Parameters:
68
- mobjects: List of mobjects to render
69
"""
70
71
def get_image(self) -> Image.Image:
72
"""
73
Get rendered image as PIL Image object.
74
75
Returns:
76
- Image.Image: PIL image containing rendered scene
77
"""
78
79
def get_pixel_array(self) -> np.ndarray:
80
"""
81
Get raw pixel array from camera buffer.
82
83
Returns:
84
- np.ndarray: Pixel array with shape (height, width, channels)
85
"""
86
87
def set_pixel_array(
88
pixel_array: np.ndarray,
89
convert_from_floats: bool = False
90
) -> None:
91
"""
92
Set camera's pixel array directly.
93
94
Parameters:
95
- pixel_array: New pixel array
96
- convert_from_floats: Whether to convert from float [0,1] to int [0,255]
97
"""
98
99
def reset(self) -> None:
100
"""Reset camera to initial state with clear background."""
101
102
def init_background(self) -> None:
103
"""Initialize background based on configuration settings."""
104
105
def resize_frame_shape(self, fixed_dimension: int = 0) -> None:
106
"""
107
Resize frame to maintain aspect ratio.
108
109
Parameters:
110
- fixed_dimension: Which dimension to keep fixed (0=width, 1=height)
111
"""
112
113
# Coordinate transformation methods
114
def transform_points_pre_display(
115
mobject: Mobject,
116
points: np.ndarray
117
) -> np.ndarray:
118
"""
119
Transform points before display rendering.
120
121
Apply camera transformations to convert scene coordinates
122
to screen coordinates.
123
124
Parameters:
125
- mobject: Mobject being transformed
126
- points: Points in scene coordinates
127
128
Returns:
129
- np.ndarray: Points in screen coordinates
130
"""
131
132
def points_to_pixel_coords(
133
points: np.ndarray
134
) -> np.ndarray:
135
"""
136
Convert scene points to pixel coordinates.
137
138
Parameters:
139
- points: Points in scene coordinates
140
141
Returns:
142
- np.ndarray: Points in pixel coordinates
143
"""
144
145
def adjust_out_of_range_points(
146
points: np.ndarray
147
) -> np.ndarray:
148
"""
149
Adjust points that fall outside visible range.
150
151
Parameters:
152
- points: Scene coordinate points
153
154
Returns:
155
- np.ndarray: Adjusted points within visible range
156
"""
157
158
# Properties for frame dimensions and positioning
159
@property
160
def frame_height() -> float:
161
"""Height of camera viewport in scene units."""
162
163
@property
164
def frame_width() -> float:
165
"""Width of camera viewport in scene units."""
166
167
@property
168
def frame_center() -> np.ndarray:
169
"""Center point of camera viewport."""
170
171
@property
172
def pixel_height() -> int:
173
"""Vertical resolution in pixels."""
174
175
@property
176
def pixel_width() -> int:
177
"""Horizontal resolution in pixels."""
178
179
@property
180
def background_color() -> str:
181
"""Background color of the camera."""
182
183
@property
184
def background_opacity() -> float:
185
"""Background opacity of the camera."""
186
```
187
188
### Moving Camera
189
190
Camera that can be moved, zoomed, and rotated during animations for dynamic perspective changes and cinematic effects.
191
192
```python { .api }
193
class MovingCamera(Camera):
194
"""
195
Camera that can move through the scene with animated frame adjustments.
196
197
Allows for dynamic camera movements including panning, zooming, and rotation
198
to create cinematic effects and follow animated objects.
199
"""
200
201
def __init__(
202
frame: Mobject = None,
203
fixed_dimension: int = 0,
204
default_frame_stroke_color: str = WHITE,
205
default_frame_stroke_width: float = 0,
206
**kwargs
207
) -> None:
208
"""
209
Parameters:
210
- frame: Mobject representing camera frame (usually ScreenRectangle)
211
- fixed_dimension: Which dimension to keep fixed during scaling (0=width, 1=height)
212
- default_frame_stroke_color: Default color for frame border
213
- default_frame_stroke_width: Default width for frame border
214
"""
215
216
# Frame manipulation methods
217
@property
218
def frame() -> Mobject:
219
"""Camera frame mobject that defines the viewport."""
220
221
def auto_zoom(
222
*mobjects: Mobject,
223
margin: float = 1,
224
only_mobjects_in_frame: bool = False,
225
animate: bool = False
226
) -> None:
227
"""
228
Automatically adjust camera to fit specified mobjects.
229
230
Parameters:
231
- *mobjects: Mobjects to fit in frame
232
- margin: Additional margin around mobjects
233
- only_mobjects_in_frame: Whether to consider only visible mobjects
234
- animate: Whether to animate the zoom
235
"""
236
237
def save_camera_position(self) -> None:
238
"""Save current camera position and zoom for later restoration."""
239
240
def restore_camera_position(self) -> None:
241
"""Restore previously saved camera position and zoom."""
242
243
# Animation-friendly properties
244
@property
245
def frame_height() -> float:
246
"""Height of camera frame (can be animated)."""
247
248
@frame_height.setter
249
def frame_height(value: float) -> None:
250
"""Set frame height (animatable)."""
251
252
@property
253
def frame_width() -> float:
254
"""Width of camera frame (can be animated)."""
255
256
@frame_width.setter
257
def frame_width(value: float) -> None:
258
"""Set frame width (animatable)."""
259
260
@property
261
def frame_center() -> np.ndarray:
262
"""Center of camera frame (can be animated)."""
263
264
@frame_center.setter
265
def frame_center(value: np.ndarray) -> None:
266
"""Set frame center (animatable)."""
267
268
# Coordinate transformation overrides
269
def points_to_pixel_coords(
270
points: np.ndarray
271
) -> np.ndarray:
272
"""Convert points to pixels accounting for frame movement."""
273
274
def adjust_out_of_range_points(
275
points: np.ndarray
276
) -> np.ndarray:
277
"""Adjust points based on current frame position."""
278
```
279
280
### Three-Dimensional Camera
281
282
Specialized camera for 3D scenes with perspective projection, camera orientation, and 3D lighting effects.
283
284
```python { .api }
285
class ThreeDCamera(Camera):
286
"""
287
Camera for 3D scenes with perspective projection and orientation control.
288
289
Provides full 3D camera functionality including perspective projection,
290
camera positioning in 3D space, and lighting for realistic 3D rendering.
291
"""
292
293
def __init__(
294
focal_distance: float = 20.0,
295
shading_factor: float = 0.2,
296
default_distance: float = 5.0,
297
light_source_start_point: np.ndarray = 9*DOWN + 7*LEFT + 10*OUT,
298
should_apply_shading: bool = True,
299
exponential_projection: bool = False,
300
phi: float = 0,
301
theta: float = -90*DEGREES,
302
gamma: float = 0,
303
zoom: float = 1,
304
**kwargs
305
) -> None:
306
"""
307
Parameters:
308
- focal_distance: Distance from camera to focal plane
309
- shading_factor: Intensity of 3D shading effects
310
- default_distance: Default z-distance for objects
311
- light_source_start_point: Initial position of light source
312
- should_apply_shading: Whether to apply 3D shading
313
- exponential_projection: Whether to use exponential perspective
314
- phi: Camera angle from z-axis (inclination) in radians
315
- theta: Camera angle from x-axis (azimuth) in radians
316
- gamma: Camera roll angle around viewing direction
317
- zoom: Camera zoom factor
318
"""
319
320
# 3D orientation and positioning
321
def set_euler_angles(
322
phi: float = None,
323
theta: float = None,
324
gamma: float = None
325
) -> None:
326
"""
327
Set camera orientation using Euler angles.
328
329
Parameters:
330
- phi: Inclination angle (angle from z-axis)
331
- theta: Azimuth angle (angle from x-axis)
332
- gamma: Roll angle (rotation around viewing direction)
333
"""
334
335
def get_euler_angles(self) -> tuple[float, float, float]:
336
"""
337
Get current camera orientation angles.
338
339
Returns:
340
- tuple: (phi, theta, gamma) angles in radians
341
"""
342
343
def set_focal_distance(self, distance: float) -> None:
344
"""
345
Set camera focal distance for perspective projection.
346
347
Parameters:
348
- distance: Distance from camera to focal plane
349
"""
350
351
def set_zoom(self, zoom_factor: float) -> None:
352
"""
353
Set camera zoom level.
354
355
Parameters:
356
- zoom_factor: Zoom multiplier (1.0 = default, >1.0 = zoom in)
357
"""
358
359
# 3D transformation methods
360
def get_rotation_matrix(self) -> np.ndarray:
361
"""
362
Get 3D rotation matrix for current camera orientation.
363
364
Returns:
365
- np.ndarray: 3x3 rotation matrix
366
"""
367
368
def reset_rotation_matrix(self) -> None:
369
"""Recalculate rotation matrix from current angles."""
370
371
def transform_points_pre_display(
372
mobject: Mobject,
373
points: np.ndarray
374
) -> np.ndarray:
375
"""
376
Apply 3D transformations including rotation and perspective projection.
377
378
Parameters:
379
- mobject: Mobject being transformed
380
- points: 3D points in scene coordinates
381
382
Returns:
383
- np.ndarray: 2D points after 3D projection
384
"""
385
386
def apply_3d_perspective(
387
points: np.ndarray
388
) -> np.ndarray:
389
"""
390
Apply perspective projection to 3D points.
391
392
Parameters:
393
- points: 3D points after rotation
394
395
Returns:
396
- np.ndarray: 2D points after perspective projection
397
"""
398
399
# Lighting and shading
400
def get_unit_normal(
401
points: np.ndarray,
402
face_indices: np.ndarray = None
403
) -> np.ndarray:
404
"""
405
Calculate unit normal vectors for 3D surfaces.
406
407
Parameters:
408
- points: 3D surface points
409
- face_indices: Indices defining surface faces
410
411
Returns:
412
- np.ndarray: Unit normal vectors
413
"""
414
415
def get_shading_factor(
416
points: np.ndarray,
417
unit_normal_vect: np.ndarray
418
) -> float:
419
"""
420
Calculate shading factor based on lighting angle.
421
422
Parameters:
423
- points: 3D surface points
424
- unit_normal_vect: Surface normal vectors
425
426
Returns:
427
- float: Shading factor [0, 1]
428
"""
429
430
# Object management for 3D scenes
431
def add_fixed_orientation_mobjects(
432
*mobjects: Mobject
433
) -> None:
434
"""
435
Add mobjects that maintain fixed orientation relative to camera.
436
437
These mobjects always face the camera regardless of camera rotation,
438
useful for text labels and 2D elements in 3D scenes.
439
440
Parameters:
441
- *mobjects: Mobjects to fix orientation
442
"""
443
444
def add_fixed_in_frame_mobjects(
445
*mobjects: Mobject
446
) -> None:
447
"""
448
Add mobjects that remain fixed in screen space.
449
450
These mobjects maintain fixed screen positions regardless of
451
camera movement, useful for UI elements and legends.
452
453
Parameters:
454
- *mobjects: Mobjects to fix in frame
455
"""
456
457
def remove_fixed_orientation_mobjects(
458
*mobjects: Mobject
459
) -> None:
460
"""Remove mobjects from fixed orientation list."""
461
462
def remove_fixed_in_frame_mobjects(
463
*mobjects: Mobject
464
) -> None:
465
"""Remove mobjects from fixed frame list."""
466
467
# Value trackers for smooth animation
468
def get_value_trackers(self) -> list[ValueTracker]:
469
"""
470
Get value trackers for animating camera parameters.
471
472
Returns:
473
- list[ValueTracker]: Trackers for phi, theta, focal_distance, gamma, zoom
474
"""
475
476
@property
477
def phi_tracker() -> ValueTracker:
478
"""Value tracker for phi (inclination) angle."""
479
480
@property
481
def theta_tracker() -> ValueTracker:
482
"""Value tracker for theta (azimuth) angle."""
483
484
@property
485
def gamma_tracker() -> ValueTracker:
486
"""Value tracker for gamma (roll) angle."""
487
488
@property
489
def focal_distance_tracker() -> ValueTracker:
490
"""Value tracker for focal distance."""
491
492
@property
493
def zoom_tracker() -> ValueTracker:
494
"""Value tracker for zoom level."""
495
```
496
497
### Specialized Camera Types
498
499
Advanced camera classes for specific use cases and complex rendering scenarios.
500
501
```python { .api }
502
class MultiCamera(Camera):
503
"""
504
Camera supporting multiple simultaneous viewpoints.
505
506
Enables split-screen effects, picture-in-picture, and other
507
multi-viewport visualizations.
508
"""
509
510
def __init__(
511
*cameras: Camera,
512
**kwargs
513
) -> None:
514
"""
515
Parameters:
516
- *cameras: Sub-cameras for different viewports
517
"""
518
519
def capture_mobjects(
520
mobjects: list[Mobject],
521
**kwargs
522
) -> None:
523
"""Render scene using all sub-cameras simultaneously."""
524
525
def set_split_positions(
526
positions: list[tuple[float, float, float, float]]
527
) -> None:
528
"""
529
Set viewport positions for each sub-camera.
530
531
Parameters:
532
- positions: List of (x, y, width, height) for each viewport
533
"""
534
535
class MappingCamera(Camera):
536
"""
537
Camera with coordinate space transformations and mappings.
538
539
Allows for non-linear coordinate transformations and custom
540
mathematical space mappings.
541
"""
542
543
def __init__(
544
mapping_func: Callable[[np.ndarray], np.ndarray] = None,
545
min_anchor_points: int = 50,
546
max_anchor_points: int = 100,
547
**kwargs
548
) -> None:
549
"""
550
Parameters:
551
- mapping_func: Function to transform coordinates
552
- min_anchor_points: Minimum points for mapping interpolation
553
- max_anchor_points: Maximum points for mapping interpolation
554
"""
555
556
def points_to_pixel_coords(
557
points: np.ndarray
558
) -> np.ndarray:
559
"""Apply coordinate mapping before pixel conversion."""
560
561
class SplitScreenCamera(MultiCamera):
562
"""
563
Camera for split-screen presentations with automatic layout.
564
565
Convenient wrapper for common split-screen configurations
566
with automatic viewport management.
567
"""
568
569
def __init__(
570
left_camera: Camera,
571
right_camera: Camera,
572
split_ratio: float = 0.5,
573
**kwargs
574
) -> None:
575
"""
576
Parameters:
577
- left_camera: Camera for left viewport
578
- right_camera: Camera for right viewport
579
- split_ratio: Horizontal split position (0.5 = center)
580
"""
581
```
582
583
## Usage Examples
584
585
### Basic Camera Movement
586
587
```python
588
from manim import *
589
590
class CameraMovementExample(MovingCameraScene):
591
def construct(self):
592
# Create objects
593
square = Square(color=BLUE)
594
circle = Circle(color=RED).shift(RIGHT * 4)
595
596
self.add(square, circle)
597
598
# Move camera to focus on square
599
self.play(
600
self.camera.frame.animate.move_to(square).scale(0.5)
601
)
602
self.wait(1)
603
604
# Move camera to circle
605
self.play(
606
self.camera.frame.animate.move_to(circle)
607
)
608
self.wait(1)
609
610
# Zoom out to show both
611
self.play(
612
self.camera.frame.animate.move_to(ORIGIN).scale(2)
613
)
614
```
615
616
### 3D Camera Control
617
618
```python
619
class ThreeDCameraExample(ThreeDScene):
620
def construct(self):
621
# Set initial camera orientation
622
self.set_camera_orientation(phi=75*DEGREES, theta=45*DEGREES)
623
624
# Create 3D objects
625
axes = ThreeDAxes()
626
cube = Cube(side_length=2, fill_opacity=0.7)
627
sphere = Sphere(radius=1.2).shift(UP * 2)
628
629
self.add(axes, cube, sphere)
630
631
# Animate camera rotation
632
self.play(
633
self.camera.phi_tracker.animate.set_value(30*DEGREES),
634
self.camera.theta_tracker.animate.set_value(60*DEGREES),
635
run_time=3
636
)
637
638
# Move camera position
639
self.play(
640
self.camera.frame_center.animate.shift(RIGHT * 2),
641
self.camera.zoom_tracker.animate.set_value(1.5),
642
run_time=2
643
)
644
645
# Add fixed orientation text
646
text = Text("3D Scene", font_size=48)
647
self.add_fixed_in_frame_mobjects(text)
648
text.to_corner(UL)
649
```
650
651
### Auto-Zoom Functionality
652
653
```python
654
class AutoZoomExample(MovingCameraScene):
655
def construct(self):
656
# Create scattered objects
657
dots = VGroup(*[
658
Dot(np.random.uniform(-6, 6, 3))
659
for _ in range(10)
660
])
661
662
self.add(dots)
663
664
# Auto-zoom to fit all dots
665
self.camera.auto_zoom(*dots, margin=1, animate=True)
666
self.wait(2)
667
668
# Focus on subset
669
subset = dots[:3]
670
self.camera.auto_zoom(*subset, margin=0.5, animate=True)
671
self.wait(2)
672
```
673
674
### Multi-Camera Setup
675
676
```python
677
class MultiCameraExample(Scene):
678
def setup(self):
679
# Create split-screen camera
680
left_camera = Camera()
681
right_camera = Camera()
682
683
self.camera = SplitScreenCamera(
684
left_camera,
685
right_camera,
686
split_ratio=0.6
687
)
688
689
def construct(self):
690
# Content will appear in both viewports
691
circle = Circle(color=BLUE)
692
square = Square(color=RED)
693
694
self.add(circle, square)
695
```
696
697
### Advanced 3D Camera Animation
698
699
```python
700
class Advanced3DCameraExample(ThreeDScene):
701
def construct(self):
702
# Create complex 3D scene
703
axes = ThreeDAxes()
704
surface = Surface(
705
lambda u, v: [u, v, u**2 + v**2],
706
u_range=[-2, 2],
707
v_range=[-2, 2],
708
resolution=16,
709
fill_opacity=0.8
710
)
711
712
self.add(axes, surface)
713
714
# Set initial position
715
self.set_camera_orientation(phi=60*DEGREES, theta=30*DEGREES)
716
717
# Create orbital camera movement
718
def orbit_camera():
719
self.begin_ambient_camera_rotation(rate=0.1)
720
721
# Add light tracking
722
self.camera.light_source.move_to([5, 5, 5])
723
724
# Animate camera parameters simultaneously
725
self.play(
726
self.camera.phi_tracker.animate.set_value(90*DEGREES),
727
self.camera.zoom_tracker.animate.set_value(0.8),
728
run_time=4
729
)
730
731
# Start orbital motion
732
orbit_camera()
733
self.wait(6)
734
self.stop_ambient_camera_rotation()
735
```
736
737
### Camera Coordinate Transformations
738
739
```python
740
class CoordinateTransformExample(Scene):
741
def construct(self):
742
# Show how camera transforms coordinates
743
axes = Axes()
744
point_in_scene = np.array([2, 1, 0])
745
746
# Convert to pixel coordinates
747
pixel_coords = self.camera.points_to_pixel_coords(
748
np.array([point_in_scene])
749
)[0]
750
751
# Create visual indicators
752
scene_dot = Dot(point_in_scene, color=RED)
753
pixel_text = Text(
754
f"Pixel: ({pixel_coords[0]:.0f}, {pixel_coords[1]:.0f})",
755
font_size=24
756
).next_to(scene_dot, UP)
757
758
coord_text = Text(
759
f"Scene: ({point_in_scene[0]}, {point_in_scene[1]})",
760
font_size=24
761
).next_to(scene_dot, DOWN)
762
763
self.add(axes, scene_dot, pixel_text, coord_text)
764
```