three::ThreeDView is Manim’s ThreeDScene. Its objects are 3D mobjects, rendered by
fframes::three_d into one image over the canvas, and fixed_in_frame draws 2D on top.
The camera is Manim’s: phi from the z axis, theta around it, focal_distance 20 and
zoom 1, so with the default phi 0 and theta -90° the xy plane lines up with
ui.space. ambient_rotation turns theta by that many radians per second, Manim’s
begin_ambient_camera_rotation. ThreeDView::projector(ui) maps Manim’s points to pixels
for labels on 3D points; ThreeDAxes draws its x, y and z that way.
Gallery scene: ThreeDView (a sphere and axes under a fixed title, with the camera turning).

ThreeDView 4 sThe mobjects keep Manim’s names, sizes and defaults: Surface (a checkerboard of
func(u, v)), ThreeDAxes (with c2p), Sphere, Cube, Prism, Cylinder, Cone,
Torus, Dot3D, Line3D, Arrow3D, Polyhedron, the four Platonic solids and
ConvexHull3D. Positions are Manim’s (x, y, z) in units, z out of the screen. Every
object takes enter: Some(Create { .. }.into()) to grow from its center or FadeIn to
fade in.
The scene is rebuilt for every frame. One checkerboard sphere at 1080p costs about 60 ms on
32 CPU threads; with fframes/wgpu the renderer uses the GPU.
use fframes_kit::prelude::*;
fn wave(u: f32, v: f32) -> (f32, f32, f32) { (u, v, 0.5 * u.sin() * v.cos())}
fn caption<'a>(ui: &mut Ui<'a, '_, '_>) -> Svgr<'a> { ui.show(Label { text: "A rotating surface".into(), x: 960.0, y: 120.0, direction: Anchor::Center, ..ui.defaults() })}
fn surface<'a>(ui: &mut Ui<'a, '_, '_>) -> Svgr<'a> { let objects: Vec<Box<dyn Object3D + 'a>> = vec![ Box::new(ThreeDAxes { ..ui.defaults() }), Box::new(Surface { func: wave, u_range: (-3.0, 3.0), v_range: (-3.0, 3.0), ..ui.defaults() }), Box::new(Sphere { center: (0.0, 0.0, 2.0), radius: 0.5, enter: Some(Create { ..ui.defaults() }.into()), ..ui.defaults() }), ]; ui.show(ThreeDView { phi: 75f32.to_radians(), theta: 30f32.to_radians(), ambient_rotation: 0.2, objects, fixed_in_frame: Some(children(caption)), ..ui.defaults() })}A vector in 3D coordinates
Section titled “A vector in 3D coordinates”Map both ends with c2p so the arrow follows the axes’ ranges and lengths. The camera
turns around the axes. Gallery scene: VectorCoordinates3D.

VectorCoordinates3D 4 suse fframes_kit::prelude::*;
fn vector<'a>(ui: &mut Ui<'a, '_, '_>) -> Svgr<'a> { let axes = ThreeDAxes { ..ui.defaults() }; let point = axes.c2p(3.0, 2.0, 2.0); let color = ui.theme.colors.highlight; let objects: Vec<Box<dyn Object3D + 'a>> = vec![ Box::new(axes), Box::new(Arrow3D { start: axes.c2p(0.0, 0.0, 0.0), end: point, color, enter: Some(Create { run_time: 1.0, ..ui.defaults() }.into()), ..ui.defaults() }), Box::new(Dot3D { point, color, radius: 0.12, ..ui.defaults() }), ]; ui.show(ThreeDView { phi: 75f32.to_radians(), theta: 30f32.to_radians(), ambient_rotation: 0.25, objects, ..ui.defaults() })}A paraboloid surface
Section titled “A paraboloid surface”Surface samples a function over two parameter ranges. Here u and v are the x and y
coordinates and the height is (u² + v²) / 4. Gallery scene: Paraboloid3D.

Paraboloid3D 4 suse fframes_kit::prelude::*;
fn paraboloid(u: f32, v: f32) -> (f32, f32, f32) { (u, v, 0.25 * (u * u + v * v))}
fn surface<'a>(ui: &mut Ui<'a, '_, '_>) -> Svgr<'a> { let objects: Vec<Box<dyn Object3D + 'a>> = vec![ Box::new(ThreeDAxes { show_labels: false, ..ui.defaults() }), Box::new(Surface { func: paraboloid, u_range: (-2.5, 2.5), v_range: (-2.5, 2.5), resolution: (24, 24), enter: Some(FadeIn { run_time: 1.0, ..ui.defaults() }.into()), ..ui.defaults() }), ]; ui.show(ThreeDView { phi: 75f32.to_radians(), theta: 30f32.to_radians(), ambient_rotation: 0.2, objects, ..ui.defaults() })}A sphere and torus
Section titled “A sphere and torus”Combine solids in one view and stagger their entrances. Torus lies around the z axis;
its major and minor radii set the ring and tube sizes. Gallery scene: SphereAndTorus3D.

