# ThreeDView

> three::ThreeDView is Manim's ThreeDScene.

Page: https://fframes-kit.pages.dev/docs/three-d/three-d-view/
Cargo feature: `3d` (`fframes-kit = { version = "..", features = ["3d"] }`)

`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).

Gallery clip of `ThreeDView`: https://fframes-kit.pages.dev/media/scenes/ThreeDView-f14d51c7.mp4

The 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.

```rust
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

Map both ends with `c2p` so the arrow follows the axes' ranges and lengths. The camera
turns around the axes. Gallery scene: `VectorCoordinates3D`.

Gallery clip of `VectorCoordinates3D`: https://fframes-kit.pages.dev/media/scenes/VectorCoordinates3D-5f60f33f.mp4

```rust
use 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

`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`.

Gallery clip of `Paraboloid3D`: https://fframes-kit.pages.dev/media/scenes/Paraboloid3D-25509ddd.mp4

```rust
use 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

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`.

Gallery clip of `SphereAndTorus3D`: https://fframes-kit.pages.dev/media/scenes/SphereAndTorus3D-7f37a12a.mp4

```rust
use 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

With `physics` and `3d`, put a `RadialWave` in a `ThreeDView` to show the changing
surface from two sources. Gallery scene: `WaveInterference3D`.

Gallery clip of `WaveInterference3D`: https://fframes-kit.pages.dev/media/scenes/WaveInterference3D-0f86c0ae.mp4

```rust
use 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

With `biology` and `3d`, the bases build a double helix that rotates about its axis.
Gallery scene: `DnaSequence3D`.

Gallery clip of `DnaSequence3D`: https://fframes-kit.pages.dev/media/scenes/DnaSequence3D-6c7c138d.mp4

```rust
use 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

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`.

Gallery clip of `WaterMolecule3D`: https://fframes-kit.pages.dev/media/scenes/WaterMolecule3D-7122e8aa.mp4

```rust
use 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")
}
```

## API

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

| 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 camera `seconds` into the view.
- `fn projector(&self, ui: &Ui) -> Projector`: The projector of the picture at `ui`'s time.

Feature `3d` · [Source](https://github.com/khanhthanhdev/fframes-kit/blob/main/fframes-kit/src/three/view.rs#L53)