SphereAndTorus3D 4 suse fframes_kit::prelude::*;
fn solids<'a>(ui: &mut Ui<'a, '_, '_>) -> Svgr<'a> { let objects: Vec<Box<dyn Object3D + 'a>> = vec![ Box::new(Sphere { radius: 0.9, fill_color: ui.theme.colors.highlight, enter: Some(Create { run_time: 0.8, ..ui.defaults() }.into()), ..ui.defaults() }), Box::new(Torus { major_radius: 2.1, minor_radius: 0.18, enter: Some(FadeIn { delay: 0.5, run_time: 0.8, ..ui.defaults() }.into()), ..ui.defaults() }), ]; ui.show(ThreeDView { phi: 75f32.to_radians(), theta: 30f32.to_radians(), ambient_rotation: 0.3, objects, ..ui.defaults() })}Physics: wave interference
Section titled “Physics: wave interference”With physics and 3d, put a RadialWave in a ThreeDView to show the changing
surface from two sources. Gallery scene: WaveInterference3D.

WaveInterference3D 4 suse fframes_kit::prelude::*;
fn interference<'a>(ui: &mut Ui<'a, '_, '_>) -> Svgr<'a> { let wave = RadialWave { sources: vec![(-1.2, 0.0), (1.2, 0.0)], wavelength: 1.0, speed: 1.2, amplitude: 0.3, front: false, x_range: (-4.0, 4.0), y_range: (-4.0, 4.0), ..ui.defaults() }; let objects: Vec<Box<dyn Object3D + 'a>> = vec![Box::new(wave)]; ui.show(ThreeDView { phi: 75f32.to_radians(), theta: 30f32.to_radians(), ambient_rotation: 0.15, objects, ..ui.defaults() })}Biology: a DNA sequence
Section titled “Biology: a DNA sequence”With biology and 3d, the bases build a double helix that rotates about its axis.
Gallery scene: DnaSequence3D.

DnaSequence3D 4 suse fframes_kit::prelude::*;
fn dna<'a>(ui: &mut Ui<'a, '_, '_>) -> Svgr<'a> { ui.show(DnaHelix3D { sequence: "GATTACAGATTACA".into(), width: 1100.0, height: 500.0, spin: 25.0, ..ui.defaults() })}Chemistry: a water molecule
Section titled “Chemistry: a water molecule”With chemistry and 3d, parse XYZ coordinates once and borrow the structure on each
frame. The first O–H bond lights up after one second. Gallery scene: WaterMolecule3D.

WaterMolecule3D 4 suse fframes_kit::prelude::*;
fn water<'a>(ui: &mut Ui<'a, '_, '_>, structure: &'a Structure3D) -> Svgr<'a> { ui.show(Molecule3D { structure: Some(structure), width: 1000.0, height: 700.0, spin: 25.0, highlight: vec![0], highlight_at: 1.0, ..ui.defaults() })}
fn water_structure() -> Result<Structure3D, XyzError> { Structure3D::from_xyz("3\nwater\nO 0 0 0.117\nH 0 0.757 -0.469\nH 0 -0.757 -0.469\n")}Components are plain structs. Set the fields you need and fill the rest with ..ui.defaults(): the defaults below come from the current theme (theme. values are fields of Theme).
ThreeDView
Section titled “ThreeDView”| Field | Type | Default | Description |
|---|---|---|---|
phi |
f32 |
0.0 |
Angle of the camera from the z axis in radians; 0 looks straight down on the xy plane. |
theta |
f32 |
-FRAC_PI_2 |
Angle of the camera around the z axis from the x axis in radians; -90 degrees is the default. |
distance |
f32 |
20.0 |
How far the camera sits from frame_center in Manim units; changes the perspective, not the lens. |
focal_distance |
f32 |
20.0 |
Distance in Manim units at which the lens shows the canvas’s height; the plane through frame_center lines up with ui.space when distance matches. |
zoom |
f32 |
1.0 |
Zoom of the lens; above 1 shows less of the scene. |
frame_center |
Point3 |
(0.0, 0.0, 0.0) |
The point the camera orbits and looks at, in Manim units (x, y, z). |
ambient_rotation |
f32 |
0.0 |
Radians per second theta turns; 0 keeps the camera still. |
objects |
Vec<Box<dyn Object3D>> |
Vec::new() |
The 3D objects drawn in the image, in order. |
fixed_in_frame |
Option<Children> |
None |
Drawn over the picture in canvas pixels, not moved by the camera (a closure taking the Ui); None for none. |
Methods:
fn camera(&self, space: Space, seconds: f32) -> Camera: The camerasecondsinto the view.fn projector(&self, ui: &Ui) -> Projector: The projector of the picture atui’s time.
Feature 3d · Source
