Skip to main content

Project

Struct Project 

Source
pub struct Project {
Show 14 fields pub width: u32, pub height: u32, pub fps: u32, pub title: String, pub scene: BaseScene, pub output_path: PathBuf, pub use_cache: bool, pub use_ffmpeg: bool, pub use_gpu: bool, pub background_color: Color, pub close_on_finish: bool, pub current_time: Duration, pub paused: bool, pub speed: f32,
}

Fields§

§width: u32§height: u32§fps: u32§title: String§scene: BaseScene§output_path: PathBuf§use_cache: bool§use_ffmpeg: bool§use_gpu: bool§background_color: Color§close_on_finish: bool§current_time: Duration§paused: bool§speed: f32

Implementations§

Source§

impl Project

Source

pub fn new(width: u32, height: u32) -> Self

Source§

impl Project

Source

pub fn with_fps(self, fps: u32) -> Self

Examples found in repository?
examples/color_interpolation.rs (line 7)
4fn main() {
5    let mut project = Project::default()
6        .with_dimensions(300, 300)
7        .with_fps(60)
8        .with_title("Color Interpolation")
9        .with_cache(true)
10        .close_on_finish();
11
12    let circle = Circle::default()
13        .with_position(Vec2::new(150.0, 150.0))
14        .with_radius(50.0)
15        .with_fill(Color::RED); // Red
16
17    project.scene.add(Box::new(circle.clone()));
18
19    let duration = Duration::from_secs(1);
20
21    project.scene.video_timeline.add(loop_anim(
22        move || {
23            chain![
24                circle.fill_color.to(Color::YELLOW, duration),
25                circle.fill_color.to(Color::GREEN, duration),
26                circle.fill_color.to(Color::BLUE, duration),
27                circle.fill_color.to(Color::RED, duration),
28            ]
29        },
30        None,
31    ));
32
33    // Show
34    project.show().expect("Failed to render");
35}
More examples
Hide additional examples
examples/math_animation.rs (line 7)
4fn main() {
5    // 1. Initialize Project
6    let mut project = Project::default()
7        .with_fps(60)
8        .with_dimensions(500, 500)
9        .with_title("Math Animation")
10        .close_on_finish();
11
12    // 2. Create MathNode (Typst syntax)
13    let tex = MathNode::default()
14        .with_position(Vec2::new(250.0, 250.0))
15        .with_equation("y = a x^2")
16        .with_font_size(48.0)
17        .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2));
18    project.scene.add(Box::new(tex.clone()));
19
20    // 3. Define Animation Sequence
21    project.scene.video_timeline.add(loop_anim(
22        move || {
23            chain![
24                tex.tex("y = a x^2 + b x", Duration::from_secs(1)),
25                tex.tex("e^(i pi) + 1 = 0", Duration::from_secs(1)),
26                tex.tex("y = a x^2", Duration::from_secs(1)),
27            ]
28        },
29        None,
30    ));
31
32    // 4. Run interactive preview
33    project.show().expect("Failed to render");
34}
examples/getting_started.rs (line 7)
4fn main() {
5    // 1. Initialize the Project
6    let mut project = Project::default()
7        .with_fps(60)
8        .with_cache(true)
9        .with_title("Getting Started")
10        .close_on_finish();
11
12    // 2. Define Nodes
13    let circle = Circle::default()
14        .with_position(Vec2::new(400.0, 300.0))
15        .with_radius(50.0)
16        .with_fill(Color::rgb8(0xe1, 0x32, 0x38)); // Red
17
18    let text = TextNode::default()
19        .with_position(Vec2::new(400.0, 450.0))
20        .with_text("Hello Rust")
21        .with_font_size(40.0)
22        .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White-ish
23
24    // 3. Add Nodes to the Scene
25    project.scene.add(Box::new(circle.clone()));
26    project.scene.add(Box::new(text.clone()));
27
28    // 4. Add Animations to the Timeline
29    project.scene.video_timeline.add(all![
30        circle.radius.to(100.0, Duration::from_secs(1)),
31        text.position
32            .to(Vec2::new(400.0, 500.0), Duration::from_secs(1)),
33    ]);
34
35    // 5. Show
36    project.show().expect("Failed to render");
37}
examples/export.rs (line 7)
4fn main() {
5    // 1. Initialize for Export
6    let mut project = Project::default()
7        .with_fps(30)
8        .with_ffmpeg(true)
9        .with_title("Export")
10        .with_output_path("output")
11        .close_on_finish();
12
13    // 2. Setup Nodes
14    let circle = Circle::default()
15        .with_position(Vec2::new(400.0, 300.0))
16        .with_radius(50.0)
17        .with_fill(Color::rgb8(0x68, 0xab, 0xdf)); // Blue
18
19    let text = TextNode::default()
20        .with_position(Vec2::new(400.0, 50.0))
21        .with_text("Export Demo")
22        .with_font_size(40.0)
23        .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White
24
25    project.scene.add(Box::new(circle.clone()));
26    project.scene.add(Box::new(text.clone()));
27
28    // 3. Define Animations (Color and Font Size)
29    project.scene.video_timeline.add(all![
30        // Circle color and size
31        circle
32            .fill_color
33            .to(Color::rgb8(0xf2, 0xf2, 0xf2), Duration::from_secs(2))
34            .ease(easings::quad_in_out),
35        circle
36            .radius
37            .to(150.0, Duration::from_secs(2))
38            .ease(easings::elastic_out),
39        // Text font size
40        text.font_size
41            .to(50.0, Duration::from_secs(2))
42            .ease(easings::cubic_out),
43    ]);
44
45    // 4. Export (Renders frames and combines them into out.mkv)
46    println!("Starting export to {}...", project.output_path.display());
47    project.export().expect("Failed to export");
48}
examples/math_code.rs (line 6)
4fn main() {
5    let mut project = Project::default()
6        .with_fps(120)
7        .with_title("Math Code")
8        .close_on_finish();
9
10    let triangle = Polygon::default()
11        .with_position(Vec2::ZERO)
12        .with_anchor(Vec2::new(-1.0, -1.0))
13        .with_points(vec![
14            Vec2::new(0.0, 0.0),
15            Vec2::new(0.0, 200.0),
16            Vec2::new(200.0, 200.0),
17        ])
18        .with_fill(Color::rgb8(0x68, 0xab, 0xdf));
19
20    let lines = vec![
21        Line::default()
22            .with_start(Vec2::new(0.0, 0.0))
23            .with_end(Vec2::new(0.0, 200.0))
24            .with_stroke(Color::WHITE, 10.0),
25        Line::default()
26            .with_start(Vec2::new(0.0, 0.0))
27            .with_end(Vec2::new(200.0, 200.0))
28            .with_stroke(Color::WHITE, 10.0),
29        Line::default()
30            .with_start(Vec2::new(0.0, 200.0))
31            .with_end(Vec2::new(200.0, 200.0))
32            .with_stroke(Color::WHITE, 10.0),
33    ];
34
35    let triangle_line_group = GroupNode::default()
36        .with_nodes(vec![
37            triangle.clone_node(),
38            lines[0].clone_node(),
39            lines[1].clone_node(),
40            lines[2].clone_node(),
41        ])
42        .with_position(Vec2::new(400.0, 200.0))
43        .with_scale(0.0);
44
45    let pytagorean_theorem = MathNode::default()
46        .with_position(Vec2::new(150.0, 150.0))
47        .with_equation("a^2 + b^2 = c^2")
48        .with_font_size(10.0)
49        .with_fill(Color::GRAY)
50        .with_opacity(0.0);
51
52    let text_a = TextNode::default()
53        .with_text("a")
54        .with_fill(Color::GRAY)
55        .with_opacity(0.0)
56        .with_scale(0.7)
57        .with_position(Vec2::new(370.0, 300.0));
58
59    let text_b = TextNode::default()
60        .with_text("b")
61        .with_fill(Color::GRAY)
62        .with_opacity(0.0)
63        .with_scale(0.7)
64        .with_position(Vec2::new(500.0, 430.0));
65
66    let text_c = TextNode::default()
67        .with_text("c")
68        .with_fill(Color::GRAY)
69        .with_opacity(0.0)
70        .with_scale(0.7)
71        .with_position(Vec2::new(510.0, 260.0));
72
73    project.scene.add(Box::new(triangle_line_group.clone()));
74    project.scene.add(Box::new(pytagorean_theorem.clone()));
75    project.scene.add(Box::new(text_a.clone()));
76    project.scene.add(Box::new(text_b.clone()));
77    project.scene.add(Box::new(text_c.clone()));
78
79    project.scene.video_timeline.add(all![
80        all![triangle_line_group
81            .scale
82            .to(Vec2::ONE, Duration::from_secs(1)),],
83        all![
84            pytagorean_theorem.opacity.to(1.0, Duration::from_secs(1)),
85            pytagorean_theorem
86                .font_size
87                .to(32.0, Duration::from_secs(1)),
88        ],
89        sequence![
90            Duration::from_millis(300),
91            text_a.scale.to(Vec2::new(1.0, 1.0), Duration::from_secs(1)),
92            text_b.scale.to(Vec2::new(1.0, 1.0), Duration::from_secs(1)),
93            text_c.scale.to(Vec2::new(1.0, 1.0), Duration::from_secs(1)),
94            text_a.opacity.to(1.0, Duration::from_secs(1)),
95            text_b.opacity.to(1.0, Duration::from_secs(1)),
96            text_c.opacity.to(1.0, Duration::from_secs(1))
97        ]
98    ]);
99
100    project.show().expect("Failed to render");
101}
examples/advanced_flow.rs (line 7)
4fn main() {
5    // 1. Initialize Project with full API coverage
6    let mut project = Project::default()
7        .with_fps(120)
8        .with_gpu(true)
9        .with_cache(true)
10        .with_ffmpeg(true)
11        .with_output_path("output")
12        .with_title("Advanced Flow")
13        .close_on_finish();
14
15    // 2. Setup Nodes
16    let mut path = BezPath::new();
17    path.move_to((100.0, 300.0));
18    path.curve_to((250.0, 100.0), (550.0, 500.0), (700.0, 300.0));
19    let path_node = PathNode::default()
20        .with_path(path)
21        .with_stroke(Color::rgb8(0x44, 0x44, 0x44), 2.0);
22    let follower = Circle::default()
23        .with_position(Vec2::new(100.0, 300.0))
24        .with_radius(20.0)
25        .with_fill(Color::rgb8(0xe1, 0x32, 0x38)); // Red
26
27    // Showcase: Rect and Line
28    let background_rect = Rect::default()
29        .with_position(Vec2::new(400.0, 300.0))
30        .with_size(Vec2::new(760.0, 560.0))
31        .with_fill(Color::rgba8(0x33, 0x33, 0x33, 150))
32        .with_radius(20.0);
33
34    let divider_line = Line::default()
35        .with_start(Vec2::new(0.0, 300.0))
36        .with_end(Vec2::new(0.0, 300.0))
37        .with_stroke(Color::rgb8(0x44, 0x44, 0x44), 1.0);
38    let title_text = TextNode::default()
39        .with_position(Vec2::new(50.0, 50.0))
40        .with_anchor(Vec2::new(-1.0, -1.0))
41        .with_text("Motion Canvas in Rust")
42        .with_font_size(40.0)
43        .with_fill(Color::rgb8(0x68, 0xab, 0xdf)) // Blue
44        .with_font("JetBrains Mono");
45
46    let code_block = CodeNode::default()
47        .with_anchor(Vec2::new(-1.0, -1.0))
48        .with_position(Vec2::new(50.0, 415.0))
49        .with_language("rust")
50        .with_opacity(0.0);
51
52    let math_eq = MathNode::default()
53        .with_position(Vec2::new(150.0, 150.0))
54        .with_equation("f(x) = sin(x)")
55        .with_font_size(30.0)
56        .with_fill(Color::rgb8(0xe6, 0xa7, 0x00)); // Yellow
57
58    let logo = ImageNode::default()
59        .with_position(Vec2::new(650.0, 150.0))
60        .with_path("./examples/images/motion-canvas-logo.png")
61        .with_size(Vec2::new(150.0, 150.0));
62
63    project.scene.video_timeline.add(all![
64        // Show code
65        chain![
66            code_block.opacity.to(1.0, Duration::from_secs(1)),
67            code_block.append("fn main() {\n", Duration::from_secs(1)),
68            code_block.append(
69                "    let mut engine = MotionCanvas::new();\n",
70                Duration::from_secs(1)
71            ),
72            code_block.append("    engine.render();\n", Duration::from_secs(1)),
73            code_block.append("}", Duration::from_secs(1)),
74            // Staggered appearance of nodes
75            sequence![
76                Duration::from_millis(200),
77                divider_line
78                    .end
79                    .to(Vec2::new(800.0, 300.0), Duration::from_secs(1))
80                    .ease(easings::cubic_out),
81                follower
82                    .radius
83                    .to(30.0, Duration::from_millis(500))
84                    .ease(easings::elastic_out),
85            ],
86            // The path follow combined with a "race" logic
87            any![
88                follower
89                    .position
90                    .follow(&path_node, Duration::from_secs(3))
91                    .ease(easings::cubic_in_out),
92                // Race: if this 'wait' finishes first, the follow is done
93                wait(Duration::from_secs(4)),
94            ],
95            // Final flourishes using different easings
96            all![
97                follower
98                    .radius
99                    .to(10.0, Duration::from_secs(1))
100                    .ease(easings::quad_out),
101                divider_line
102                    .start
103                    .to(Vec2::new(400.0, 300.0), Duration::from_secs(1))
104                    .ease(easings::cubic_in),
105            ]
106        ]
107    ]);
108
109    // 4. Build Scene
110    project.scene.add(Box::new(background_rect.clone()));
111    project.scene.add(Box::new(divider_line.clone()));
112    project.scene.add(Box::new(path_node.clone()));
113    project.scene.add(Box::new(follower.clone()));
114    project.scene.add(Box::new(title_text.clone()));
115    project.scene.add(Box::new(code_block.clone()));
116    project.scene.add(Box::new(math_eq.clone()));
117    project.scene.add(Box::new(logo.clone()));
118
119    // 5. Run (Choose show() for interactive or export() for PNGs)
120    project.show().expect("Failed to render");
121    // project.export().expect("Failed to export");
122}
Source

pub fn with_dimensions(self, width: u32, height: u32) -> Self

Examples found in repository?
examples/color_interpolation.rs (line 6)
4fn main() {
5    let mut project = Project::default()
6        .with_dimensions(300, 300)
7        .with_fps(60)
8        .with_title("Color Interpolation")
9        .with_cache(true)
10        .close_on_finish();
11
12    let circle = Circle::default()
13        .with_position(Vec2::new(150.0, 150.0))
14        .with_radius(50.0)
15        .with_fill(Color::RED); // Red
16
17    project.scene.add(Box::new(circle.clone()));
18
19    let duration = Duration::from_secs(1);
20
21    project.scene.video_timeline.add(loop_anim(
22        move || {
23            chain![
24                circle.fill_color.to(Color::YELLOW, duration),
25                circle.fill_color.to(Color::GREEN, duration),
26                circle.fill_color.to(Color::BLUE, duration),
27                circle.fill_color.to(Color::RED, duration),
28            ]
29        },
30        None,
31    ));
32
33    // Show
34    project.show().expect("Failed to render");
35}
More examples
Hide additional examples
examples/math_animation.rs (line 8)
4fn main() {
5    // 1. Initialize Project
6    let mut project = Project::default()
7        .with_fps(60)
8        .with_dimensions(500, 500)
9        .with_title("Math Animation")
10        .close_on_finish();
11
12    // 2. Create MathNode (Typst syntax)
13    let tex = MathNode::default()
14        .with_position(Vec2::new(250.0, 250.0))
15        .with_equation("y = a x^2")
16        .with_font_size(48.0)
17        .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2));
18    project.scene.add(Box::new(tex.clone()));
19
20    // 3. Define Animation Sequence
21    project.scene.video_timeline.add(loop_anim(
22        move || {
23            chain![
24                tex.tex("y = a x^2 + b x", Duration::from_secs(1)),
25                tex.tex("e^(i pi) + 1 = 0", Duration::from_secs(1)),
26                tex.tex("y = a x^2", Duration::from_secs(1)),
27            ]
28        },
29        None,
30    ));
31
32    // 4. Run interactive preview
33    project.show().expect("Failed to render");
34}
examples/audio_demo.rs (line 6)
4fn main() {
5    let mut project = Project::default()
6        .with_dimensions(800, 450)
7        .with_title("Audio Demo")
8        .with_background(Color::rgb8(20, 20, 25))
9        .close_on_finish();
10
11    // Setup Video Timeline
12    let rect = Rect::new(Vec2::new(100.0, 100.0), Vec2::new(200.0, 200.0), Color::RED)
13        .with_anchor(Vec2::new(-1.0, -1.0));
14
15    project.scene.add(Box::new(rect.clone()));
16
17    project.scene.video_timeline.add(chain!(
18        all![
19            rect.position
20                .to(Vec2::new(150.0, 150.0), Duration::from_secs(1)),
21            rect.size.to(Vec2::new(200.0, 50.0), Duration::from_secs(1)),
22            rect.fill_color.to(Color::BLUE, Duration::from_secs(1)),
23        ],
24        all![
25            rect.position
26                .to(Vec2::new(350.0, 150.0), Duration::from_secs(1)),
27            rect.fill_color.to(Color::RED, Duration::from_secs(1)),
28        ]
29    ));
30
31    // Setup Audio Timeline using new macros and builder
32    project.scene.audio_timeline.add(chain!(
33        play!(AudioNode::new("./examples/audios/combo-1.mp3").with_volume(0.5)),
34        audio_wait!(0.05),
35        play!(AudioNode::new("./examples/audios/combo-2.mp3").with_volume(1.0))
36    ));
37
38    println!("Project configured with separate video and audio timelines.");
39    println!(
40        "Video duration: {:?}",
41        project.scene.video_timeline.duration()
42    );
43    println!(
44        "Audio duration: {:?}",
45        project.scene.audio_timeline.duration()
46    );
47
48    project.show().expect("Failed to run audio demo");
49}
examples/images.rs (line 6)
4fn main() {
5    let mut project = Project::default()
6        .with_dimensions(600, 600)
7        .with_title("Images")
8        .close_on_finish();
9
10    // Using the sample logo path from the project
11    let png = ImageNode::default()
12        .with_position(Vec2::new(450.0, 450.0))
13        .with_path("./examples/images/motion-canvas-logo.png")
14        .with_size(Vec2::new(200.0, 200.0));
15
16    let svg = ImageNode::default()
17        .with_position(Vec2::new(150.0, 150.0))
18        .with_path("./examples/images/motion-canvas-rs.svg")
19        .with_size(Vec2::new(200.0, 200.0));
20
21    project.scene.add(Box::new(png.clone()));
22    project.scene.add(Box::new(svg.clone()));
23
24    const MOVE_DUR: Duration = Duration::from_secs(2);
25
26    project.scene.video_timeline.add(loop_anim(
27        move || {
28            with_easing(
29                bounce_out,
30                vec![chain![
31                    all!(
32                        png.position.to(Vec2::new(150.0, 450.0), MOVE_DUR),
33                        svg.position.to(Vec2::new(450.0, 150.0), MOVE_DUR),
34                    ),
35                    all!(
36                        png.position.to(Vec2::new(150.0, 150.0), MOVE_DUR),
37                        svg.position.to(Vec2::new(450.0, 450.0), MOVE_DUR),
38                    ),
39                    all!(
40                        png.position.to(Vec2::new(450.0, 150.0), MOVE_DUR),
41                        svg.position.to(Vec2::new(150.0, 450.0), MOVE_DUR),
42                    ),
43                    all!(
44                        png.position.to(Vec2::new(450.0, 450.0), MOVE_DUR),
45                        svg.position.to(Vec2::new(150.0, 150.0), MOVE_DUR),
46                    ),
47                ]],
48            )
49        },
50        None,
51    ));
52
53    project.show().expect("Failed to render");
54}
examples/grid.rs (line 7)
4fn main() {
5    let mut project = Project::default()
6        .with_title("Grid Example")
7        .with_dimensions(1280, 720)
8        .close_on_finish();
9
10    // 1. Create Grid Node
11    let grid = GridNode::square(Vec2::new(640.0, 360.0), 4.0, 50.0)
12        .with_stroke(Palette::DARK_GRAY, 2.0)
13        .with_opacity(0.0);
14
15    // 2. Create Text Node
16    let text_node = TextNode::default()
17        .with_position(Vec2::new(640.0, 100.0))
18        .with_font_size(36.0)
19        .with_fill(Color::WHITE)
20        .with_text("Grid: 4x4 | Spacing: 50x50");
21
22    // 3. Bind Text to Grid's rows/columns and spacing
23    let cols_sig = grid.columns.clone();
24    let spacing_sig = grid.spacing.clone();
25    let text_link = text_node.text.bind(grid.rows.clone(), move |rows| {
26        let cols = cols_sig.get();
27        let spacing = spacing_sig.get();
28        format!(
29            "Grid: {:.0}x{:.0} | Spacing: {:.0}x{:.0}",
30            cols, rows, spacing.x, spacing.y
31        )
32    });
33
34    // Add to scene
35    project.scene.add(Box::new(grid.clone()));
36    project.scene.add(Box::new(text_node));
37    project.scene.add(Box::new(text_link));
38
39    project.scene.video_timeline.add(chain![
40        grid.opacity
41            .to(1.0, Duration::from_secs(1))
42            .ease(easings::cubic_out),
43        wait(Duration::from_millis(500)),
44        all![
45            grid.rows.to(16.0, Duration::from_secs(2)),
46            grid.columns.to(16.0, Duration::from_secs(2)),
47            grid.stroke_color.to(Palette::BLUE, Duration::from_secs(2)),
48            grid.spacing
49                .to(Vec2::new(100.0, 100.0), Duration::from_secs(2)),
50        ],
51        wait(Duration::from_millis(500)),
52        all![
53            grid.rows.to(8.0, Duration::from_secs(2)),
54            grid.columns.to(8.0, Duration::from_secs(2)),
55            grid.stroke_color
56                .to(Palette::ORANGE, Duration::from_secs(2)),
57            grid.spacing
58                .to(Vec2::new(20.0, 20.0), Duration::from_secs(2)),
59        ],
60        wait(Duration::from_secs(1)),
61        grid.opacity
62            .to(0.0, Duration::from_secs(1))
63            .ease(easings::cubic_out),
64        wait(Duration::from_millis(500)),
65    ]);
66
67    project.show().expect("Failed to render");
68}
examples/shapes.rs (line 5)
3fn main() {
4    let mut project = Project::default()
5        .with_dimensions(800, 300)
6        .with_title("Shapes");
7
8    // Configuration
9    let y_shapes = 150.0;
10    let y_labels = 50.0;
11    let spacing = 180.0;
12    let x_start = 130.0;
13
14    // Circle
15    let circle_text = TextNode::default()
16        .with_text("Circle")
17        .with_position(Vec2::new(x_start, y_labels))
18        .with_anchor(Vec2::ZERO)
19        .with_font_size(24.0)
20        .with_fill(Color::DIM_GRAY);
21    let circle = Circle::default()
22        .with_position(Vec2::new(x_start, y_shapes))
23        .with_radius(50.0)
24        .with_fill(Color::rgb8(0xe1, 0x32, 0x38));
25
26    // Rectangle
27    let x_rect = x_start + spacing;
28    let rect_text = TextNode::default()
29        .with_text("Rect")
30        .with_position(Vec2::new(x_rect, y_labels))
31        .with_anchor(Vec2::ZERO)
32        .with_font_size(24.0)
33        .with_fill(Color::DIM_GRAY);
34    let rect = Rect::default()
35        .with_position(Vec2::new(x_rect, y_shapes))
36        .with_size(Vec2::new(100.0, 100.0))
37        .with_radius(8.0)
38        .with_fill(Color::rgb8(0x68, 0xab, 0xdf));
39
40    // Line
41    let x_line = x_rect + spacing;
42    let line_text = TextNode::default()
43        .with_text("Line")
44        .with_position(Vec2::new(x_line, y_labels))
45        .with_anchor(Vec2::ZERO)
46        .with_font_size(24.0)
47        .with_fill(Color::DIM_GRAY);
48    let line = Line::default()
49        .with_position(Vec2::new(x_line, y_shapes))
50        .with_start(Vec2::new(-50.0, 50.0))
51        .with_end(Vec2::new(50.0, -50.0))
52        .with_stroke(Color::WHITE, 4.0);
53
54    // Polygon
55    let x_poly = x_line + spacing;
56    let poly_text = TextNode::default()
57        .with_text("Poly")
58        .with_position(Vec2::new(x_poly, y_labels))
59        .with_anchor(Vec2::ZERO)
60        .with_font_size(24.0)
61        .with_fill(Color::DIM_GRAY);
62    let poly = Polygon::regular(5, 50.0)
63        .with_position(Vec2::new(x_poly, y_shapes))
64        .with_fill(Color::rgb8(0xe6, 0xa7, 0x00));
65
66    // Add all nodes to the scene
67    project.scene.add(Box::new(circle));
68    project.scene.add(Box::new(circle_text));
69    project.scene.add(Box::new(rect));
70    project.scene.add(Box::new(rect_text));
71    project.scene.add(Box::new(line));
72    project.scene.add(Box::new(line_text));
73    project.scene.add(Box::new(poly));
74    project.scene.add(Box::new(poly_text));
75
76    project.show().expect("Failed to render");
77}
Source

pub fn with_title(self, title: &str) -> Self

Examples found in repository?
examples/color_interpolation.rs (line 8)
4fn main() {
5    let mut project = Project::default()
6        .with_dimensions(300, 300)
7        .with_fps(60)
8        .with_title("Color Interpolation")
9        .with_cache(true)
10        .close_on_finish();
11
12    let circle = Circle::default()
13        .with_position(Vec2::new(150.0, 150.0))
14        .with_radius(50.0)
15        .with_fill(Color::RED); // Red
16
17    project.scene.add(Box::new(circle.clone()));
18
19    let duration = Duration::from_secs(1);
20
21    project.scene.video_timeline.add(loop_anim(
22        move || {
23            chain![
24                circle.fill_color.to(Color::YELLOW, duration),
25                circle.fill_color.to(Color::GREEN, duration),
26                circle.fill_color.to(Color::BLUE, duration),
27                circle.fill_color.to(Color::RED, duration),
28            ]
29        },
30        None,
31    ));
32
33    // Show
34    project.show().expect("Failed to render");
35}
More examples
Hide additional examples
examples/math_animation.rs (line 9)
4fn main() {
5    // 1. Initialize Project
6    let mut project = Project::default()
7        .with_fps(60)
8        .with_dimensions(500, 500)
9        .with_title("Math Animation")
10        .close_on_finish();
11
12    // 2. Create MathNode (Typst syntax)
13    let tex = MathNode::default()
14        .with_position(Vec2::new(250.0, 250.0))
15        .with_equation("y = a x^2")
16        .with_font_size(48.0)
17        .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2));
18    project.scene.add(Box::new(tex.clone()));
19
20    // 3. Define Animation Sequence
21    project.scene.video_timeline.add(loop_anim(
22        move || {
23            chain![
24                tex.tex("y = a x^2 + b x", Duration::from_secs(1)),
25                tex.tex("e^(i pi) + 1 = 0", Duration::from_secs(1)),
26                tex.tex("y = a x^2", Duration::from_secs(1)),
27            ]
28        },
29        None,
30    ));
31
32    // 4. Run interactive preview
33    project.show().expect("Failed to render");
34}
examples/getting_started.rs (line 9)
4fn main() {
5    // 1. Initialize the Project
6    let mut project = Project::default()
7        .with_fps(60)
8        .with_cache(true)
9        .with_title("Getting Started")
10        .close_on_finish();
11
12    // 2. Define Nodes
13    let circle = Circle::default()
14        .with_position(Vec2::new(400.0, 300.0))
15        .with_radius(50.0)
16        .with_fill(Color::rgb8(0xe1, 0x32, 0x38)); // Red
17
18    let text = TextNode::default()
19        .with_position(Vec2::new(400.0, 450.0))
20        .with_text("Hello Rust")
21        .with_font_size(40.0)
22        .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White-ish
23
24    // 3. Add Nodes to the Scene
25    project.scene.add(Box::new(circle.clone()));
26    project.scene.add(Box::new(text.clone()));
27
28    // 4. Add Animations to the Timeline
29    project.scene.video_timeline.add(all![
30        circle.radius.to(100.0, Duration::from_secs(1)),
31        text.position
32            .to(Vec2::new(400.0, 500.0), Duration::from_secs(1)),
33    ]);
34
35    // 5. Show
36    project.show().expect("Failed to render");
37}
examples/code_advanced.rs (line 6)
4fn main() {
5    let mut project = Project::default()
6        .with_title("Code Advanced")
7        .close_on_finish();
8
9    let code = CodeNode::default()
10        .with_position(Vec2::new(50.0, 50.0))
11        .with_code(
12            r#"fn main() {
13    println!("Hello");
14}"#,
15        )
16        .with_language("rust")
17        .with_font_size(32.0)
18        .with_dim_opacity(0.1);
19
20    project.scene.add(Box::new(code.clone()));
21
22    project.scene.video_timeline.add(sequence![
23        Duration::from_secs(1),
24        // 1. Select line 2 (println) - using 1-based index string
25        code.select_string("2", Duration::from_millis(300)),
26        // 2. Select range 1-2
27        code.select_string("1-2", Duration::from_millis(300)),
28        // 3. Append a comment
29        code.append("\n// Done!", Duration::from_millis(300)),
30        // 3. Reset selection
31        code.select_lines(vec![], Duration::from_millis(300)),
32        // 4. Prepend a header (Now natively lazy, no wrapper needed!)
33        code.prepend("// My Script\n", Duration::from_millis(300)),
34    ]);
35
36    project.show().expect("Failed to render");
37}
examples/export.rs (line 9)
4fn main() {
5    // 1. Initialize for Export
6    let mut project = Project::default()
7        .with_fps(30)
8        .with_ffmpeg(true)
9        .with_title("Export")
10        .with_output_path("output")
11        .close_on_finish();
12
13    // 2. Setup Nodes
14    let circle = Circle::default()
15        .with_position(Vec2::new(400.0, 300.0))
16        .with_radius(50.0)
17        .with_fill(Color::rgb8(0x68, 0xab, 0xdf)); // Blue
18
19    let text = TextNode::default()
20        .with_position(Vec2::new(400.0, 50.0))
21        .with_text("Export Demo")
22        .with_font_size(40.0)
23        .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White
24
25    project.scene.add(Box::new(circle.clone()));
26    project.scene.add(Box::new(text.clone()));
27
28    // 3. Define Animations (Color and Font Size)
29    project.scene.video_timeline.add(all![
30        // Circle color and size
31        circle
32            .fill_color
33            .to(Color::rgb8(0xf2, 0xf2, 0xf2), Duration::from_secs(2))
34            .ease(easings::quad_in_out),
35        circle
36            .radius
37            .to(150.0, Duration::from_secs(2))
38            .ease(easings::elastic_out),
39        // Text font size
40        text.font_size
41            .to(50.0, Duration::from_secs(2))
42            .ease(easings::cubic_out),
43    ]);
44
45    // 4. Export (Renders frames and combines them into out.mkv)
46    println!("Starting export to {}...", project.output_path.display());
47    project.export().expect("Failed to export");
48}
examples/polygon.rs (line 5)
4fn main() {
5    let mut project = Project::default().with_title("Polygon").close_on_finish();
6
7    // Create a regular pentagon
8    let pentagon = Polygon::regular(5, 100.0)
9        .with_fill(Color::rgb8(0xe1, 0x32, 0x38)) // Red
10        .with_position(Vec2::new(200.0, 300.0))
11        .with_scale(0.0);
12
13    // Create a custom triangle
14    let triangle = Polygon::default()
15        .with_position(Vec2::new(500.0, 300.0))
16        .with_points(vec![
17            Vec2::new(0.0, -100.0),
18            Vec2::new(100.0, 100.0),
19            Vec2::new(-100.0, 100.0),
20        ])
21        .with_fill(Color::rgb8(0x68, 0xab, 0xdf)) // Blue
22        .with_stroke(Color::WHITE, 4.0);
23
24    project.scene.add(Box::new(pentagon.clone()));
25    project.scene.add(Box::new(triangle.clone()));
26
27    // Animate rotation and opacity
28    project.scene.video_timeline.add(all![
29        chain![
30            pentagon.scale.to(Vec2::ONE, Duration::from_secs(1)),
31            pentagon
32                .rotation
33                .to(std::f32::consts::PI, Duration::from_secs(2)),
34            pentagon.scale.to(-Vec2::ONE, Duration::from_secs(1)),
35        ],
36        chain![
37            triangle.opacity.to(1.0, Duration::from_secs(1)),
38            triangle
39                .position
40                .to(Vec2::new(500.0, 300.0), Duration::from_secs(1)),
41            triangle
42                .rotation
43                .to(360.0_f32.to_radians(), Duration::from_secs(1))
44        ],
45    ]);
46
47    project.show().expect("Failed to render");
48}
Source

pub fn with_output_path(self, path: &str) -> Self

Examples found in repository?
examples/export.rs (line 10)
4fn main() {
5    // 1. Initialize for Export
6    let mut project = Project::default()
7        .with_fps(30)
8        .with_ffmpeg(true)
9        .with_title("Export")
10        .with_output_path("output")
11        .close_on_finish();
12
13    // 2. Setup Nodes
14    let circle = Circle::default()
15        .with_position(Vec2::new(400.0, 300.0))
16        .with_radius(50.0)
17        .with_fill(Color::rgb8(0x68, 0xab, 0xdf)); // Blue
18
19    let text = TextNode::default()
20        .with_position(Vec2::new(400.0, 50.0))
21        .with_text("Export Demo")
22        .with_font_size(40.0)
23        .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White
24
25    project.scene.add(Box::new(circle.clone()));
26    project.scene.add(Box::new(text.clone()));
27
28    // 3. Define Animations (Color and Font Size)
29    project.scene.video_timeline.add(all![
30        // Circle color and size
31        circle
32            .fill_color
33            .to(Color::rgb8(0xf2, 0xf2, 0xf2), Duration::from_secs(2))
34            .ease(easings::quad_in_out),
35        circle
36            .radius
37            .to(150.0, Duration::from_secs(2))
38            .ease(easings::elastic_out),
39        // Text font size
40        text.font_size
41            .to(50.0, Duration::from_secs(2))
42            .ease(easings::cubic_out),
43    ]);
44
45    // 4. Export (Renders frames and combines them into out.mkv)
46    println!("Starting export to {}...", project.output_path.display());
47    project.export().expect("Failed to export");
48}
More examples
Hide additional examples
examples/advanced_flow.rs (line 11)
4fn main() {
5    // 1. Initialize Project with full API coverage
6    let mut project = Project::default()
7        .with_fps(120)
8        .with_gpu(true)
9        .with_cache(true)
10        .with_ffmpeg(true)
11        .with_output_path("output")
12        .with_title("Advanced Flow")
13        .close_on_finish();
14
15    // 2. Setup Nodes
16    let mut path = BezPath::new();
17    path.move_to((100.0, 300.0));
18    path.curve_to((250.0, 100.0), (550.0, 500.0), (700.0, 300.0));
19    let path_node = PathNode::default()
20        .with_path(path)
21        .with_stroke(Color::rgb8(0x44, 0x44, 0x44), 2.0);
22    let follower = Circle::default()
23        .with_position(Vec2::new(100.0, 300.0))
24        .with_radius(20.0)
25        .with_fill(Color::rgb8(0xe1, 0x32, 0x38)); // Red
26
27    // Showcase: Rect and Line
28    let background_rect = Rect::default()
29        .with_position(Vec2::new(400.0, 300.0))
30        .with_size(Vec2::new(760.0, 560.0))
31        .with_fill(Color::rgba8(0x33, 0x33, 0x33, 150))
32        .with_radius(20.0);
33
34    let divider_line = Line::default()
35        .with_start(Vec2::new(0.0, 300.0))
36        .with_end(Vec2::new(0.0, 300.0))
37        .with_stroke(Color::rgb8(0x44, 0x44, 0x44), 1.0);
38    let title_text = TextNode::default()
39        .with_position(Vec2::new(50.0, 50.0))
40        .with_anchor(Vec2::new(-1.0, -1.0))
41        .with_text("Motion Canvas in Rust")
42        .with_font_size(40.0)
43        .with_fill(Color::rgb8(0x68, 0xab, 0xdf)) // Blue
44        .with_font("JetBrains Mono");
45
46    let code_block = CodeNode::default()
47        .with_anchor(Vec2::new(-1.0, -1.0))
48        .with_position(Vec2::new(50.0, 415.0))
49        .with_language("rust")
50        .with_opacity(0.0);
51
52    let math_eq = MathNode::default()
53        .with_position(Vec2::new(150.0, 150.0))
54        .with_equation("f(x) = sin(x)")
55        .with_font_size(30.0)
56        .with_fill(Color::rgb8(0xe6, 0xa7, 0x00)); // Yellow
57
58    let logo = ImageNode::default()
59        .with_position(Vec2::new(650.0, 150.0))
60        .with_path("./examples/images/motion-canvas-logo.png")
61        .with_size(Vec2::new(150.0, 150.0));
62
63    project.scene.video_timeline.add(all![
64        // Show code
65        chain![
66            code_block.opacity.to(1.0, Duration::from_secs(1)),
67            code_block.append("fn main() {\n", Duration::from_secs(1)),
68            code_block.append(
69                "    let mut engine = MotionCanvas::new();\n",
70                Duration::from_secs(1)
71            ),
72            code_block.append("    engine.render();\n", Duration::from_secs(1)),
73            code_block.append("}", Duration::from_secs(1)),
74            // Staggered appearance of nodes
75            sequence![
76                Duration::from_millis(200),
77                divider_line
78                    .end
79                    .to(Vec2::new(800.0, 300.0), Duration::from_secs(1))
80                    .ease(easings::cubic_out),
81                follower
82                    .radius
83                    .to(30.0, Duration::from_millis(500))
84                    .ease(easings::elastic_out),
85            ],
86            // The path follow combined with a "race" logic
87            any![
88                follower
89                    .position
90                    .follow(&path_node, Duration::from_secs(3))
91                    .ease(easings::cubic_in_out),
92                // Race: if this 'wait' finishes first, the follow is done
93                wait(Duration::from_secs(4)),
94            ],
95            // Final flourishes using different easings
96            all![
97                follower
98                    .radius
99                    .to(10.0, Duration::from_secs(1))
100                    .ease(easings::quad_out),
101                divider_line
102                    .start
103                    .to(Vec2::new(400.0, 300.0), Duration::from_secs(1))
104                    .ease(easings::cubic_in),
105            ]
106        ]
107    ]);
108
109    // 4. Build Scene
110    project.scene.add(Box::new(background_rect.clone()));
111    project.scene.add(Box::new(divider_line.clone()));
112    project.scene.add(Box::new(path_node.clone()));
113    project.scene.add(Box::new(follower.clone()));
114    project.scene.add(Box::new(title_text.clone()));
115    project.scene.add(Box::new(code_block.clone()));
116    project.scene.add(Box::new(math_eq.clone()));
117    project.scene.add(Box::new(logo.clone()));
118
119    // 5. Run (Choose show() for interactive or export() for PNGs)
120    project.show().expect("Failed to render");
121    // project.export().expect("Failed to export");
122}
Source

pub fn with_cache(self, use_cache: bool) -> Self

Examples found in repository?
examples/color_interpolation.rs (line 9)
4fn main() {
5    let mut project = Project::default()
6        .with_dimensions(300, 300)
7        .with_fps(60)
8        .with_title("Color Interpolation")
9        .with_cache(true)
10        .close_on_finish();
11
12    let circle = Circle::default()
13        .with_position(Vec2::new(150.0, 150.0))
14        .with_radius(50.0)
15        .with_fill(Color::RED); // Red
16
17    project.scene.add(Box::new(circle.clone()));
18
19    let duration = Duration::from_secs(1);
20
21    project.scene.video_timeline.add(loop_anim(
22        move || {
23            chain![
24                circle.fill_color.to(Color::YELLOW, duration),
25                circle.fill_color.to(Color::GREEN, duration),
26                circle.fill_color.to(Color::BLUE, duration),
27                circle.fill_color.to(Color::RED, duration),
28            ]
29        },
30        None,
31    ));
32
33    // Show
34    project.show().expect("Failed to render");
35}
More examples
Hide additional examples
examples/getting_started.rs (line 8)
4fn main() {
5    // 1. Initialize the Project
6    let mut project = Project::default()
7        .with_fps(60)
8        .with_cache(true)
9        .with_title("Getting Started")
10        .close_on_finish();
11
12    // 2. Define Nodes
13    let circle = Circle::default()
14        .with_position(Vec2::new(400.0, 300.0))
15        .with_radius(50.0)
16        .with_fill(Color::rgb8(0xe1, 0x32, 0x38)); // Red
17
18    let text = TextNode::default()
19        .with_position(Vec2::new(400.0, 450.0))
20        .with_text("Hello Rust")
21        .with_font_size(40.0)
22        .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White-ish
23
24    // 3. Add Nodes to the Scene
25    project.scene.add(Box::new(circle.clone()));
26    project.scene.add(Box::new(text.clone()));
27
28    // 4. Add Animations to the Timeline
29    project.scene.video_timeline.add(all![
30        circle.radius.to(100.0, Duration::from_secs(1)),
31        text.position
32            .to(Vec2::new(400.0, 500.0), Duration::from_secs(1)),
33    ]);
34
35    // 5. Show
36    project.show().expect("Failed to render");
37}
examples/advanced_flow.rs (line 9)
4fn main() {
5    // 1. Initialize Project with full API coverage
6    let mut project = Project::default()
7        .with_fps(120)
8        .with_gpu(true)
9        .with_cache(true)
10        .with_ffmpeg(true)
11        .with_output_path("output")
12        .with_title("Advanced Flow")
13        .close_on_finish();
14
15    // 2. Setup Nodes
16    let mut path = BezPath::new();
17    path.move_to((100.0, 300.0));
18    path.curve_to((250.0, 100.0), (550.0, 500.0), (700.0, 300.0));
19    let path_node = PathNode::default()
20        .with_path(path)
21        .with_stroke(Color::rgb8(0x44, 0x44, 0x44), 2.0);
22    let follower = Circle::default()
23        .with_position(Vec2::new(100.0, 300.0))
24        .with_radius(20.0)
25        .with_fill(Color::rgb8(0xe1, 0x32, 0x38)); // Red
26
27    // Showcase: Rect and Line
28    let background_rect = Rect::default()
29        .with_position(Vec2::new(400.0, 300.0))
30        .with_size(Vec2::new(760.0, 560.0))
31        .with_fill(Color::rgba8(0x33, 0x33, 0x33, 150))
32        .with_radius(20.0);
33
34    let divider_line = Line::default()
35        .with_start(Vec2::new(0.0, 300.0))
36        .with_end(Vec2::new(0.0, 300.0))
37        .with_stroke(Color::rgb8(0x44, 0x44, 0x44), 1.0);
38    let title_text = TextNode::default()
39        .with_position(Vec2::new(50.0, 50.0))
40        .with_anchor(Vec2::new(-1.0, -1.0))
41        .with_text("Motion Canvas in Rust")
42        .with_font_size(40.0)
43        .with_fill(Color::rgb8(0x68, 0xab, 0xdf)) // Blue
44        .with_font("JetBrains Mono");
45
46    let code_block = CodeNode::default()
47        .with_anchor(Vec2::new(-1.0, -1.0))
48        .with_position(Vec2::new(50.0, 415.0))
49        .with_language("rust")
50        .with_opacity(0.0);
51
52    let math_eq = MathNode::default()
53        .with_position(Vec2::new(150.0, 150.0))
54        .with_equation("f(x) = sin(x)")
55        .with_font_size(30.0)
56        .with_fill(Color::rgb8(0xe6, 0xa7, 0x00)); // Yellow
57
58    let logo = ImageNode::default()
59        .with_position(Vec2::new(650.0, 150.0))
60        .with_path("./examples/images/motion-canvas-logo.png")
61        .with_size(Vec2::new(150.0, 150.0));
62
63    project.scene.video_timeline.add(all![
64        // Show code
65        chain![
66            code_block.opacity.to(1.0, Duration::from_secs(1)),
67            code_block.append("fn main() {\n", Duration::from_secs(1)),
68            code_block.append(
69                "    let mut engine = MotionCanvas::new();\n",
70                Duration::from_secs(1)
71            ),
72            code_block.append("    engine.render();\n", Duration::from_secs(1)),
73            code_block.append("}", Duration::from_secs(1)),
74            // Staggered appearance of nodes
75            sequence![
76                Duration::from_millis(200),
77                divider_line
78                    .end
79                    .to(Vec2::new(800.0, 300.0), Duration::from_secs(1))
80                    .ease(easings::cubic_out),
81                follower
82                    .radius
83                    .to(30.0, Duration::from_millis(500))
84                    .ease(easings::elastic_out),
85            ],
86            // The path follow combined with a "race" logic
87            any![
88                follower
89                    .position
90                    .follow(&path_node, Duration::from_secs(3))
91                    .ease(easings::cubic_in_out),
92                // Race: if this 'wait' finishes first, the follow is done
93                wait(Duration::from_secs(4)),
94            ],
95            // Final flourishes using different easings
96            all![
97                follower
98                    .radius
99                    .to(10.0, Duration::from_secs(1))
100                    .ease(easings::quad_out),
101                divider_line
102                    .start
103                    .to(Vec2::new(400.0, 300.0), Duration::from_secs(1))
104                    .ease(easings::cubic_in),
105            ]
106        ]
107    ]);
108
109    // 4. Build Scene
110    project.scene.add(Box::new(background_rect.clone()));
111    project.scene.add(Box::new(divider_line.clone()));
112    project.scene.add(Box::new(path_node.clone()));
113    project.scene.add(Box::new(follower.clone()));
114    project.scene.add(Box::new(title_text.clone()));
115    project.scene.add(Box::new(code_block.clone()));
116    project.scene.add(Box::new(math_eq.clone()));
117    project.scene.add(Box::new(logo.clone()));
118
119    // 5. Run (Choose show() for interactive or export() for PNGs)
120    project.show().expect("Failed to render");
121    // project.export().expect("Failed to export");
122}
Source

pub fn with_ffmpeg(self, use_ffmpeg: bool) -> Self

Examples found in repository?
examples/export.rs (line 8)
4fn main() {
5    // 1. Initialize for Export
6    let mut project = Project::default()
7        .with_fps(30)
8        .with_ffmpeg(true)
9        .with_title("Export")
10        .with_output_path("output")
11        .close_on_finish();
12
13    // 2. Setup Nodes
14    let circle = Circle::default()
15        .with_position(Vec2::new(400.0, 300.0))
16        .with_radius(50.0)
17        .with_fill(Color::rgb8(0x68, 0xab, 0xdf)); // Blue
18
19    let text = TextNode::default()
20        .with_position(Vec2::new(400.0, 50.0))
21        .with_text("Export Demo")
22        .with_font_size(40.0)
23        .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White
24
25    project.scene.add(Box::new(circle.clone()));
26    project.scene.add(Box::new(text.clone()));
27
28    // 3. Define Animations (Color and Font Size)
29    project.scene.video_timeline.add(all![
30        // Circle color and size
31        circle
32            .fill_color
33            .to(Color::rgb8(0xf2, 0xf2, 0xf2), Duration::from_secs(2))
34            .ease(easings::quad_in_out),
35        circle
36            .radius
37            .to(150.0, Duration::from_secs(2))
38            .ease(easings::elastic_out),
39        // Text font size
40        text.font_size
41            .to(50.0, Duration::from_secs(2))
42            .ease(easings::cubic_out),
43    ]);
44
45    // 4. Export (Renders frames and combines them into out.mkv)
46    println!("Starting export to {}...", project.output_path.display());
47    project.export().expect("Failed to export");
48}
More examples
Hide additional examples
examples/advanced_flow.rs (line 10)
4fn main() {
5    // 1. Initialize Project with full API coverage
6    let mut project = Project::default()
7        .with_fps(120)
8        .with_gpu(true)
9        .with_cache(true)
10        .with_ffmpeg(true)
11        .with_output_path("output")
12        .with_title("Advanced Flow")
13        .close_on_finish();
14
15    // 2. Setup Nodes
16    let mut path = BezPath::new();
17    path.move_to((100.0, 300.0));
18    path.curve_to((250.0, 100.0), (550.0, 500.0), (700.0, 300.0));
19    let path_node = PathNode::default()
20        .with_path(path)
21        .with_stroke(Color::rgb8(0x44, 0x44, 0x44), 2.0);
22    let follower = Circle::default()
23        .with_position(Vec2::new(100.0, 300.0))
24        .with_radius(20.0)
25        .with_fill(Color::rgb8(0xe1, 0x32, 0x38)); // Red
26
27    // Showcase: Rect and Line
28    let background_rect = Rect::default()
29        .with_position(Vec2::new(400.0, 300.0))
30        .with_size(Vec2::new(760.0, 560.0))
31        .with_fill(Color::rgba8(0x33, 0x33, 0x33, 150))
32        .with_radius(20.0);
33
34    let divider_line = Line::default()
35        .with_start(Vec2::new(0.0, 300.0))
36        .with_end(Vec2::new(0.0, 300.0))
37        .with_stroke(Color::rgb8(0x44, 0x44, 0x44), 1.0);
38    let title_text = TextNode::default()
39        .with_position(Vec2::new(50.0, 50.0))
40        .with_anchor(Vec2::new(-1.0, -1.0))
41        .with_text("Motion Canvas in Rust")
42        .with_font_size(40.0)
43        .with_fill(Color::rgb8(0x68, 0xab, 0xdf)) // Blue
44        .with_font("JetBrains Mono");
45
46    let code_block = CodeNode::default()
47        .with_anchor(Vec2::new(-1.0, -1.0))
48        .with_position(Vec2::new(50.0, 415.0))
49        .with_language("rust")
50        .with_opacity(0.0);
51
52    let math_eq = MathNode::default()
53        .with_position(Vec2::new(150.0, 150.0))
54        .with_equation("f(x) = sin(x)")
55        .with_font_size(30.0)
56        .with_fill(Color::rgb8(0xe6, 0xa7, 0x00)); // Yellow
57
58    let logo = ImageNode::default()
59        .with_position(Vec2::new(650.0, 150.0))
60        .with_path("./examples/images/motion-canvas-logo.png")
61        .with_size(Vec2::new(150.0, 150.0));
62
63    project.scene.video_timeline.add(all![
64        // Show code
65        chain![
66            code_block.opacity.to(1.0, Duration::from_secs(1)),
67            code_block.append("fn main() {\n", Duration::from_secs(1)),
68            code_block.append(
69                "    let mut engine = MotionCanvas::new();\n",
70                Duration::from_secs(1)
71            ),
72            code_block.append("    engine.render();\n", Duration::from_secs(1)),
73            code_block.append("}", Duration::from_secs(1)),
74            // Staggered appearance of nodes
75            sequence![
76                Duration::from_millis(200),
77                divider_line
78                    .end
79                    .to(Vec2::new(800.0, 300.0), Duration::from_secs(1))
80                    .ease(easings::cubic_out),
81                follower
82                    .radius
83                    .to(30.0, Duration::from_millis(500))
84                    .ease(easings::elastic_out),
85            ],
86            // The path follow combined with a "race" logic
87            any![
88                follower
89                    .position
90                    .follow(&path_node, Duration::from_secs(3))
91                    .ease(easings::cubic_in_out),
92                // Race: if this 'wait' finishes first, the follow is done
93                wait(Duration::from_secs(4)),
94            ],
95            // Final flourishes using different easings
96            all![
97                follower
98                    .radius
99                    .to(10.0, Duration::from_secs(1))
100                    .ease(easings::quad_out),
101                divider_line
102                    .start
103                    .to(Vec2::new(400.0, 300.0), Duration::from_secs(1))
104                    .ease(easings::cubic_in),
105            ]
106        ]
107    ]);
108
109    // 4. Build Scene
110    project.scene.add(Box::new(background_rect.clone()));
111    project.scene.add(Box::new(divider_line.clone()));
112    project.scene.add(Box::new(path_node.clone()));
113    project.scene.add(Box::new(follower.clone()));
114    project.scene.add(Box::new(title_text.clone()));
115    project.scene.add(Box::new(code_block.clone()));
116    project.scene.add(Box::new(math_eq.clone()));
117    project.scene.add(Box::new(logo.clone()));
118
119    // 5. Run (Choose show() for interactive or export() for PNGs)
120    project.show().expect("Failed to render");
121    // project.export().expect("Failed to export");
122}
Source

pub fn with_gpu(self, use_gpu: bool) -> Self

Examples found in repository?
examples/advanced_flow.rs (line 8)
4fn main() {
5    // 1. Initialize Project with full API coverage
6    let mut project = Project::default()
7        .with_fps(120)
8        .with_gpu(true)
9        .with_cache(true)
10        .with_ffmpeg(true)
11        .with_output_path("output")
12        .with_title("Advanced Flow")
13        .close_on_finish();
14
15    // 2. Setup Nodes
16    let mut path = BezPath::new();
17    path.move_to((100.0, 300.0));
18    path.curve_to((250.0, 100.0), (550.0, 500.0), (700.0, 300.0));
19    let path_node = PathNode::default()
20        .with_path(path)
21        .with_stroke(Color::rgb8(0x44, 0x44, 0x44), 2.0);
22    let follower = Circle::default()
23        .with_position(Vec2::new(100.0, 300.0))
24        .with_radius(20.0)
25        .with_fill(Color::rgb8(0xe1, 0x32, 0x38)); // Red
26
27    // Showcase: Rect and Line
28    let background_rect = Rect::default()
29        .with_position(Vec2::new(400.0, 300.0))
30        .with_size(Vec2::new(760.0, 560.0))
31        .with_fill(Color::rgba8(0x33, 0x33, 0x33, 150))
32        .with_radius(20.0);
33
34    let divider_line = Line::default()
35        .with_start(Vec2::new(0.0, 300.0))
36        .with_end(Vec2::new(0.0, 300.0))
37        .with_stroke(Color::rgb8(0x44, 0x44, 0x44), 1.0);
38    let title_text = TextNode::default()
39        .with_position(Vec2::new(50.0, 50.0))
40        .with_anchor(Vec2::new(-1.0, -1.0))
41        .with_text("Motion Canvas in Rust")
42        .with_font_size(40.0)
43        .with_fill(Color::rgb8(0x68, 0xab, 0xdf)) // Blue
44        .with_font("JetBrains Mono");
45
46    let code_block = CodeNode::default()
47        .with_anchor(Vec2::new(-1.0, -1.0))
48        .with_position(Vec2::new(50.0, 415.0))
49        .with_language("rust")
50        .with_opacity(0.0);
51
52    let math_eq = MathNode::default()
53        .with_position(Vec2::new(150.0, 150.0))
54        .with_equation("f(x) = sin(x)")
55        .with_font_size(30.0)
56        .with_fill(Color::rgb8(0xe6, 0xa7, 0x00)); // Yellow
57
58    let logo = ImageNode::default()
59        .with_position(Vec2::new(650.0, 150.0))
60        .with_path("./examples/images/motion-canvas-logo.png")
61        .with_size(Vec2::new(150.0, 150.0));
62
63    project.scene.video_timeline.add(all![
64        // Show code
65        chain![
66            code_block.opacity.to(1.0, Duration::from_secs(1)),
67            code_block.append("fn main() {\n", Duration::from_secs(1)),
68            code_block.append(
69                "    let mut engine = MotionCanvas::new();\n",
70                Duration::from_secs(1)
71            ),
72            code_block.append("    engine.render();\n", Duration::from_secs(1)),
73            code_block.append("}", Duration::from_secs(1)),
74            // Staggered appearance of nodes
75            sequence![
76                Duration::from_millis(200),
77                divider_line
78                    .end
79                    .to(Vec2::new(800.0, 300.0), Duration::from_secs(1))
80                    .ease(easings::cubic_out),
81                follower
82                    .radius
83                    .to(30.0, Duration::from_millis(500))
84                    .ease(easings::elastic_out),
85            ],
86            // The path follow combined with a "race" logic
87            any![
88                follower
89                    .position
90                    .follow(&path_node, Duration::from_secs(3))
91                    .ease(easings::cubic_in_out),
92                // Race: if this 'wait' finishes first, the follow is done
93                wait(Duration::from_secs(4)),
94            ],
95            // Final flourishes using different easings
96            all![
97                follower
98                    .radius
99                    .to(10.0, Duration::from_secs(1))
100                    .ease(easings::quad_out),
101                divider_line
102                    .start
103                    .to(Vec2::new(400.0, 300.0), Duration::from_secs(1))
104                    .ease(easings::cubic_in),
105            ]
106        ]
107    ]);
108
109    // 4. Build Scene
110    project.scene.add(Box::new(background_rect.clone()));
111    project.scene.add(Box::new(divider_line.clone()));
112    project.scene.add(Box::new(path_node.clone()));
113    project.scene.add(Box::new(follower.clone()));
114    project.scene.add(Box::new(title_text.clone()));
115    project.scene.add(Box::new(code_block.clone()));
116    project.scene.add(Box::new(math_eq.clone()));
117    project.scene.add(Box::new(logo.clone()));
118
119    // 5. Run (Choose show() for interactive or export() for PNGs)
120    project.show().expect("Failed to render");
121    // project.export().expect("Failed to export");
122}
Source

pub fn with_background(self, color: Color) -> Self

Examples found in repository?
examples/audio_demo.rs (line 8)
4fn main() {
5    let mut project = Project::default()
6        .with_dimensions(800, 450)
7        .with_title("Audio Demo")
8        .with_background(Color::rgb8(20, 20, 25))
9        .close_on_finish();
10
11    // Setup Video Timeline
12    let rect = Rect::new(Vec2::new(100.0, 100.0), Vec2::new(200.0, 200.0), Color::RED)
13        .with_anchor(Vec2::new(-1.0, -1.0));
14
15    project.scene.add(Box::new(rect.clone()));
16
17    project.scene.video_timeline.add(chain!(
18        all![
19            rect.position
20                .to(Vec2::new(150.0, 150.0), Duration::from_secs(1)),
21            rect.size.to(Vec2::new(200.0, 50.0), Duration::from_secs(1)),
22            rect.fill_color.to(Color::BLUE, Duration::from_secs(1)),
23        ],
24        all![
25            rect.position
26                .to(Vec2::new(350.0, 150.0), Duration::from_secs(1)),
27            rect.fill_color.to(Color::RED, Duration::from_secs(1)),
28        ]
29    ));
30
31    // Setup Audio Timeline using new macros and builder
32    project.scene.audio_timeline.add(chain!(
33        play!(AudioNode::new("./examples/audios/combo-1.mp3").with_volume(0.5)),
34        audio_wait!(0.05),
35        play!(AudioNode::new("./examples/audios/combo-2.mp3").with_volume(1.0))
36    ));
37
38    println!("Project configured with separate video and audio timelines.");
39    println!(
40        "Video duration: {:?}",
41        project.scene.video_timeline.duration()
42    );
43    println!(
44        "Audio duration: {:?}",
45        project.scene.audio_timeline.duration()
46    );
47
48    project.show().expect("Failed to run audio demo");
49}
More examples
Hide additional examples
examples/camera_demo.rs (line 7)
4fn main() {
5    let mut project = Project::default()
6        .with_title("Camera Demo")
7        .with_background(Color::rgb8(0x1a, 0x1a, 0x1a))
8        .close_on_finish();
9
10    // Create a grid of circles to move the camera around
11    let mut nodes: Vec<Box<dyn Node>> = Vec::new();
12
13    for x in -2..=2 {
14        for y in -2..=2 {
15            let color = if (x + y) % 2 == 0 {
16                Color::rgb8(0x34, 0x98, 0xdb) // Blue
17            } else {
18                Color::rgb8(0xe7, 0x4c, 0x3c) // Red
19            };
20
21            nodes.push(Box::new(
22                Circle::default()
23                    .with_position(Vec2::new(x as f32 * 200.0, y as f32 * 200.0))
24                    .with_radius(40.0)
25                    .with_fill(color),
26            ));
27        }
28    }
29
30    // Create the camera and add our nodes to it
31    let camera = CameraNode::new(nodes)
32        .with_position(Vec2::new(0.0, 0.0))
33        .with_zoom(1.0);
34
35    project.scene.add(Box::new(camera.clone()));
36
37    // Add HUD text (NOT in camera, so it stays fixed)
38    project.scene.add(Box::new(
39        TextNode::default()
40            .with_position(Vec2::new(400.0, 50.0))
41            .with_text("Camera Control")
42            .with_font_size(60.0)
43            .with_fill(Color::WHITE)
44            .with_anchor(Vec2::ZERO),
45    ));
46
47    // Animation: Pan, Zoom, and Rotate the camera
48    project.scene.video_timeline.add(loop_anim!(
49        chain![
50            wait(Duration::from_secs(1)),
51            // 1. Pan to the right
52            camera
53                .position
54                .to(Vec2::new(200.0, 0.0), Duration::from_secs(1)),
55            // 2. Zoom in
56            camera.zoom.to(2.0, Duration::from_secs(1)),
57            // 3. Pan down and rotate
58            all![
59                camera
60                    .position
61                    .to(Vec2::new(200.0, 200.0), Duration::from_secs(1)),
62                camera
63                    .rotation
64                    .to(std::f32::consts::PI / 4.0, Duration::from_secs(1)),
65            ],
66            // 4. Zoom out and reset
67            all![
68                camera.position.to(Vec2::ZERO, Duration::from_secs(1)),
69                camera.zoom.to(0.5, Duration::from_secs(1)),
70                camera.rotation.to(0.0, Duration::from_secs(1)),
71            ],
72            // 5. Back to normal
73            camera.zoom.to(1.0, Duration::from_secs(1)),
74        ],
75        None,
76    ));
77
78    project.show().expect("Failed to render");
79}
examples/nested_cameras.rs (line 7)
4fn main() {
5    let mut project = Project::default()
6        .with_title("Nested Cameras Demo")
7        .with_background(Color::rgb8(0x1a, 0x1a, 0x1a))
8        .close_on_finish();
9
10    // Elements to view
11    let rect = Rect::default()
12        .with_size(Vec2::new(200.0, 200.0))
13        .with_fill(Color::BLUE);
14
15    let circle = Circle::default()
16        .with_position(Vec2::new(100.0, 100.0))
17        .with_radius(30.0)
18        .with_fill(Color::WHITE);
19
20    // Inner Camera: Zooms into the action
21    // We disable 'centered' here so it doesn't double-shift the viewport
22    let inner_camera = CameraNode::new(vec![Box::new(rect.clone()), Box::new(circle.clone())])
23        .with_centered(false);
24
25    // Outer Camera: Handles global movement (centered)
26    let outer_camera = CameraNode::new(vec![Box::new(inner_camera.clone())]).with_centered(true);
27
28    // HUD Indicators (Outside cameras to show state)
29    let outer_status = TextNode::default()
30        .with_text("Outer Camera: Panning")
31        .with_position(Vec2::new(20.0, 20.0))
32        .with_anchor(Vec2::new(-1.0, -1.0))
33        .with_font_size(24.0)
34        .with_fill(Color::rgb8(0x2e, 0xcc, 0x71)) // Green
35        .with_opacity(0.0);
36
37    let inner_status = TextNode::default()
38        .with_text("Inner Camera: Zooming")
39        .with_position(Vec2::new(20.0, 60.0))
40        .with_anchor(Vec2::new(-1.0, -1.0))
41        .with_font_size(24.0)
42        .with_fill(Color::rgb8(0xf1, 0xc4, 0x0f)) // Yellow
43        .with_opacity(0.0);
44
45    project.scene.add(Box::new(outer_camera.clone()));
46
47    project.scene.add(Box::new(outer_status.clone()));
48    project.scene.add(Box::new(inner_status.clone()));
49
50    // Animation Flow
51    project.scene.video_timeline.add(loop_anim!(
52        chain![
53            // 1. Outer camera moves left
54            all![
55                outer_status.opacity.to(1.0, Duration::from_millis(300)),
56                outer_camera
57                    .position
58                    .to(Vec2::new(-200.0, 0.0), Duration::from_secs(1)),
59            ],
60            outer_status.opacity.to(0.0, Duration::from_millis(300)),
61            // 2. Inner camera zooms in
62            all![
63                inner_status.opacity.to(1.0, Duration::from_millis(300)),
64                inner_camera.zoom.to(3.0, Duration::from_secs(1)),
65            ],
66            inner_status.opacity.to(0.0, Duration::from_millis(300)),
67            // 3. Reset both
68            all![
69                outer_status.opacity.to(0.5, Duration::from_millis(300)),
70                inner_status.opacity.to(0.5, Duration::from_millis(300)),
71                outer_camera
72                    .position
73                    .to(Vec2::new(0.0, 0.0), Duration::from_secs(1)),
74                inner_camera.zoom.to(1.0, Duration::from_secs(1)),
75            ],
76            all![
77                outer_status.opacity.to(0.0, Duration::from_millis(300)),
78                inner_status.opacity.to(0.0, Duration::from_millis(300)),
79            ],
80        ],
81        None
82    ));
83
84    project.show().expect("Failed to render");
85}
examples/news_feed.rs (line 91)
88fn main() {
89    let mut project = Project::default()
90        .with_fps(60)
91        .with_background(BG)
92        .with_title("News Feed System")
93        .with_dimensions(1920, 1080)
94        .close_on_finish();
95
96    // -- Title --
97    let title = TextNode::default()
98        .with_position(Vec2::new(80.0, 50.0))
99        .with_anchor(Vec2::new(-1.0, -1.0))
100        .with_text("News Feed Architecture")
101        .with_font_size(52.0)
102        .with_font("JetBrains Mono")
103        .with_fill(WHITE)
104        .with_scale(0.95)
105        .with_opacity(0.0);
106
107    // -- Top Tier --
108    let (user_box, user_label) = create_box(
109        "User (Web/App)",
110        Vec2::new(960.0, 130.0),
111        Vec2::new(200.0, 60.0),
112    );
113
114    let (line_lb, lb_end) = create_line(Vec2::new(960.0, 130.0), Vec2::new(960.0, 260.0));
115    let (lb_box, lb_label) = create_box(
116        "Load Balancer",
117        Vec2::new(960.0, 260.0),
118        Vec2::new(160.0, 60.0),
119    );
120
121    let (line_ws, ws_end) = create_line(Vec2::new(960.0, 260.0), Vec2::new(960.0, 390.0));
122    let (ws_box, ws_label) = create_box(
123        "Web Servers",
124        Vec2::new(960.0, 390.0),
125        Vec2::new(300.0, 80.0),
126    );
127
128    // -- Services Tier --
129    let (line_post, post_end) = create_line(Vec2::new(960.0, 390.0), Vec2::new(600.0, 520.0));
130    let (post_box, post_label) = create_box(
131        "Post Service",
132        Vec2::new(600.0, 520.0),
133        Vec2::new(160.0, 60.0),
134    );
135
136    let (line_fanout, fanout_end) = create_line(Vec2::new(960.0, 390.0), Vec2::new(960.0, 520.0));
137    let (fanout_box, fanout_label) = create_box(
138        "Fanout Service",
139        Vec2::new(960.0, 520.0),
140        Vec2::new(180.0, 60.0),
141    );
142
143    let (line_notif, notif_end) = create_line(Vec2::new(960.0, 390.0), Vec2::new(1320.0, 520.0));
144    let (notif_box, notif_label) = create_box(
145        "Notification Service",
146        Vec2::new(1320.0, 520.0),
147        Vec2::new(240.0, 60.0),
148    );
149
150    // -- Post Caches & DB --
151    let (line_post_cache, post_cache_end) =
152        create_line(Vec2::new(600.0, 520.0), Vec2::new(450.0, 720.0));
153    let (post_cache_box, post_cache_label) = create_box(
154        "Post Cache",
155        Vec2::new(450.0, 720.0),
156        Vec2::new(160.0, 80.0),
157    );
158
159    let (line_post_db, post_db_end) = create_line(Vec2::new(600.0, 520.0), Vec2::new(750.0, 720.0));
160    let (post_db_box, post_db_label) =
161        create_box("Post DB", Vec2::new(750.0, 720.0), Vec2::new(140.0, 80.0));
162
163    // -- NF Cache --
164    let (line_nf_cache, nf_cache_end) =
165        create_line(Vec2::new(960.0, 520.0), Vec2::new(960.0, 720.0));
166    let (nf_cache_box, nf_cache_label) = create_box(
167        "News Feed Cache",
168        Vec2::new(960.0, 720.0),
169        Vec2::new(200.0, 80.0),
170    );
171
172    // -- Fanout DBs Flow --
173    let (line_graph_db, graph_db_end) =
174        create_line(Vec2::new(960.0, 520.0), Vec2::new(1280.0, 620.0));
175    let (graph_db_box, graph_db_label) =
176        create_box("Graph DB", Vec2::new(1280.0, 620.0), Vec2::new(140.0, 60.0));
177
178    let (line_user_cache, user_cache_end) =
179        create_line(Vec2::new(960.0, 520.0), Vec2::new(1200.0, 720.0));
180    let (user_cache_box, user_cache_label) = create_box(
181        "User Cache",
182        Vec2::new(1200.0, 720.0),
183        Vec2::new(160.0, 80.0),
184    );
185
186    let (line_user_db, user_db_end) =
187        create_line(Vec2::new(1200.0, 720.0), Vec2::new(1200.0, 860.0));
188    let (user_db_box, user_db_label) =
189        create_box("User DB", Vec2::new(1200.0, 860.0), Vec2::new(140.0, 80.0));
190
191    // -- Case Indicators --
192    let case1 = TextNode::default()
193        .with_position(Vec2::new(80.0, 110.0))
194        .with_text("Scenario 1: Cache Miss (Data Fallback)")
195        .with_anchor(Vec2::new(-1.0, -1.0))
196        .with_font_size(24.0)
197        .with_font("JetBrains Mono")
198        .with_fill(YELLOW)
199        .with_opacity(0.0);
200
201    let case2 = TextNode::default()
202        .with_position(Vec2::new(80.0, 110.0))
203        .with_text("Scenario 2: Cache Hit (Fast Path)")
204        .with_anchor(Vec2::new(-1.0, -1.0))
205        .with_font_size(24.0)
206        .with_font("JetBrains Mono")
207        .with_fill(GREEN)
208        .with_opacity(0.0);
209
210    let case3 = TextNode::default()
211        .with_position(Vec2::new(80.0, 110.0))
212        .with_text("Scenario 3: Rate Limited (Packet Dropped)")
213        .with_font_size(24.0)
214        .with_anchor(Vec2::new(-1.0, -1.0))
215        .with_font("JetBrains Mono")
216        .with_fill(RED)
217        .with_opacity(0.0);
218
219    // -- Flow Step Indicators --
220
221    let case4 = TextNode::default()
222        .with_position(Vec2::new(80.0, 110.0))
223        .with_text("Scenario 4: Full Publish (Parallel)")
224        .with_font_size(24.0)
225        .with_anchor(Vec2::new(-1.0, -1.0))
226        .with_font("JetBrains Mono")
227        .with_fill(GREEN)
228        .with_opacity(0.0);
229
230    let step1 = TextNode::default()
231        .with_position(Vec2::new(1120.0, 570.0))
232        .with_text("1: Get Friend IDs")
233        .with_font_size(16.0)
234        .with_font("JetBrains Mono")
235        .with_fill(YELLOW)
236        .with_opacity(0.0);
237
238    let step2 = TextNode::default()
239        .with_position(Vec2::new(1060.0, 600.0))
240        .with_text("2: Get Friend Data")
241        .with_font_size(16.0)
242        .with_font("JetBrains Mono")
243        .with_fill(YELLOW)
244        .with_opacity(0.0);
245
246    let step3 = TextNode::default()
247        .with_position(Vec2::new(960.0, 620.0))
248        .with_text("3: Update Feed")
249        .with_font_size(16.0)
250        .with_font("JetBrains Mono")
251        .with_fill(YELLOW)
252        .with_opacity(0.0);
253
254    // Initial packet using exact center point
255    let packet = Circle::default()
256        .with_position(Vec2::new(960.0, 130.0))
257        .with_radius(12.0)
258        .with_scale(0.95)
259        .with_opacity(0.0)
260        .with_fill(GREEN);
261
262    let packet_post = Circle::default()
263        .with_position(Vec2::new(960.0, 130.0))
264        .with_radius(12.0)
265        .with_scale(0.95)
266        .with_opacity(0.0)
267        .with_fill(ACCENT);
268
269    let packet_notif = Circle::default()
270        .with_position(Vec2::new(960.0, 130.0))
271        .with_radius(12.0)
272        .with_scale(0.95)
273        .with_opacity(0.0)
274        .with_fill(YELLOW);
275
276    let fast_in = easings::cubic_out;
277    let smooth = easings::cubic_in_out;
278    let appear_dur = Duration::from_millis(250);
279    let move_dur = Duration::from_millis(700);
280
281    // -- Sequence --
282    project.scene.video_timeline.add(chain![
283        wait(Duration::from_millis(500)),
284        all![
285            show(&title, appear_dur),
286            title
287                .scale
288                .to(Vec2::new(1.0, 1.0), appear_dur)
289                .ease(fast_in)
290        ],
291        // Topology Staggered
292        sequence![
293            Duration::from_millis(60),
294            all![
295                show(&user_box, appear_dur),
296                user_box
297                    .scale
298                    .to(Vec2::new(1.0, 1.0), appear_dur)
299                    .ease(fast_in),
300                show(&user_label, appear_dur),
301                user_label
302                    .scale
303                    .to(Vec2::new(1.0, 1.0), appear_dur)
304                    .ease(fast_in),
305            ],
306            all![
307                show(&line_lb, appear_dur),
308                line_lb.end.to(lb_end, appear_dur).ease(smooth),
309                show(&lb_box, appear_dur),
310                lb_box
311                    .scale
312                    .to(Vec2::new(1.0, 1.0), appear_dur)
313                    .ease(fast_in),
314                show(&lb_label, appear_dur),
315                lb_label
316                    .scale
317                    .to(Vec2::new(1.0, 1.0), appear_dur)
318                    .ease(fast_in),
319            ],
320            all![
321                show(&line_ws, appear_dur),
322                line_ws.end.to(ws_end, appear_dur).ease(smooth),
323                show(&ws_box, appear_dur),
324                ws_box
325                    .scale
326                    .to(Vec2::new(1.0, 1.0), appear_dur)
327                    .ease(fast_in),
328                show(&ws_label, appear_dur),
329                ws_label
330                    .scale
331                    .to(Vec2::new(1.0, 1.0), appear_dur)
332                    .ease(fast_in),
333            ]
334        ],
335        sequence![
336            Duration::from_millis(60),
337            all![
338                show(&line_post, appear_dur),
339                line_post.end.to(post_end, appear_dur).ease(smooth),
340                show(&post_box, appear_dur),
341                post_box
342                    .scale
343                    .to(Vec2::new(1.0, 1.0), appear_dur)
344                    .ease(fast_in),
345                show(&post_label, appear_dur),
346                post_label
347                    .scale
348                    .to(Vec2::new(1.0, 1.0), appear_dur)
349                    .ease(fast_in),
350            ],
351            all![
352                show(&line_fanout, appear_dur),
353                line_fanout.end.to(fanout_end, appear_dur).ease(smooth),
354                show(&fanout_box, appear_dur),
355                fanout_box
356                    .scale
357                    .to(Vec2::new(1.0, 1.0), appear_dur)
358                    .ease(fast_in),
359                show(&fanout_label, appear_dur),
360                fanout_label
361                    .scale
362                    .to(Vec2::new(1.0, 1.0), appear_dur)
363                    .ease(fast_in),
364            ],
365            all![
366                show(&line_notif, appear_dur),
367                line_notif.end.to(notif_end, appear_dur).ease(smooth),
368                show(&notif_box, appear_dur),
369                notif_box
370                    .scale
371                    .to(Vec2::new(1.0, 1.0), appear_dur)
372                    .ease(fast_in),
373                show(&notif_label, appear_dur),
374                notif_label
375                    .scale
376                    .to(Vec2::new(1.0, 1.0), appear_dur)
377                    .ease(fast_in),
378            ],
379        ],
380        sequence![
381            Duration::from_millis(60),
382            all![
383                show(&line_post_cache, appear_dur),
384                line_post_cache
385                    .end
386                    .to(post_cache_end, appear_dur)
387                    .ease(smooth),
388                show(&post_cache_box, appear_dur),
389                post_cache_box
390                    .scale
391                    .to(Vec2::new(1.0, 1.0), appear_dur)
392                    .ease(fast_in),
393                show(&post_cache_label, appear_dur),
394                post_cache_label
395                    .scale
396                    .to(Vec2::new(1.0, 1.0), appear_dur)
397                    .ease(fast_in),
398            ],
399            all![
400                show(&line_post_db, appear_dur),
401                line_post_db.end.to(post_db_end, appear_dur).ease(smooth),
402                show(&post_db_box, appear_dur),
403                post_db_box
404                    .scale
405                    .to(Vec2::new(1.0, 1.0), appear_dur)
406                    .ease(fast_in),
407                show(&post_db_label, appear_dur),
408                post_db_label
409                    .scale
410                    .to(Vec2::new(1.0, 1.0), appear_dur)
411                    .ease(fast_in),
412            ],
413            all![
414                show(&line_nf_cache, appear_dur),
415                line_nf_cache.end.to(nf_cache_end, appear_dur).ease(smooth),
416                show(&nf_cache_box, appear_dur),
417                nf_cache_box
418                    .scale
419                    .to(Vec2::new(1.0, 1.0), appear_dur)
420                    .ease(fast_in),
421                show(&nf_cache_label, appear_dur),
422                nf_cache_label
423                    .scale
424                    .to(Vec2::new(1.0, 1.0), appear_dur)
425                    .ease(fast_in),
426            ],
427            all![
428                show(&line_graph_db, appear_dur),
429                line_graph_db.end.to(graph_db_end, appear_dur).ease(smooth),
430                show(&graph_db_box, appear_dur),
431                graph_db_box
432                    .scale
433                    .to(Vec2::new(1.0, 1.0), appear_dur)
434                    .ease(fast_in),
435                show(&graph_db_label, appear_dur),
436                graph_db_label
437                    .scale
438                    .to(Vec2::new(1.0, 1.0), appear_dur)
439                    .ease(fast_in),
440            ],
441            all![
442                show(&line_user_cache, appear_dur),
443                line_user_cache
444                    .end
445                    .to(user_cache_end, appear_dur)
446                    .ease(smooth),
447                show(&user_cache_box, appear_dur),
448                user_cache_box
449                    .scale
450                    .to(Vec2::new(1.0, 1.0), appear_dur)
451                    .ease(fast_in),
452                show(&user_cache_label, appear_dur),
453                user_cache_label
454                    .scale
455                    .to(Vec2::new(1.0, 1.0), appear_dur)
456                    .ease(fast_in),
457            ],
458            all![
459                show(&line_user_db, appear_dur),
460                line_user_db.end.to(user_db_end, appear_dur).ease(smooth),
461                show(&user_db_box, appear_dur),
462                user_db_box
463                    .scale
464                    .to(Vec2::new(1.0, 1.0), appear_dur)
465                    .ease(fast_in),
466                show(&user_db_label, appear_dur),
467                user_db_label
468                    .scale
469                    .to(Vec2::new(1.0, 1.0), appear_dur)
470                    .ease(fast_in),
471            ]
472        ],
473        wait(Duration::from_secs(1)),
474        // ==========================================
475        //  SCENARIO 1: CACHE MISS
476        // ==========================================
477        all![
478            show(&case1, appear_dur),
479            show(&packet, Duration::from_millis(200)),
480            packet.fill_color.to(GREEN, Duration::from_millis(0)), // Start healthy
481            packet
482                .scale
483                .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
484                .ease(fast_in),
485            packet
486                .position
487                .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
488        ],
489        packet
490            .position
491            .to(Vec2::new(960.0, 260.0), move_dur)
492            .ease(smooth),
493        packet
494            .position
495            .to(Vec2::new(960.0, 390.0), move_dur)
496            .ease(smooth),
497        packet
498            .position
499            .to(Vec2::new(960.0, 520.0), move_dur)
500            .ease(smooth),
501        wait(Duration::from_millis(200)),
502        // 1: Graph DB Check
503        all![
504            packet
505                .position
506                .to(Vec2::new(1280.0, 620.0), move_dur)
507                .ease(smooth),
508            delay![move_dur / 2, show(&step1, appear_dur)]
509        ],
510        packet
511            .position
512            .to(Vec2::new(960.0, 520.0), move_dur)
513            .ease(smooth),
514        // 2: User Cache MISSED -> Query DB
515        all![
516            packet
517                .position
518                .to(Vec2::new(1200.0, 720.0), move_dur)
519                .ease(smooth),
520            delay![move_dur / 2, show(&step2, appear_dur)]
521        ],
522        // Alert Miss: Flash Yellow and head to DB
523        all![
524            packet.fill_color.to(YELLOW, Duration::from_millis(200)),
525            packet
526                .scale
527                .to(Vec2::new(1.2, 1.2), Duration::from_millis(200))
528                .ease(easings::bounce_out),
529            delay![
530                Duration::from_millis(200),
531                packet
532                    .position
533                    .to(Vec2::new(1200.0, 860.0), move_dur)
534                    .ease(smooth)
535            ]
536        ],
537        // Return from DB to Fanout smoothly
538        all![
539            packet.fill_color.to(GREEN, Duration::from_millis(200)),
540            packet
541                .scale
542                .to(Vec2::new(1.0, 1.0), Duration::from_millis(200)),
543            packet
544                .position
545                .to(Vec2::new(1200.0, 720.0), move_dur)
546                .ease(smooth)
547        ],
548        packet
549            .position
550            .to(Vec2::new(960.0, 520.0), move_dur)
551            .ease(smooth),
552        // 3: Write to News Feed Cache
553        all![
554            packet
555                .position
556                .to(Vec2::new(960.0, 720.0), move_dur)
557                .ease(smooth),
558            delay![move_dur / 2, show(&step3, appear_dur)]
559        ],
560        packet
561            .position
562            .to(Vec2::new(960.0, 520.0), move_dur)
563            .ease(smooth),
564        wait(Duration::from_millis(200)),
565        // Packet Flow (Back Track to User)
566        packet
567            .position
568            .to(Vec2::new(960.0, 390.0), move_dur)
569            .ease(smooth),
570        packet
571            .position
572            .to(Vec2::new(960.0, 260.0), move_dur)
573            .ease(smooth),
574        packet
575            .position
576            .to(Vec2::new(960.0, 130.0), move_dur)
577            .ease(smooth),
578        // Hide packet & Case states
579        all![
580            hide(&packet, Duration::from_millis(200)),
581            hide(&case1, appear_dur),
582            hide(&step1, appear_dur),
583            hide(&step2, appear_dur),
584            hide(&step3, appear_dur),
585        ],
586        wait(Duration::from_secs(1)),
587        // ==========================================
588        //  SCENARIO 2: CACHE HIT
589        // ==========================================
590        all![
591            show(&case2, appear_dur),
592            show(&packet, Duration::from_millis(200)),
593            packet
594                .position
595                .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
596            packet
597                .scale
598                .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
599                .ease(fast_in),
600        ],
601        packet
602            .position
603            .to(Vec2::new(960.0, 260.0), move_dur)
604            .ease(smooth),
605        packet
606            .position
607            .to(Vec2::new(960.0, 390.0), move_dur)
608            .ease(smooth),
609        packet
610            .position
611            .to(Vec2::new(960.0, 520.0), move_dur)
612            .ease(smooth),
613        // 1: Graph DB
614        all![
615            packet
616                .position
617                .to(Vec2::new(1280.0, 620.0), move_dur)
618                .ease(smooth),
619            delay![move_dur / 2, show(&step1, appear_dur)]
620        ],
621        packet
622            .position
623            .to(Vec2::new(960.0, 520.0), move_dur)
624            .ease(smooth),
625        // 2: User Cache HIT (Skip DB entirely)
626        all![
627            packet
628                .position
629                .to(Vec2::new(1200.0, 720.0), move_dur)
630                .ease(smooth),
631            delay![move_dur / 2, show(&step2, appear_dur)]
632        ],
633        packet
634            .position
635            .to(Vec2::new(960.0, 520.0), move_dur)
636            .ease(smooth),
637        // 3: News Feed Cache
638        all![
639            packet
640                .position
641                .to(Vec2::new(960.0, 720.0), move_dur)
642                .ease(smooth),
643            delay![move_dur / 2, show(&step3, appear_dur)]
644        ],
645        packet
646            .position
647            .to(Vec2::new(960.0, 520.0), move_dur)
648            .ease(smooth),
649        // Return
650        packet
651            .position
652            .to(Vec2::new(960.0, 390.0), move_dur)
653            .ease(smooth),
654        packet
655            .position
656            .to(Vec2::new(960.0, 260.0), move_dur)
657            .ease(smooth),
658        packet
659            .position
660            .to(Vec2::new(960.0, 130.0), move_dur)
661            .ease(smooth),
662        all![
663            hide(&packet, Duration::from_millis(200)),
664            hide(&case2, appear_dur),
665            hide(&step1, appear_dur),
666            hide(&step2, appear_dur),
667            hide(&step3, appear_dur),
668        ],
669        wait(Duration::from_secs(1)),
670        // ==========================================
671        //  SCENARIO 3: DROPPED PACKET
672        // ==========================================
673        all![
674            show(&case3, appear_dur),
675            show(&packet, Duration::from_millis(200)),
676            packet.fill_color.to(GREEN, Duration::from_millis(0)),
677            packet
678                .position
679                .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
680            packet
681                .scale
682                .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
683                .ease(fast_in),
684        ],
685        // To LB
686        packet
687            .position
688            .to(Vec2::new(960.0, 260.0), move_dur)
689            .ease(smooth),
690        // Moving to Web Servers but fails mid-way / at LB
691        all![
692            packet
693                .position
694                .to(Vec2::new(960.0, 390.0), move_dur)
695                .ease(smooth),
696            // Turns Red and gets destroyed
697            sequence![
698                move_dur / 2, // Wait until halfway
699                all![
700                    packet.fill_color.to(RED, Duration::from_millis(150)),
701                    packet
702                        .scale
703                        .to(Vec2::new(1.8, 1.8), Duration::from_millis(150))
704                        .ease(easings::elastic_out),
705                ],
706                all![
707                    hide(&packet, Duration::from_millis(200)),
708                    packet
709                        .scale
710                        .to(Vec2::new(0.0, 0.0), Duration::from_millis(200))
711                        .ease(easings::cubic_in),
712                ]
713            ]
714        ],
715        hide(&case3, appear_dur),
716        wait(Duration::from_secs(1)),
717        // ==========================================
718        //  SCENARIO 4: PARALLEL PUBLISH
719        // ==========================================
720        all![
721            show(&case4, appear_dur),
722            show(&packet, Duration::from_millis(200)),
723            show(&packet_post, Duration::from_millis(200)),
724            show(&packet_notif, Duration::from_millis(200)),
725            packet.fill_color.to(GREEN, Duration::from_millis(0)),
726            packet
727                .position
728                .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
729            packet
730                .scale
731                .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
732                .ease(fast_in),
733            packet_post
734                .position
735                .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
736            packet_post
737                .scale
738                .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
739                .ease(fast_in),
740            packet_notif
741                .position
742                .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
743            packet_notif
744                .scale
745                .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
746                .ease(fast_in),
747        ],
748        // Flow down collectively to Web Servers
749        all![
750            packet
751                .position
752                .to(Vec2::new(960.0, 260.0), move_dur)
753                .ease(smooth),
754            packet_post
755                .position
756                .to(Vec2::new(960.0, 260.0), move_dur)
757                .ease(smooth),
758            packet_notif
759                .position
760                .to(Vec2::new(960.0, 260.0), move_dur)
761                .ease(smooth),
762        ],
763        all![
764            packet
765                .position
766                .to(Vec2::new(960.0, 390.0), move_dur)
767                .ease(smooth),
768            packet_post
769                .position
770                .to(Vec2::new(960.0, 390.0), move_dur)
771                .ease(smooth),
772            packet_notif
773                .position
774                .to(Vec2::new(960.0, 390.0), move_dur)
775                .ease(smooth),
776        ],
777        // BRANCHING!
778        all![
779            // Main packet goes to fanout
780            packet
781                .position
782                .to(Vec2::new(960.0, 520.0), move_dur)
783                .ease(smooth),
784            // Post packet to Post Service
785            packet_post
786                .position
787                .to(Vec2::new(600.0, 520.0), move_dur)
788                .ease(smooth),
789            // Notif packet to Notification Service
790            packet_notif
791                .position
792                .to(Vec2::new(1320.0, 520.0), move_dur)
793                .ease(smooth),
794        ],
795        wait(Duration::from_millis(200)),
796        // Post DB, NF Cache, Notifications APNS fly off
797        all![
798            // Fanout updates NF Cache
799            packet
800                .position
801                .to(Vec2::new(960.0, 720.0), move_dur)
802                .ease(smooth),
803            // Post updates DB
804            packet_post
805                .position
806                .to(Vec2::new(750.0, 720.0), move_dur)
807                .ease(smooth),
808            // Notif flies off-screen (Apple Push Notification Server etc)
809            packet_notif
810                .position
811                .to(Vec2::new(1500.0, 520.0), move_dur)
812                .ease(smooth),
813            hide(&packet_notif, move_dur),
814        ],
815        wait(Duration::from_millis(200)),
816        // Return
817        all![
818            packet
819                .position
820                .to(Vec2::new(960.0, 520.0), move_dur)
821                .ease(smooth),
822            packet_post
823                .position
824                .to(Vec2::new(600.0, 520.0), move_dur)
825                .ease(smooth),
826        ],
827        all![
828            packet
829                .position
830                .to(Vec2::new(960.0, 390.0), move_dur)
831                .ease(smooth),
832            packet_post
833                .position
834                .to(Vec2::new(960.0, 390.0), move_dur)
835                .ease(smooth),
836        ],
837        all![
838            packet
839                .position
840                .to(Vec2::new(960.0, 260.0), move_dur)
841                .ease(smooth),
842            packet_post
843                .position
844                .to(Vec2::new(960.0, 260.0), move_dur)
845                .ease(smooth),
846        ],
847        all![
848            packet
849                .position
850                .to(Vec2::new(960.0, 130.0), move_dur)
851                .ease(smooth),
852            packet_post
853                .position
854                .to(Vec2::new(960.0, 130.0), move_dur)
855                .ease(smooth),
856        ],
857        // Hide
858        all![
859            hide(&packet, Duration::from_millis(200)),
860            hide(&packet_post, Duration::from_millis(200)),
861            hide(&case4, appear_dur),
862        ],
863        wait(Duration::from_secs(2)),
864    ]);
865
866    // -- Adding to Scene --
867    for element in [
868        Box::new(title) as Box<dyn Node>,
869        Box::new(line_lb),
870        Box::new(line_ws),
871        Box::new(line_post),
872        Box::new(line_fanout),
873        Box::new(line_notif),
874        Box::new(line_post_cache),
875        Box::new(line_post_db),
876        Box::new(line_nf_cache),
877        Box::new(line_graph_db),
878        Box::new(line_user_cache),
879        Box::new(line_user_db),
880        Box::new(user_box),
881        Box::new(user_label),
882        Box::new(lb_box),
883        Box::new(lb_label),
884        Box::new(ws_box),
885        Box::new(ws_label),
886        Box::new(post_box),
887        Box::new(post_label),
888        Box::new(fanout_box),
889        Box::new(fanout_label),
890        Box::new(notif_box),
891        Box::new(notif_label),
892        Box::new(post_cache_box),
893        Box::new(post_cache_label),
894        Box::new(post_db_box),
895        Box::new(post_db_label),
896        Box::new(nf_cache_box),
897        Box::new(nf_cache_label),
898        Box::new(graph_db_box),
899        Box::new(graph_db_label),
900        Box::new(user_cache_box),
901        Box::new(user_cache_label),
902        Box::new(user_db_box),
903        Box::new(user_db_label),
904        Box::new(case1),
905        Box::new(case2),
906        Box::new(case3),
907        Box::new(step1),
908        Box::new(step2),
909        Box::new(step3),
910        Box::new(case4),
911        Box::new(packet_post),
912        Box::new(packet_notif),
913        Box::new(packet),
914    ] {
915        project.scene.add(element);
916    }
917
918    project.show().expect("Failed to render");
919}
examples/explainer.rs (line 153)
148fn main() {
149    let mut project = Project::default()
150        .with_dimensions(CANVAS_W, CANVAS_H)
151        .with_fps(60)
152        .with_title("Explainer")
153        .with_background(BG)
154        .close_on_finish();
155
156    // =====================================================================
157    //  S1: TITLE CARD
158    // =====================================================================
159    let s1_line = hline(100.0);
160    let s1_title = TextNode::default()
161        .with_anchor(Vec2::new(-1.0, -1.0))
162        .with_position(Vec2::new(LEFT, 120.0))
163        .with_text("motion-canvas-rs")
164        .with_font_size(52.0)
165        .with_fill(ACCENT)
166        .with_font(FONT)
167        .with_opacity(0.0);
168    let s1_sub = h2("A GPU-Accelerated Vector Animation Engine", 185.0);
169    let s1_built = body(
170        "Built on Vello + Typst  —  Inspired by Motion Canvas",
171        220.0,
172    );
173    let s1_desc = body(
174        "This animation will teach you how the library works,",
175        280.0,
176    );
177    let s1_desc2 = body(
178        "from struct definitions to the GPU rendering pipeline.",
179        305.0,
180    );
181
182    let s1_logo = ImageNode::default()
183        .with_position(Vec2::new(950.0, 420.0))
184        .with_path("examples/images/motion-canvas-rs.svg")
185        .with_scale(0.3)
186        .with_opacity(0.0);
187
188    for n in [&s1_title, &s1_sub, &s1_built, &s1_desc, &s1_desc2] {
189        project.scene.add(Box::new(n.clone()));
190    }
191    project.scene.add(Box::new(s1_line.clone()));
192    project.scene.add(Box::new(s1_logo.clone()));
193
194    // =====================================================================
195    //  S2: THE 5 STEPS
196    // =====================================================================
197    let s2_h = title("Every program follows 5 steps", 50.0);
198    let steps = [
199        "1. Create a Project       — your canvas settings",
200        "2. Create Nodes           — shapes, text, images",
201        "3. Add Nodes to Scene     — what gets drawn",
202        "4. Animate the Timeline   — how things move",
203        "5. Show or Export         — live window or video",
204    ];
205    let s2_texts: Vec<TextNode> = steps
206        .iter()
207        .enumerate()
208        .map(|(i, s)| body(s, 120.0 + i as f32 * 35.0))
209        .collect();
210
211    project.scene.add(Box::new(s2_h.clone()));
212    for t in &s2_texts {
213        project.scene.add(Box::new(t.clone()));
214    }
215
216    // =====================================================================
217    //  S3: WHAT IS A STRUCT?  +  The Project struct
218    // =====================================================================
219    let s3_h = title("What is a 'struct'?", 50.0);
220    let s3_explain = h2(
221        "A struct is a container that groups related data together.",
222        95.0,
223    );
224    let s3_analogy = body(
225        "Think of it like a class in Python/JS, but it only holds data.",
226        130.0,
227    );
228
229    let s3_code = code_block(
230        "pub struct Project {
231    pub width: u32,          // Canvas width in pixels
232    pub height: u32,         // Canvas height in pixels
233    pub fps: u32,            // Frames per second
234    pub title: String,       // Window title
235    pub scene: BaseScene,    // Holds nodes + timelines
236    pub background_color: Color,
237    pub close_on_finish: bool,
238}",
239        175.0,
240    );
241
242    let s3_note = note(
243        "^ This is the actual Project struct from the library.",
244        420.0,
245    );
246    let s3_note2 = body(
247        "'pub' means public — anyone can read/write these fields.",
248        455.0,
249    );
250    let s3_note3 = body(
251        "u32 = unsigned 32-bit integer,  String = text,  bool = true/false",
252        485.0,
253    );
254
255    for n in [
256        &s3_h,
257        &s3_explain,
258        &s3_analogy,
259        &s3_note,
260        &s3_note2,
261        &s3_note3,
262    ] {
263        project.scene.add(Box::new(n.clone()));
264    }
265    project.scene.add(Box::new(s3_code.clone()));
266
267    // =====================================================================
268    //  S4: WHAT IS impl?  +  Builder pattern
269    // =====================================================================
270    let s4_h = title("What is 'impl'?", 50.0);
271    let s4_explain = h2("impl adds methods (functions) to a struct.", 95.0);
272    let s4_analogy = body(
273        "Like adding methods to a class. Separated from the data.",
274        130.0,
275    );
276
277    let s4_code = code_block(
278        "impl Project {
279    pub fn with_fps(mut self, fps: u32) -> Self {
280        self.fps = fps;   // set the value
281        self              // return yourself (builder pattern)
282    }
283    pub fn with_title(mut self, title: &str) -> Self {
284        self.title = title.to_string();
285        self
286    }
287}",
288        175.0,
289    );
290
291    let s4_usage = body("Usage — chain calls to configure:", 430.0);
292    let s4_usage_code = code_block(
293        "let project = Project::default()
294    .with_fps(60)
295    .with_title(\"My Animation\")
296    .with_dimensions(800, 600)
297    .close_on_finish();",
298        460.0,
299    );
300
301    let s4_note = note(
302        "Each .with_*() returns 'self', so you can chain them.",
303        590.0,
304    );
305
306    for n in [&s4_h, &s4_explain, &s4_analogy, &s4_usage, &s4_note] {
307        project.scene.add(Box::new(n.clone()));
308    }
309    project.scene.add(Box::new(s4_code.clone()));
310    project.scene.add(Box::new(s4_usage_code.clone()));
311
312    // =====================================================================
313    //  S5: WHAT IS A TRAIT?  +  The Node trait
314    // =====================================================================
315    let s5_h = title("What is a 'trait'?", 50.0);
316    let s5_explain = h2(
317        "A trait is a contract — like an interface in Java/TypeScript.",
318        95.0,
319    );
320    let s5_analogy = body(
321        "Any type that implements a trait promises to provide those methods.",
322        130.0,
323    );
324
325    let s5_code = code_block(
326        "pub trait Node: Send + Sync + 'static {
327    fn render(&self, scene: &mut Scene,
328              parent_transform: Affine,
329              parent_opacity: f32);
330    fn update(&mut self, dt: Duration);
331    fn state_hash(&self) -> u64;
332    fn clone_node(&self) -> Box<dyn Node>;
333}",
334        175.0,
335    );
336
337    let s5_r = note(
338        "render()     — draw yourself using current signal values",
339        410.0,
340    );
341    let s5_u = note(
342        "update(dt)   — called every frame (for per-frame logic)",
343        435.0,
344    );
345    let s5_s = note(
346        "state_hash() — returns a number that changes when you change",
347        460.0,
348    );
349    let s5_c = note("clone_node() — make a deep copy of yourself", 485.0);
350    let s5_every = body(
351        "Circle, Rect, Line, TextNode, Polygon all implement Node.",
352        530.0,
353    );
354
355    for n in [
356        &s5_h,
357        &s5_explain,
358        &s5_analogy,
359        &s5_r,
360        &s5_u,
361        &s5_s,
362        &s5_c,
363        &s5_every,
364    ] {
365        project.scene.add(Box::new(n.clone()));
366    }
367    project.scene.add(Box::new(s5_code.clone()));
368
369    // =====================================================================
370    //  S6: NODE GALLERY  (visual demo)
371    // =====================================================================
372    let s6_h = title("The Built-in Nodes", 50.0);
373    let s6_sub = body(
374        "Each node uses the builder pattern and stores properties as Signals.",
375        90.0,
376    );
377
378    let demo_c = Circle::default()
379        .with_position(Vec2::new(120.0, 230.0))
380        .with_radius(40.0)
381        .with_fill(RED)
382        .with_opacity(0.0);
383    let demo_r = Rect::default()
384        .with_position(Vec2::new(293.0, 230.0))
385        .with_size(Vec2::new(80.0, 80.0))
386        .with_fill(ACCENT)
387        .with_radius(8.0)
388        .with_opacity(0.0);
389    let demo_l = Line::default()
390        .with_start(Vec2::new(420.0, 200.0))
391        .with_end(Vec2::new(510.0, 270.0))
392        .with_stroke(WHITE, 3.0)
393        .with_opacity(0.0);
394    let demo_p = Polygon::regular(5, 40.0)
395        .with_position(Vec2::new(606.0, 230.0))
396        .with_fill(YELLOW)
397        .with_opacity(0.0);
398    let demo_t = TextNode::default()
399        .with_position(Vec2::new(765.0, 230.0))
400        .with_text("Abc")
401        .with_font_size(36.0)
402        .with_fill(GREEN)
403        .with_font(FONT)
404        .with_opacity(0.0);
405
406    let lc = dim("Circle", 95.0, 285.0);
407    let lr = dim("Rect", 280.0, 285.0);
408    let ll = dim("Line", 445.0, 285.0);
409    let lp = dim("Polygon", 580.0, 285.0);
410    let lt = dim("TextNode", 735.0, 285.0);
411
412    let s6_box_h = h2("Why Box<dyn Node>?", 340.0);
413    let s6_box1 = body("The scene stores different node types in one list:", 375.0);
414    let s6_box_code = code_block(
415        "pub struct BaseScene {
416    pub nodes: Vec<Box<dyn Node>>,  // a list of \"any Node\"
417}
418// 'Box' = heap-allocated,  'dyn Node' = any type implementing Node
419// Like List<INode> in Java or Array<Node> in TypeScript
420project.scene.add(Box::new(circle));  // wrap + add",
421        405.0,
422    );
423
424    for n in [&s6_h, &s6_sub, &lc, &lr, &ll, &lp, &lt, &s6_box_h, &s6_box1] {
425        project.scene.add(Box::new(n.clone()));
426    }
427    project.scene.add(Box::new(demo_c.clone()));
428    project.scene.add(Box::new(demo_r.clone()));
429    project.scene.add(Box::new(demo_l.clone()));
430    project.scene.add(Box::new(demo_p.clone()));
431    project.scene.add(Box::new(demo_t.clone()));
432    project.scene.add(Box::new(s6_box_code.clone()));
433
434    // =====================================================================
435    //  S7: SIGNALS — The Reactive Core
436    // =====================================================================
437    let s7_h = title("Signals — The Reactive Core", 50.0);
438    let s7_sub = h2("Every animatable property is a Signal<T>.", 95.0);
439
440    let s7_code = code_block(
441        "pub struct Signal<T> {
442    pub data: Arc<Mutex<SignalData<T>>>,
443}
444pub struct SignalData<T> {
445    pub value: T,  // the actual value (f32, Vec2, Color...)
446}",
447        140.0,
448    );
449
450    let s7_arc = note("Arc = shared pointer. Multiple owners, same data.", 310.0);
451    let s7_mutex = note(
452        "Mutex = lock. Only one thread reads/writes at a time.",
453        335.0,
454    );
455    let s7_why = body(
456        "Why? A node and its animation both need the same property:",
457        380.0,
458    );
459
460    let s7_diagram_code = code_block(
461        "let circle = Circle::default().with_radius(50.0);
462// circle.radius is a Signal<f32>
463
464circle.radius.to(100.0, Duration::from_secs(1));
465// Creates a SignalTween with a CLONE of circle.radius
466// Both point to the SAME underlying value (via Arc)
467
468// The animation WRITES new values each frame
469// The node READS them when rendering",
470        420.0,
471    );
472
473    // Live demo circle
474    let sig_demo = Circle::default()
475        .with_position(Vec2::new(1000.0, 400.0))
476        .with_radius(50.0)
477        .with_fill(RED)
478        .with_stroke(Color::rgba8(255, 255, 255, 50), 2.0)
479        .with_opacity(0.0);
480    let sig_lbl = dim("Live Signal demo", 900.0, 150.0);
481
482    for n in [&s7_h, &s7_sub, &s7_arc, &s7_mutex, &s7_why, &sig_lbl] {
483        project.scene.add(Box::new(n.clone()));
484    }
485    project.scene.add(Box::new(s7_code.clone()));
486    project.scene.add(Box::new(s7_diagram_code.clone()));
487    project.scene.add(Box::new(sig_demo.clone()));
488
489    // =====================================================================
490    //  S8: SIGNAL TWEEN — How animations work per-frame
491    // =====================================================================
492    let s8_h = title("SignalTween — The Animation Engine", 50.0);
493    let s8_sub = body(
494        ".to() creates a SignalTween that interpolates over time:",
495        90.0,
496    );
497
498    let s8_code = code_block(
499        "pub struct SignalTween<T> {
500    data: Arc<Mutex<SignalData<T>>>,  // shared ref to signal
501    start_value: Option<T>,           // captured on FIRST update (lazy!)
502    target_value: Option<T>,          // where we're going
503    duration: Duration,               // how long
504    elapsed: Duration,                // how much time passed
505    easing: fn(f32) -> f32,           // curve function
506}",
507        125.0,
508    );
509
510    let s8_how = h2("Each frame update:", 340.0);
511    let s8_steps = [
512        "1. elapsed += dt",
513        "2. t_linear = elapsed / duration          (0.0 to 1.0)",
514        "3. t_eased  = easing(t_linear)            (curved)",
515        "4. value    = lerp(start, target, t)      (interpolate)",
516        "5. Write value into Signal                (node sees it)",
517        "6. If elapsed >= duration: finished!      (return leftover dt)",
518    ];
519    let s8_step_texts: Vec<TextNode> = s8_steps
520        .iter()
521        .enumerate()
522        .map(|(i, s)| body(s, 370.0 + i as f32 * 28.0))
523        .collect();
524
525    let s8_lazy = note(
526        "start_value is captured lazily — so chained tweens read the",
527        570.0,
528    );
529    let s8_lazy2 = note(
530        "correct value at their actual start time, not creation time.",
531        590.0,
532    );
533
534    // Progress bar
535    let prog_bg = Rect::default()
536        .with_position(Vec2::new(895.0, 150.0))
537        .with_size(Vec2::new(400.0, 16.0))
538        .with_fill(Color::rgba8(255, 255, 255, 15))
539        .with_radius(8.0)
540        .with_opacity(0.0);
541    let prog_fill = Rect::default()
542        .with_position(Vec2::new(695.0, 154.0))
543        .with_anchor(Vec2::new(-1.0, 0.5))
544        .with_size(Vec2::new(0.0, 16.0))
545        .with_fill(ACCENT)
546        .with_radius(8.0)
547        .with_opacity(0.0);
548    let plbl0 = dim("t=0", 700.0, 172.0);
549    let plbl1 = dim("t=1", 1070.0, 172.0);
550    let tween_ball = Circle::default()
551        .with_position(Vec2::new(900.0, 430.0))
552        .with_radius(30.0)
553        .with_fill(RED)
554        .with_opacity(0.0);
555    let tween_lbl = dim("radius animating: 30 -> 80", 760.0, 220.0);
556
557    for n in [
558        &s8_h, &s8_sub, &s8_how, &s8_lazy, &s8_lazy2, &plbl0, &plbl1, &tween_lbl,
559    ] {
560        project.scene.add(Box::new(n.clone()));
561    }
562    project.scene.add(Box::new(s8_code.clone()));
563    for t in &s8_step_texts {
564        project.scene.add(Box::new(t.clone()));
565    }
566    project.scene.add(Box::new(prog_bg.clone()));
567    project.scene.add(Box::new(prog_fill.clone()));
568    project.scene.add(Box::new(tween_ball.clone()));
569
570    // =====================================================================
571    //  S9: TWEENABLE + EASINGS
572    // =====================================================================
573    let s9_h = title("Tweenable — What Can Be Animated", 50.0);
574    let s9_code = code_block(
575        "pub trait Tweenable: Clone + Send + Sync {
576    fn interpolate(a: &Self, b: &Self, t: f32) -> Self;
577    fn state_hash(&self) -> u64;
578}
579// Implemented for: f32, Vec2, Color, String, Affine, Vec<Vec2>
580//   f32:    lerp(a, b, t) = a + (b-a)*t
581//   Vec2:   lerp x and y independently
582//   Color:  lerp R,G,B,A channels independently
583//   String: snap — returns 'a' until t>=1, then 'b'",
584        95.0,
585    );
586
587    let s9_easing_h = h2("Easing functions curve the linear t:", 310.0);
588    let s9_easing_desc = body("Same distance, same duration — different feel.", 340.0);
589
590    let enames = [
591        "linear",
592        "cubic_in_out",
593        "elastic_out",
594        "bounce_out",
595        "back_out",
596    ];
597    let ecolors = [WHITE, ACCENT, RED, YELLOW, GREEN];
598    let mut eballs: Vec<Circle> = Vec::new();
599    let mut elabels: Vec<TextNode> = Vec::new();
600    for (i, name) in enames.iter().enumerate() {
601        let y = 390.0 + i as f32 * 55.0;
602        let b = Circle::default()
603            .with_position(Vec2::new(250.0, y))
604            .with_radius(12.0)
605            .with_fill(ecolors[i])
606            .with_opacity(0.0);
607        let l = dim(name, LEFT, y - 5.0);
608        project.scene.add(Box::new(b.clone()));
609        project.scene.add(Box::new(l.clone()));
610        eballs.push(b);
611        elabels.push(l);
612    }
613    project.scene.add(Box::new(s9_h.clone()));
614    project.scene.add(Box::new(s9_code.clone()));
615    project.scene.add(Box::new(s9_easing_h.clone()));
616    project.scene.add(Box::new(s9_easing_desc.clone()));
617
618    // =====================================================================
619    //  S10: FLOW CONTROLS
620    // =====================================================================
621    let s10_h = title("Flow Controls — Composing Animations", 50.0);
622    let s10_sub = body(
623        "Individual tweens are simple. Power comes from composing them.",
624        90.0,
625    );
626
627    // chain
628    let s10_chain_h = h2("chain![ ] — one after another", 140.0);
629    let chain_d: Vec<Circle> = (0..3)
630        .map(|i| {
631            Circle::default()
632                .with_position(Vec2::new(LEFT + 30.0 + i as f32 * 60.0, 200.0))
633                .with_radius(18.0)
634                .with_fill([RED, ACCENT, YELLOW][i])
635                .with_opacity(0.0)
636        })
637        .collect();
638
639    // all
640    let s10_all_h = h2("all![ ] — all at the same time", 270.0);
641    let all_d: Vec<Circle> = (0..3)
642        .map(|i| {
643            Circle::default()
644                .with_position(Vec2::new(LEFT + 30.0 + i as f32 * 60.0, 330.0))
645                .with_radius(18.0)
646                .with_fill([RED, ACCENT, YELLOW][i])
647                .with_opacity(0.0)
648        })
649        .collect();
650
651    // sequence
652    let s10_seq_h = h2("sequence![ ] — staggered starts", 400.0);
653    let seq_d: Vec<Circle> = (0..3)
654        .map(|i| {
655            Circle::default()
656                .with_position(Vec2::new(LEFT + 30.0 + i as f32 * 60.0, 460.0))
657                .with_radius(18.0)
658                .with_fill([RED, ACCENT, YELLOW][i])
659                .with_opacity(0.0)
660        })
661        .collect();
662
663    let s10_code = code_block(
664        "chain![ a, b, c ]           // a then b then c
665all![ a, b, c ]             // a + b + c together
666sequence![ 200ms, a, b, c ] // staggered
667delay![ 500ms, a ]          // wait then play
668wait(1s)                    // pause
669any![ a, b ]                // race: first wins
670loop_anim![ a, 3 ]          // repeat 3 times",
671        510.0,
672    );
673
674    project.scene.add(Box::new(s10_h.clone()));
675    project.scene.add(Box::new(s10_sub.clone()));
676    project.scene.add(Box::new(s10_chain_h.clone()));
677    project.scene.add(Box::new(s10_all_h.clone()));
678    project.scene.add(Box::new(s10_seq_h.clone()));
679    project.scene.add(Box::new(s10_code.clone()));
680    for d in &chain_d {
681        project.scene.add(Box::new(d.clone()));
682    }
683    for d in &all_d {
684        project.scene.add(Box::new(d.clone()));
685    }
686    for d in &seq_d {
687        project.scene.add(Box::new(d.clone()));
688    }
689
690    // =====================================================================
691    //  S11: TIMELINE + RENDERING
692    // =====================================================================
693    let s11_h = title("The Timeline — Animation Queue", 50.0);
694    let s11_code = code_block(
695        "pub struct Timeline {
696    pub animations: Vec<Box<dyn Animation>>,
697}
698impl Timeline {
699    fn update(&mut self, mut dt: Duration) {
700        while !self.animations.is_empty() {
701            let (finished, leftover) = self.animations[0].update(dt);
702            if finished {
703                self.animations.remove(0); // pop front
704                dt = leftover;             // pass leftover to next!
705            } else { break; }
706        }
707    }
708}",
709        95.0,
710    );
711
712    let s11_leftover = note(
713        "leftover propagation: if A finishes mid-frame, the remaining",
714        390.0,
715    );
716    let s11_leftover2 = note(
717        "dt is immediately given to B. No 'lost frames' at transitions.",
718        415.0,
719    );
720
721    let s11_render_h = h2("Rendering Pipeline (per frame):", 470.0);
722    let s11_steps = [
723        "1. Timeline.update(dt)  =>  SignalTween writes to Signals",
724        "2. Node.render()        =>  reads signals, draws shapes",
725        "3. Vello GPU            =>  compiles scene => wgpu => pixels",
726        "4. state_hash()         =>  seahash for hashing scene state, skip if unchanged",
727    ];
728    let s11_render_texts: Vec<TextNode> = s11_steps
729        .iter()
730        .enumerate()
731        .map(|(i, s)| body(s, 505.0 + i as f32 * 28.0))
732        .collect();
733
734    project.scene.add(Box::new(s11_h.clone()));
735    project.scene.add(Box::new(s11_code.clone()));
736    for n in [&s11_leftover, &s11_leftover2, &s11_render_h] {
737        project.scene.add(Box::new(n.clone()));
738    }
739    for t in &s11_render_texts {
740        project.scene.add(Box::new(t.clone()));
741    }
742
743    // =====================================================================
744    //  S12: EVENT LOOP — Why an infinite loop?
745    // =====================================================================
746    let s12_h = title("Why an Infinite Loop? — The Event Loop", 50.0);
747    let s12_sub = body(
748        "GPU rendering requires a persistent event loop (winit + wgpu).",
749        90.0,
750    );
751    let s12_code = code_block(
752        "event_loop.run(|event, elwt| {
753    match event {
754        Resumed => {           // GPU surface ready
755            renderer.resume(&window);
756        }
757        AboutToWait => {       // run every frame
758            scene.update(dt);  // advance animations
759            let hash = scene.state_hash();
760            if hash != last_hash {    // dirty?
761                window.request_redraw();
762            }
763        }
764        RedrawRequested => {   // GPU draw call
765            renderer.render(&scene, w, h);
766        }
767    }
768});",
769        130.0,
770    );
771    let s12_why = note(
772        "The window stays open because the GPU surface is tied to",
773        490.0,
774    );
775    let s12_why2 = note(
776        "the OS event loop. Without it, the surface is immediately dropped.",
777        510.0,
778    );
779    let s12_hash = body(
780        "state_hash() skips re-rendering unchanged frames (dirty-checking).",
781        550.0,
782    );
783
784    for n in [&s12_h, &s12_sub, &s12_why, &s12_why2, &s12_hash] {
785        project.scene.add(Box::new(n.clone()));
786    }
787    project.scene.add(Box::new(s12_code.clone()));
788
789    // =====================================================================
790    //  S13: HEADLESS EXPORT — GPU without a window
791    // =====================================================================
792    let s13_h = title("Headless Export: GPU -> PNG -> FFmpeg", 50.0);
793    let s13_sub = body(
794        "Same GPU rendering, but without a window — output to files.",
795        90.0,
796    );
797    let s13_code = code_block(
798        "pub struct Exporter {
799    texture: wgpu::Texture,       // GPU-side image
800    output_buffer: wgpu::Buffer,  // CPU-readable copy
801    renderer: Renderer,           // Vello
802}
803fn export_frame(&mut self, scene) -> Vec<u8> {
804    scene.render(&mut self.scene);           // 1. build shapes
805    renderer.render_to_texture(..);          // 2. GPU draws
806    encoder.copy_texture_to_buffer(..);      // 3. GPU -> CPU
807    output_buffer.map_async(Read, ..);       // 4. read pixels
808    return pixels;                           // 5. raw RGBA
809}",
810        130.0,
811    );
812    let s13_cache = note(
813        "Cache: state_hash per frame. If unchanged, skip GPU entirely.",
814        420.0,
815    );
816    let s13_ffmpeg = note(
817        "FFmpeg: raw pixels piped to stdin -> libx264 -> .mkv video.",
818        445.0,
819    );
820    let s13_parallel = body(
821        "PNG saving runs on a background thread. Export is pipelined.",
822        485.0,
823    );
824
825    for n in [&s13_h, &s13_sub, &s13_cache, &s13_ffmpeg, &s13_parallel] {
826        project.scene.add(Box::new(n.clone()));
827    }
828    project.scene.add(Box::new(s13_code.clone()));
829
830    // =====================================================================
831    //  S14: ENGINE UTILITIES
832    // =====================================================================
833    let s14_h = title("Under the Hood: Utility Modules", 50.0);
834    let s14_sub = body(
835        "Helper systems that power the engine behind the scenes.",
836        90.0,
837    );
838    let s14_code = code_block(
839        "// src/engine/util/
840font_manager.rs    // Lazy-loads system fonts via Typst
841                   // Global HashMap cache with lazy_static
842
843image_manager.rs   // Loads PNG + SVG (via resvg)
844                   // Caches decoded images as Arc<Image>
845
846code_tokenizer.rs  // Syntax highlighting via Syntect
847                   // Parses code -> colored spans for CodeNode
848
849hash.rs            // SeaHash: fast, deterministic fingerprints
850                   // Position-aware combination
851                   // Powers Rayon parallel state hashing
852
853export.rs          // FFmpeg pipe: rawvideo -> libx264
854                   // Audio merging with filter_complex
855                   // Title sanitization for filenames",
856        130.0,
857    );
858    let s14_lazy = note(
859        "lazy_static + Mutex = global singleton, created once, cached forever.",
860        495.0,
861    );
862    let s14_arc = body(
863        "Arc<Image> lets multiple nodes share one decoded image without copies.",
864        530.0,
865    );
866    let s14_hash = note(
867        "Rayon + SeaHash = Deterministic fingerprints across runs & threads.",
868        565.0,
869    );
870
871    for n in [&s14_h, &s14_sub, &s14_lazy, &s14_arc, &s14_hash] {
872        project.scene.add(Box::new(n.clone()));
873    }
874    project.scene.add(Box::new(s14_code.clone()));
875
876    // =====================================================================
877    //  S15: FINALE
878    // =====================================================================
879    let fin = TextNode::default()
880        .with_anchor(Vec2::new(-1.0, -1.0))
881        .with_position(Vec2::new(LEFT, 200.0))
882        .with_text("That's how it works!")
883        .with_font_size(48.0)
884        .with_fill(ACCENT)
885        .with_font(FONT)
886        .with_opacity(0.0);
887    let fin_steps = [
888        "1.  struct          — data container",
889        "2.  impl            — methods / builder pattern",
890        "3.  trait Node      — interface contract",
891        "4.  Box<dyn Node>   — type-erased heap allocation",
892        "5.  Signal<T>       — Arc<Mutex> shared reactive state",
893        "6.  SignalTween     — per-frame lerp interpolation",
894        "7.  Timeline        — sequential queue + leftover dt",
895        "8.  Event Loop      — winit + wgpu infinite loop",
896        "9.  Exporter        — headless GPU -> PNG/FFmpeg",
897        "10. Utilities       — font/image cache, syntax highlight",
898    ];
899    let fin_texts: Vec<TextNode> = fin_steps
900        .iter()
901        .enumerate()
902        .map(|(i, s)| body(s, 270.0 + i as f32 * 28.0))
903        .collect();
904    let fin_hint = dim("cargo run --example getting_started", LEFT, 570.0);
905
906    project.scene.add(Box::new(fin.clone()));
907    for t in &fin_texts {
908        project.scene.add(Box::new(t.clone()));
909    }
910    project.scene.add(Box::new(fin_hint.clone()));
911
912    // =====================================================================
913    //  ANIMATION TIMELINE
914    // =====================================================================
915    // Helper: hide_all takes a vec of opacity signals and fades them out
916    let hide_dur = ms(200);
917
918    project.scene.video_timeline.add(chain![
919        // ── S1: TITLE ──
920        s1_line
921            .end
922            .to(Vec2::new(500.0, 100.0), ms(500))
923            .ease(easings::cubic_out),
924        sequence![
925            ms(120),
926            show(&s1_title, ms(500)),
927            show(&s1_sub, ms(500)),
928            show(&s1_built, ms(500)),
929            show(&s1_logo, ms(600)),
930            show(&s1_desc, ms(500)),
931            show(&s1_desc2, ms(500)),
932        ],
933        wait(secs(5)),
934        all![
935            hide(&s1_title, hide_dur),
936            hide(&s1_sub, hide_dur),
937            hide(&s1_built, hide_dur),
938            hide(&s1_desc, hide_dur),
939            hide(&s1_desc2, hide_dur),
940            hide(&s1_logo, hide_dur),
941            s1_line.end.to(Vec2::new(LEFT, 100.0), hide_dur)
942        ],
943        wait(ms(150)),
944        // ── S2: FIVE STEPS ──
945        show(&s2_h, ms(500)),
946        wait(ms(400)),
947        sequence![
948            ms(250),
949            show(&s2_texts[0], ms(400)),
950            show(&s2_texts[1], ms(400)),
951            show(&s2_texts[2], ms(400)),
952            show(&s2_texts[3], ms(400)),
953            show(&s2_texts[4], ms(400)),
954        ],
955        wait(secs(8)),
956        all![
957            hide(&s2_h, hide_dur),
958            hide(&s2_texts[0], hide_dur),
959            hide(&s2_texts[1], hide_dur),
960            hide(&s2_texts[2], hide_dur),
961            hide(&s2_texts[3], hide_dur),
962            hide(&s2_texts[4], hide_dur)
963        ],
964        wait(ms(150)),
965        // ── S3: STRUCT ──
966        sequence![
967            ms(120),
968            show(&s3_h, ms(500)),
969            show(&s3_explain, ms(400)),
970            show(&s3_analogy, ms(400))
971        ],
972        wait(ms(500)),
973        show(&s3_code, ms(500)),
974        wait(secs(6)),
975        sequence![
976            ms(300),
977            show(&s3_note, ms(400)),
978            show(&s3_note2, ms(400)),
979            show(&s3_note3, ms(400))
980        ],
981        wait(secs(6)),
982        all![
983            hide(&s3_h, hide_dur),
984            hide(&s3_explain, hide_dur),
985            hide(&s3_analogy, hide_dur),
986            hide(&s3_code, hide_dur),
987            hide(&s3_note, hide_dur),
988            hide(&s3_note2, hide_dur),
989            hide(&s3_note3, hide_dur)
990        ],
991        wait(ms(150)),
992        // ── S4: IMPL / BUILDER ──
993        sequence![
994            ms(120),
995            show(&s4_h, ms(500)),
996            show(&s4_explain, ms(400)),
997            show(&s4_analogy, ms(400))
998        ],
999        wait(ms(500)),
1000        show(&s4_code, ms(500)),
1001        wait(secs(7)),
1002        show(&s4_usage, ms(400)),
1003        show(&s4_usage_code, ms(500)),
1004        wait(secs(2)),
1005        show(&s4_note, ms(400)),
1006        wait(secs(5)),
1007        all![
1008            hide(&s4_h, hide_dur),
1009            hide(&s4_explain, hide_dur),
1010            hide(&s4_analogy, hide_dur),
1011            hide(&s4_code, hide_dur),
1012            hide(&s4_usage, hide_dur),
1013            hide(&s4_usage_code, hide_dur),
1014            hide(&s4_note, hide_dur)
1015        ],
1016        wait(ms(150)),
1017        // ── S5: TRAIT / NODE ──
1018        sequence![
1019            ms(120),
1020            show(&s5_h, ms(500)),
1021            show(&s5_explain, ms(400)),
1022            show(&s5_analogy, ms(400))
1023        ],
1024        wait(ms(500)),
1025        show(&s5_code, ms(500)),
1026        wait(secs(6)),
1027        sequence![
1028            ms(300),
1029            show(&s5_r, ms(400)),
1030            show(&s5_u, ms(400)),
1031            show(&s5_s, ms(400)),
1032            show(&s5_c, ms(400))
1033        ],
1034        wait(secs(2)),
1035        show(&s5_every, ms(400)),
1036        wait(secs(4)),
1037        all![
1038            hide(&s5_h, hide_dur),
1039            hide(&s5_explain, hide_dur),
1040            hide(&s5_analogy, hide_dur),
1041            hide(&s5_code, hide_dur),
1042            hide(&s5_r, hide_dur),
1043            hide(&s5_u, hide_dur),
1044            hide(&s5_s, hide_dur),
1045            hide(&s5_c, hide_dur),
1046            hide(&s5_every, hide_dur)
1047        ],
1048        wait(ms(150)),
1049        // ── S6: NODE GALLERY ──
1050        sequence![ms(120), show(&s6_h, ms(500)), show(&s6_sub, ms(400))],
1051        wait(ms(400)),
1052        sequence![
1053            ms(200),
1054            all![show(&demo_c, ms(400)), show(&lc, ms(400))],
1055            all![show(&demo_r, ms(400)), show(&lr, ms(400))],
1056            all![show(&demo_l, ms(400)), show(&ll, ms(400))],
1057            all![show(&demo_p, ms(400)), show(&lp, ms(400))],
1058            all![show(&demo_t, ms(400)), show(&lt, ms(400))],
1059        ],
1060        wait(secs(2)),
1061        sequence![
1062            ms(200),
1063            show(&s6_box_h, ms(400)),
1064            show(&s6_box1, ms(400)),
1065            show(&s6_box_code, ms(500))
1066        ],
1067        wait(secs(7)),
1068        all![
1069            hide(&s6_h, hide_dur),
1070            hide(&s6_sub, hide_dur),
1071            hide(&demo_c, hide_dur),
1072            hide(&demo_r, hide_dur),
1073            hide(&demo_l, hide_dur),
1074            hide(&demo_p, hide_dur),
1075            hide(&demo_t, hide_dur),
1076            hide(&lc, hide_dur),
1077            hide(&lr, hide_dur),
1078            hide(&ll, hide_dur),
1079            hide(&lp, hide_dur),
1080            hide(&lt, hide_dur),
1081            hide(&s6_box_h, hide_dur),
1082            hide(&s6_box1, hide_dur),
1083            hide(&s6_box_code, hide_dur)
1084        ],
1085        wait(ms(150)),
1086        // ── S7: SIGNALS ──
1087        sequence![ms(120), show(&s7_h, ms(500)), show(&s7_sub, ms(400))],
1088        wait(ms(400)),
1089        show(&s7_code, ms(500)),
1090        wait(secs(5)),
1091        sequence![ms(300), show(&s7_arc, ms(400)), show(&s7_mutex, ms(400))],
1092        wait(secs(4)),
1093        show(&s7_why, ms(400)),
1094        show(&s7_diagram_code, ms(500)),
1095        wait(secs(6)),
1096        // Live demo
1097        all![show(&sig_demo, ms(300)), show(&sig_lbl, ms(300))],
1098        chain![
1099            sig_demo.radius.to(80.0, ms(700)).ease(easings::elastic_out),
1100            sig_demo.fill_color.to(TEAL, ms(500)),
1101            sig_demo
1102                .position
1103                .to(Vec2::new(950.0, 350.0), ms(500))
1104                .ease(easings::cubic_out),
1105            wait(ms(300)),
1106            all![
1107                sig_demo.radius.to(50.0, ms(400)),
1108                sig_demo.fill_color.to(RED, ms(400)),
1109                sig_demo.position.to(Vec2::new(900.0, 300.0), ms(400))
1110            ],
1111        ],
1112        wait(secs(3)),
1113        all![
1114            hide(&s7_h, hide_dur),
1115            hide(&s7_sub, hide_dur),
1116            hide(&s7_code, hide_dur),
1117            hide(&s7_arc, hide_dur),
1118            hide(&s7_mutex, hide_dur),
1119            hide(&s7_why, hide_dur),
1120            hide(&s7_diagram_code, hide_dur),
1121            hide(&sig_demo, hide_dur),
1122            hide(&sig_lbl, hide_dur)
1123        ],
1124        wait(ms(150)),
1125        // ── S8: SIGNAL TWEEN ──
1126        sequence![ms(120), show(&s8_h, ms(500)), show(&s8_sub, ms(400))],
1127        wait(ms(400)),
1128        show(&s8_code, ms(500)),
1129        wait(secs(6)),
1130        show(&s8_how, ms(300)),
1131        sequence![
1132            ms(100),
1133            show(&s8_step_texts[0], ms(250)),
1134            show(&s8_step_texts[1], ms(250)),
1135            show(&s8_step_texts[2], ms(250)),
1136            show(&s8_step_texts[3], ms(250)),
1137            show(&s8_step_texts[4], ms(250)),
1138            show(&s8_step_texts[5], ms(250)),
1139        ],
1140        wait(ms(500)),
1141        sequence![ms(100), show(&s8_lazy, ms(300)), show(&s8_lazy2, ms(300))],
1142        wait(ms(500)),
1143        // Progress bar demo
1144        all![
1145            show(&prog_bg, ms(200)),
1146            show(&prog_fill, ms(200)),
1147            show(&plbl0, ms(200)),
1148            show(&plbl1, ms(200)),
1149            show(&tween_ball, ms(200)),
1150            show(&tween_lbl, ms(200))
1151        ],
1152        all![
1153            prog_fill
1154                .size
1155                .to(Vec2::new(400.0, 16.0), secs(2))
1156                .ease(easings::cubic_in_out),
1157            tween_ball
1158                .radius
1159                .to(80.0, secs(2))
1160                .ease(easings::cubic_in_out),
1161        ],
1162        wait(secs(3)),
1163        all![
1164            hide(&s8_h, hide_dur),
1165            hide(&s8_sub, hide_dur),
1166            hide(&s8_code, hide_dur),
1167            hide(&s8_how, hide_dur),
1168            hide(&s8_lazy, hide_dur),
1169            hide(&s8_lazy2, hide_dur),
1170            hide(&prog_bg, hide_dur),
1171            hide(&prog_fill, hide_dur),
1172            hide(&plbl0, hide_dur),
1173            hide(&plbl1, hide_dur),
1174            hide(&tween_ball, hide_dur),
1175            hide(&tween_lbl, hide_dur),
1176            hide(&s8_step_texts[0], hide_dur),
1177            hide(&s8_step_texts[1], hide_dur),
1178            hide(&s8_step_texts[2], hide_dur),
1179            hide(&s8_step_texts[3], hide_dur),
1180            hide(&s8_step_texts[4], hide_dur),
1181            hide(&s8_step_texts[5], hide_dur)
1182        ],
1183        wait(ms(150)),
1184        // ── S9: TWEENABLE + EASINGS ──
1185        show(&s9_h, ms(500)),
1186        show(&s9_code, ms(500)),
1187        wait(secs(4)),
1188        sequence![
1189            ms(60),
1190            show(&s9_easing_h, ms(300)),
1191            show(&s9_easing_desc, ms(300))
1192        ],
1193        sequence![
1194            ms(50),
1195            all![show(&eballs[0], ms(200)), show(&elabels[0], ms(200))],
1196            all![show(&eballs[1], ms(200)), show(&elabels[1], ms(200))],
1197            all![show(&eballs[2], ms(200)), show(&elabels[2], ms(200))],
1198            all![show(&eballs[3], ms(200)), show(&elabels[3], ms(200))],
1199            all![show(&eballs[4], ms(200)), show(&elabels[4], ms(200))],
1200        ],
1201        wait(ms(300)),
1202        // Race!
1203        all![
1204            eballs[0]
1205                .position
1206                .to(Vec2::new(1050.0, 390.0), secs(2))
1207                .ease(easings::linear),
1208            eballs[1]
1209                .position
1210                .to(Vec2::new(1050.0, 445.0), secs(2))
1211                .ease(easings::cubic_in_out),
1212            eballs[2]
1213                .position
1214                .to(Vec2::new(1050.0, 500.0), secs(2))
1215                .ease(easings::elastic_out),
1216            eballs[3]
1217                .position
1218                .to(Vec2::new(1050.0, 555.0), secs(2))
1219                .ease(easings::bounce_out),
1220            eballs[4]
1221                .position
1222                .to(Vec2::new(1050.0, 610.0), secs(2))
1223                .ease(easings::back_out),
1224        ],
1225        wait(ms(500)),
1226        all![
1227            eballs[0]
1228                .position
1229                .to(Vec2::new(250.0, 390.0), secs(2))
1230                .ease(easings::linear),
1231            eballs[1]
1232                .position
1233                .to(Vec2::new(250.0, 445.0), secs(2))
1234                .ease(easings::cubic_in_out),
1235            eballs[2]
1236                .position
1237                .to(Vec2::new(250.0, 500.0), secs(2))
1238                .ease(easings::elastic_out),
1239            eballs[3]
1240                .position
1241                .to(Vec2::new(250.0, 555.0), secs(2))
1242                .ease(easings::bounce_out),
1243            eballs[4]
1244                .position
1245                .to(Vec2::new(250.0, 610.0), secs(2))
1246                .ease(easings::back_out),
1247        ],
1248        wait(ms(500)),
1249        all![
1250            hide(&s9_h, hide_dur),
1251            hide(&s9_code, hide_dur),
1252            hide(&s9_easing_h, hide_dur),
1253            hide(&s9_easing_desc, hide_dur),
1254            hide(&eballs[0], hide_dur),
1255            hide(&eballs[1], hide_dur),
1256            hide(&eballs[2], hide_dur),
1257            hide(&eballs[3], hide_dur),
1258            hide(&eballs[4], hide_dur),
1259            hide(&elabels[0], hide_dur),
1260            hide(&elabels[1], hide_dur),
1261            hide(&elabels[2], hide_dur),
1262            hide(&elabels[3], hide_dur),
1263            hide(&elabels[4], hide_dur)
1264        ],
1265        wait(ms(150)),
1266        // ── S10: FLOW CONTROLS ──
1267        sequence![ms(120), show(&s10_h, ms(500)), show(&s10_sub, ms(400))],
1268        wait(ms(400)),
1269        // chain demo
1270        show(&s10_chain_h, ms(400)),
1271        all![
1272            show(&chain_d[0], ms(300)),
1273            show(&chain_d[1], ms(300)),
1274            show(&chain_d[2], ms(300))
1275        ],
1276        wait(ms(300)),
1277        chain![
1278            chain_d[0]
1279                .position
1280                .to(Vec2::new(700.0, 200.0), ms(500))
1281                .ease(easings::cubic_out),
1282            chain_d[1]
1283                .position
1284                .to(Vec2::new(800.0, 200.0), ms(500))
1285                .ease(easings::cubic_out),
1286            chain_d[2]
1287                .position
1288                .to(Vec2::new(900.0, 200.0), ms(500))
1289                .ease(easings::cubic_out),
1290        ],
1291        wait(secs(1)),
1292        // all demo
1293        show(&s10_all_h, ms(400)),
1294        all![
1295            show(&all_d[0], ms(300)),
1296            show(&all_d[1], ms(300)),
1297            show(&all_d[2], ms(300))
1298        ],
1299        wait(ms(300)),
1300        all![
1301            all_d[0]
1302                .position
1303                .to(Vec2::new(700.0, 330.0), ms(500))
1304                .ease(easings::cubic_out),
1305            all_d[1]
1306                .position
1307                .to(Vec2::new(800.0, 330.0), ms(500))
1308                .ease(easings::cubic_out),
1309            all_d[2]
1310                .position
1311                .to(Vec2::new(900.0, 330.0), ms(500))
1312                .ease(easings::cubic_out),
1313        ],
1314        wait(secs(1)),
1315        // sequence demo
1316        show(&s10_seq_h, ms(400)),
1317        all![
1318            show(&seq_d[0], ms(300)),
1319            show(&seq_d[1], ms(300)),
1320            show(&seq_d[2], ms(300))
1321        ],
1322        wait(ms(300)),
1323        sequence![
1324            ms(250),
1325            seq_d[0]
1326                .position
1327                .to(Vec2::new(700.0, 460.0), ms(500))
1328                .ease(easings::cubic_out),
1329            seq_d[1]
1330                .position
1331                .to(Vec2::new(800.0, 460.0), ms(500))
1332                .ease(easings::cubic_out),
1333            seq_d[2]
1334                .position
1335                .to(Vec2::new(900.0, 460.0), ms(500))
1336                .ease(easings::cubic_out),
1337        ],
1338        wait(secs(1)),
1339        show(&s10_code, ms(500)),
1340        wait(secs(8)),
1341        all![
1342            hide(&s10_h, hide_dur),
1343            hide(&s10_sub, hide_dur),
1344            hide(&s10_chain_h, hide_dur),
1345            hide(&s10_all_h, hide_dur),
1346            hide(&s10_seq_h, hide_dur),
1347            hide(&s10_code, hide_dur),
1348            hide(&chain_d[0], hide_dur),
1349            hide(&chain_d[1], hide_dur),
1350            hide(&chain_d[2], hide_dur),
1351            hide(&all_d[0], hide_dur),
1352            hide(&all_d[1], hide_dur),
1353            hide(&all_d[2], hide_dur),
1354            hide(&seq_d[0], hide_dur),
1355            hide(&seq_d[1], hide_dur),
1356            hide(&seq_d[2], hide_dur)
1357        ],
1358        wait(ms(150)),
1359        // ── S11: TIMELINE + RENDERING ──
1360        show(&s11_h, ms(500)),
1361        wait(ms(400)),
1362        show(&s11_code, ms(500)),
1363        wait(secs(7)),
1364        sequence![
1365            ms(200),
1366            show(&s11_leftover, ms(400)),
1367            show(&s11_leftover2, ms(400))
1368        ],
1369        wait(secs(4)),
1370        show(&s11_render_h, ms(400)),
1371        sequence![
1372            ms(200),
1373            show(&s11_render_texts[0], ms(350)),
1374            show(&s11_render_texts[1], ms(350)),
1375            show(&s11_render_texts[2], ms(350)),
1376            show(&s11_render_texts[3], ms(350)),
1377        ],
1378        wait(secs(7)),
1379        all![
1380            hide(&s11_h, hide_dur),
1381            hide(&s11_code, hide_dur),
1382            hide(&s11_leftover, hide_dur),
1383            hide(&s11_leftover2, hide_dur),
1384            hide(&s11_render_h, hide_dur),
1385            hide(&s11_render_texts[0], hide_dur),
1386            hide(&s11_render_texts[1], hide_dur),
1387            hide(&s11_render_texts[2], hide_dur),
1388            hide(&s11_render_texts[3], hide_dur)
1389        ],
1390        wait(ms(300)),
1391        // ── S12: EVENT LOOP ──
1392        sequence![ms(120), show(&s12_h, ms(500)), show(&s12_sub, ms(400))],
1393        wait(ms(500)),
1394        show(&s12_code, ms(500)),
1395        wait(secs(8)),
1396        sequence![
1397            ms(200),
1398            show(&s12_why, ms(400)),
1399            show(&s12_why2, ms(400)),
1400            show(&s12_hash, ms(400))
1401        ],
1402        wait(secs(5)),
1403        all![
1404            hide(&s12_h, hide_dur),
1405            hide(&s12_sub, hide_dur),
1406            hide(&s12_code, hide_dur),
1407            hide(&s12_why, hide_dur),
1408            hide(&s12_why2, hide_dur),
1409            hide(&s12_hash, hide_dur)
1410        ],
1411        wait(ms(150)),
1412        // ── S13: HEADLESS EXPORT ──
1413        sequence![ms(120), show(&s13_h, ms(500)), show(&s13_sub, ms(400))],
1414        wait(ms(500)),
1415        show(&s13_code, ms(500)),
1416        wait(secs(8)),
1417        sequence![
1418            ms(200),
1419            show(&s13_cache, ms(400)),
1420            show(&s13_ffmpeg, ms(400)),
1421            show(&s13_parallel, ms(400))
1422        ],
1423        wait(secs(5)),
1424        all![
1425            hide(&s13_h, hide_dur),
1426            hide(&s13_sub, hide_dur),
1427            hide(&s13_code, hide_dur),
1428            hide(&s13_cache, hide_dur),
1429            hide(&s13_ffmpeg, hide_dur),
1430            hide(&s13_parallel, hide_dur)
1431        ],
1432        wait(ms(150)),
1433        // ── S14: UTILITIES ──
1434        sequence![ms(120), show(&s14_h, ms(500)), show(&s14_sub, ms(400))],
1435        wait(ms(500)),
1436        show(&s14_code, ms(500)),
1437        wait(secs(8)),
1438        sequence![ms(200), show(&s14_lazy, ms(400)), show(&s14_arc, ms(400))],
1439        wait(secs(5)),
1440        all![
1441            hide(&s14_h, hide_dur),
1442            hide(&s14_sub, hide_dur),
1443            hide(&s14_code, hide_dur),
1444            hide(&s14_lazy, hide_dur),
1445            hide(&s14_arc, hide_dur)
1446        ],
1447        wait(ms(300)),
1448        // ── S15: FINALE ──
1449        show(&fin, ms(700)),
1450        wait(ms(500)),
1451        sequence![
1452            ms(150),
1453            show(&fin_texts[0], ms(350)),
1454            show(&fin_texts[1], ms(350)),
1455            show(&fin_texts[2], ms(350)),
1456            show(&fin_texts[3], ms(350)),
1457            show(&fin_texts[4], ms(350)),
1458            show(&fin_texts[5], ms(350)),
1459            show(&fin_texts[6], ms(350)),
1460            show(&fin_texts[7], ms(350)),
1461            show(&fin_texts[8], ms(350)),
1462            show(&fin_texts[9], ms(350)),
1463        ],
1464        wait(secs(2)),
1465        show(&fin_hint, ms(400)),
1466        wait(secs(6)),
1467    ]);
1468
1469    #[cfg(feature = "audio")]
1470    project
1471        .scene
1472        .audio_timeline
1473        .add(play!(AudioNode::new("background.mp3").with_volume(0.3)));
1474
1475    project.show().expect("Failed to render");
1476}
Source

pub fn with_close_on_finish(self, close: bool) -> Self

Source

pub fn close_on_finish(self) -> Self

Examples found in repository?
examples/color_interpolation.rs (line 10)
4fn main() {
5    let mut project = Project::default()
6        .with_dimensions(300, 300)
7        .with_fps(60)
8        .with_title("Color Interpolation")
9        .with_cache(true)
10        .close_on_finish();
11
12    let circle = Circle::default()
13        .with_position(Vec2::new(150.0, 150.0))
14        .with_radius(50.0)
15        .with_fill(Color::RED); // Red
16
17    project.scene.add(Box::new(circle.clone()));
18
19    let duration = Duration::from_secs(1);
20
21    project.scene.video_timeline.add(loop_anim(
22        move || {
23            chain![
24                circle.fill_color.to(Color::YELLOW, duration),
25                circle.fill_color.to(Color::GREEN, duration),
26                circle.fill_color.to(Color::BLUE, duration),
27                circle.fill_color.to(Color::RED, duration),
28            ]
29        },
30        None,
31    ));
32
33    // Show
34    project.show().expect("Failed to render");
35}
More examples
Hide additional examples
examples/math_animation.rs (line 10)
4fn main() {
5    // 1. Initialize Project
6    let mut project = Project::default()
7        .with_fps(60)
8        .with_dimensions(500, 500)
9        .with_title("Math Animation")
10        .close_on_finish();
11
12    // 2. Create MathNode (Typst syntax)
13    let tex = MathNode::default()
14        .with_position(Vec2::new(250.0, 250.0))
15        .with_equation("y = a x^2")
16        .with_font_size(48.0)
17        .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2));
18    project.scene.add(Box::new(tex.clone()));
19
20    // 3. Define Animation Sequence
21    project.scene.video_timeline.add(loop_anim(
22        move || {
23            chain![
24                tex.tex("y = a x^2 + b x", Duration::from_secs(1)),
25                tex.tex("e^(i pi) + 1 = 0", Duration::from_secs(1)),
26                tex.tex("y = a x^2", Duration::from_secs(1)),
27            ]
28        },
29        None,
30    ));
31
32    // 4. Run interactive preview
33    project.show().expect("Failed to render");
34}
examples/getting_started.rs (line 10)
4fn main() {
5    // 1. Initialize the Project
6    let mut project = Project::default()
7        .with_fps(60)
8        .with_cache(true)
9        .with_title("Getting Started")
10        .close_on_finish();
11
12    // 2. Define Nodes
13    let circle = Circle::default()
14        .with_position(Vec2::new(400.0, 300.0))
15        .with_radius(50.0)
16        .with_fill(Color::rgb8(0xe1, 0x32, 0x38)); // Red
17
18    let text = TextNode::default()
19        .with_position(Vec2::new(400.0, 450.0))
20        .with_text("Hello Rust")
21        .with_font_size(40.0)
22        .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White-ish
23
24    // 3. Add Nodes to the Scene
25    project.scene.add(Box::new(circle.clone()));
26    project.scene.add(Box::new(text.clone()));
27
28    // 4. Add Animations to the Timeline
29    project.scene.video_timeline.add(all![
30        circle.radius.to(100.0, Duration::from_secs(1)),
31        text.position
32            .to(Vec2::new(400.0, 500.0), Duration::from_secs(1)),
33    ]);
34
35    // 5. Show
36    project.show().expect("Failed to render");
37}
examples/code_advanced.rs (line 7)
4fn main() {
5    let mut project = Project::default()
6        .with_title("Code Advanced")
7        .close_on_finish();
8
9    let code = CodeNode::default()
10        .with_position(Vec2::new(50.0, 50.0))
11        .with_code(
12            r#"fn main() {
13    println!("Hello");
14}"#,
15        )
16        .with_language("rust")
17        .with_font_size(32.0)
18        .with_dim_opacity(0.1);
19
20    project.scene.add(Box::new(code.clone()));
21
22    project.scene.video_timeline.add(sequence![
23        Duration::from_secs(1),
24        // 1. Select line 2 (println) - using 1-based index string
25        code.select_string("2", Duration::from_millis(300)),
26        // 2. Select range 1-2
27        code.select_string("1-2", Duration::from_millis(300)),
28        // 3. Append a comment
29        code.append("\n// Done!", Duration::from_millis(300)),
30        // 3. Reset selection
31        code.select_lines(vec![], Duration::from_millis(300)),
32        // 4. Prepend a header (Now natively lazy, no wrapper needed!)
33        code.prepend("// My Script\n", Duration::from_millis(300)),
34    ]);
35
36    project.show().expect("Failed to render");
37}
examples/export.rs (line 11)
4fn main() {
5    // 1. Initialize for Export
6    let mut project = Project::default()
7        .with_fps(30)
8        .with_ffmpeg(true)
9        .with_title("Export")
10        .with_output_path("output")
11        .close_on_finish();
12
13    // 2. Setup Nodes
14    let circle = Circle::default()
15        .with_position(Vec2::new(400.0, 300.0))
16        .with_radius(50.0)
17        .with_fill(Color::rgb8(0x68, 0xab, 0xdf)); // Blue
18
19    let text = TextNode::default()
20        .with_position(Vec2::new(400.0, 50.0))
21        .with_text("Export Demo")
22        .with_font_size(40.0)
23        .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White
24
25    project.scene.add(Box::new(circle.clone()));
26    project.scene.add(Box::new(text.clone()));
27
28    // 3. Define Animations (Color and Font Size)
29    project.scene.video_timeline.add(all![
30        // Circle color and size
31        circle
32            .fill_color
33            .to(Color::rgb8(0xf2, 0xf2, 0xf2), Duration::from_secs(2))
34            .ease(easings::quad_in_out),
35        circle
36            .radius
37            .to(150.0, Duration::from_secs(2))
38            .ease(easings::elastic_out),
39        // Text font size
40        text.font_size
41            .to(50.0, Duration::from_secs(2))
42            .ease(easings::cubic_out),
43    ]);
44
45    // 4. Export (Renders frames and combines them into out.mkv)
46    println!("Starting export to {}...", project.output_path.display());
47    project.export().expect("Failed to export");
48}
examples/polygon.rs (line 5)
4fn main() {
5    let mut project = Project::default().with_title("Polygon").close_on_finish();
6
7    // Create a regular pentagon
8    let pentagon = Polygon::regular(5, 100.0)
9        .with_fill(Color::rgb8(0xe1, 0x32, 0x38)) // Red
10        .with_position(Vec2::new(200.0, 300.0))
11        .with_scale(0.0);
12
13    // Create a custom triangle
14    let triangle = Polygon::default()
15        .with_position(Vec2::new(500.0, 300.0))
16        .with_points(vec![
17            Vec2::new(0.0, -100.0),
18            Vec2::new(100.0, 100.0),
19            Vec2::new(-100.0, 100.0),
20        ])
21        .with_fill(Color::rgb8(0x68, 0xab, 0xdf)) // Blue
22        .with_stroke(Color::WHITE, 4.0);
23
24    project.scene.add(Box::new(pentagon.clone()));
25    project.scene.add(Box::new(triangle.clone()));
26
27    // Animate rotation and opacity
28    project.scene.video_timeline.add(all![
29        chain![
30            pentagon.scale.to(Vec2::ONE, Duration::from_secs(1)),
31            pentagon
32                .rotation
33                .to(std::f32::consts::PI, Duration::from_secs(2)),
34            pentagon.scale.to(-Vec2::ONE, Duration::from_secs(1)),
35        ],
36        chain![
37            triangle.opacity.to(1.0, Duration::from_secs(1)),
38            triangle
39                .position
40                .to(Vec2::new(500.0, 300.0), Duration::from_secs(1)),
41            triangle
42                .rotation
43                .to(360.0_f32.to_radians(), Duration::from_secs(1))
44        ],
45    ]);
46
47    project.show().expect("Failed to render");
48}
Source

pub fn export(&mut self) -> Result<()>

Examples found in repository?
examples/export.rs (line 47)
4fn main() {
5    // 1. Initialize for Export
6    let mut project = Project::default()
7        .with_fps(30)
8        .with_ffmpeg(true)
9        .with_title("Export")
10        .with_output_path("output")
11        .close_on_finish();
12
13    // 2. Setup Nodes
14    let circle = Circle::default()
15        .with_position(Vec2::new(400.0, 300.0))
16        .with_radius(50.0)
17        .with_fill(Color::rgb8(0x68, 0xab, 0xdf)); // Blue
18
19    let text = TextNode::default()
20        .with_position(Vec2::new(400.0, 50.0))
21        .with_text("Export Demo")
22        .with_font_size(40.0)
23        .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White
24
25    project.scene.add(Box::new(circle.clone()));
26    project.scene.add(Box::new(text.clone()));
27
28    // 3. Define Animations (Color and Font Size)
29    project.scene.video_timeline.add(all![
30        // Circle color and size
31        circle
32            .fill_color
33            .to(Color::rgb8(0xf2, 0xf2, 0xf2), Duration::from_secs(2))
34            .ease(easings::quad_in_out),
35        circle
36            .radius
37            .to(150.0, Duration::from_secs(2))
38            .ease(easings::elastic_out),
39        // Text font size
40        text.font_size
41            .to(50.0, Duration::from_secs(2))
42            .ease(easings::cubic_out),
43    ]);
44
45    // 4. Export (Renders frames and combines them into out.mkv)
46    println!("Starting export to {}...", project.output_path.display());
47    project.export().expect("Failed to export");
48}
Source

pub fn show(self) -> Result<()>

Examples found in repository?
examples/color_interpolation.rs (line 34)
4fn main() {
5    let mut project = Project::default()
6        .with_dimensions(300, 300)
7        .with_fps(60)
8        .with_title("Color Interpolation")
9        .with_cache(true)
10        .close_on_finish();
11
12    let circle = Circle::default()
13        .with_position(Vec2::new(150.0, 150.0))
14        .with_radius(50.0)
15        .with_fill(Color::RED); // Red
16
17    project.scene.add(Box::new(circle.clone()));
18
19    let duration = Duration::from_secs(1);
20
21    project.scene.video_timeline.add(loop_anim(
22        move || {
23            chain![
24                circle.fill_color.to(Color::YELLOW, duration),
25                circle.fill_color.to(Color::GREEN, duration),
26                circle.fill_color.to(Color::BLUE, duration),
27                circle.fill_color.to(Color::RED, duration),
28            ]
29        },
30        None,
31    ));
32
33    // Show
34    project.show().expect("Failed to render");
35}
More examples
Hide additional examples
examples/math_animation.rs (line 33)
4fn main() {
5    // 1. Initialize Project
6    let mut project = Project::default()
7        .with_fps(60)
8        .with_dimensions(500, 500)
9        .with_title("Math Animation")
10        .close_on_finish();
11
12    // 2. Create MathNode (Typst syntax)
13    let tex = MathNode::default()
14        .with_position(Vec2::new(250.0, 250.0))
15        .with_equation("y = a x^2")
16        .with_font_size(48.0)
17        .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2));
18    project.scene.add(Box::new(tex.clone()));
19
20    // 3. Define Animation Sequence
21    project.scene.video_timeline.add(loop_anim(
22        move || {
23            chain![
24                tex.tex("y = a x^2 + b x", Duration::from_secs(1)),
25                tex.tex("e^(i pi) + 1 = 0", Duration::from_secs(1)),
26                tex.tex("y = a x^2", Duration::from_secs(1)),
27            ]
28        },
29        None,
30    ));
31
32    // 4. Run interactive preview
33    project.show().expect("Failed to render");
34}
examples/getting_started.rs (line 36)
4fn main() {
5    // 1. Initialize the Project
6    let mut project = Project::default()
7        .with_fps(60)
8        .with_cache(true)
9        .with_title("Getting Started")
10        .close_on_finish();
11
12    // 2. Define Nodes
13    let circle = Circle::default()
14        .with_position(Vec2::new(400.0, 300.0))
15        .with_radius(50.0)
16        .with_fill(Color::rgb8(0xe1, 0x32, 0x38)); // Red
17
18    let text = TextNode::default()
19        .with_position(Vec2::new(400.0, 450.0))
20        .with_text("Hello Rust")
21        .with_font_size(40.0)
22        .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White-ish
23
24    // 3. Add Nodes to the Scene
25    project.scene.add(Box::new(circle.clone()));
26    project.scene.add(Box::new(text.clone()));
27
28    // 4. Add Animations to the Timeline
29    project.scene.video_timeline.add(all![
30        circle.radius.to(100.0, Duration::from_secs(1)),
31        text.position
32            .to(Vec2::new(400.0, 500.0), Duration::from_secs(1)),
33    ]);
34
35    // 5. Show
36    project.show().expect("Failed to render");
37}
examples/code_advanced.rs (line 36)
4fn main() {
5    let mut project = Project::default()
6        .with_title("Code Advanced")
7        .close_on_finish();
8
9    let code = CodeNode::default()
10        .with_position(Vec2::new(50.0, 50.0))
11        .with_code(
12            r#"fn main() {
13    println!("Hello");
14}"#,
15        )
16        .with_language("rust")
17        .with_font_size(32.0)
18        .with_dim_opacity(0.1);
19
20    project.scene.add(Box::new(code.clone()));
21
22    project.scene.video_timeline.add(sequence![
23        Duration::from_secs(1),
24        // 1. Select line 2 (println) - using 1-based index string
25        code.select_string("2", Duration::from_millis(300)),
26        // 2. Select range 1-2
27        code.select_string("1-2", Duration::from_millis(300)),
28        // 3. Append a comment
29        code.append("\n// Done!", Duration::from_millis(300)),
30        // 3. Reset selection
31        code.select_lines(vec![], Duration::from_millis(300)),
32        // 4. Prepend a header (Now natively lazy, no wrapper needed!)
33        code.prepend("// My Script\n", Duration::from_millis(300)),
34    ]);
35
36    project.show().expect("Failed to render");
37}
examples/polygon.rs (line 47)
4fn main() {
5    let mut project = Project::default().with_title("Polygon").close_on_finish();
6
7    // Create a regular pentagon
8    let pentagon = Polygon::regular(5, 100.0)
9        .with_fill(Color::rgb8(0xe1, 0x32, 0x38)) // Red
10        .with_position(Vec2::new(200.0, 300.0))
11        .with_scale(0.0);
12
13    // Create a custom triangle
14    let triangle = Polygon::default()
15        .with_position(Vec2::new(500.0, 300.0))
16        .with_points(vec![
17            Vec2::new(0.0, -100.0),
18            Vec2::new(100.0, 100.0),
19            Vec2::new(-100.0, 100.0),
20        ])
21        .with_fill(Color::rgb8(0x68, 0xab, 0xdf)) // Blue
22        .with_stroke(Color::WHITE, 4.0);
23
24    project.scene.add(Box::new(pentagon.clone()));
25    project.scene.add(Box::new(triangle.clone()));
26
27    // Animate rotation and opacity
28    project.scene.video_timeline.add(all![
29        chain![
30            pentagon.scale.to(Vec2::ONE, Duration::from_secs(1)),
31            pentagon
32                .rotation
33                .to(std::f32::consts::PI, Duration::from_secs(2)),
34            pentagon.scale.to(-Vec2::ONE, Duration::from_secs(1)),
35        ],
36        chain![
37            triangle.opacity.to(1.0, Duration::from_secs(1)),
38            triangle
39                .position
40                .to(Vec2::new(500.0, 300.0), Duration::from_secs(1)),
41            triangle
42                .rotation
43                .to(360.0_f32.to_radians(), Duration::from_secs(1))
44        ],
45    ]);
46
47    project.show().expect("Failed to render");
48}
examples/audio_demo.rs (line 48)
4fn main() {
5    let mut project = Project::default()
6        .with_dimensions(800, 450)
7        .with_title("Audio Demo")
8        .with_background(Color::rgb8(20, 20, 25))
9        .close_on_finish();
10
11    // Setup Video Timeline
12    let rect = Rect::new(Vec2::new(100.0, 100.0), Vec2::new(200.0, 200.0), Color::RED)
13        .with_anchor(Vec2::new(-1.0, -1.0));
14
15    project.scene.add(Box::new(rect.clone()));
16
17    project.scene.video_timeline.add(chain!(
18        all![
19            rect.position
20                .to(Vec2::new(150.0, 150.0), Duration::from_secs(1)),
21            rect.size.to(Vec2::new(200.0, 50.0), Duration::from_secs(1)),
22            rect.fill_color.to(Color::BLUE, Duration::from_secs(1)),
23        ],
24        all![
25            rect.position
26                .to(Vec2::new(350.0, 150.0), Duration::from_secs(1)),
27            rect.fill_color.to(Color::RED, Duration::from_secs(1)),
28        ]
29    ));
30
31    // Setup Audio Timeline using new macros and builder
32    project.scene.audio_timeline.add(chain!(
33        play!(AudioNode::new("./examples/audios/combo-1.mp3").with_volume(0.5)),
34        audio_wait!(0.05),
35        play!(AudioNode::new("./examples/audios/combo-2.mp3").with_volume(1.0))
36    ));
37
38    println!("Project configured with separate video and audio timelines.");
39    println!(
40        "Video duration: {:?}",
41        project.scene.video_timeline.duration()
42    );
43    println!(
44        "Audio duration: {:?}",
45        project.scene.audio_timeline.duration()
46    );
47
48    project.show().expect("Failed to run audio demo");
49}
Source

pub fn seek_to(&mut self, target_time: Duration)

Source

pub fn get_frame_name(&self, frame_count: u32) -> String

Trait Implementations§

Source§

impl Default for Project

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<S, D, Swp, Dwp, T> AdaptInto<D, Swp, Dwp, T> for S
where T: Real + Zero + Arithmetics + Clone, Swp: WhitePoint<T>, Dwp: WhitePoint<T>, D: AdaptFrom<S, Swp, Dwp, T>,

Source§

fn adapt_into_using<M>(self, method: M) -> D
where M: TransformMatrix<T>,

Convert the source color to the destination color using the specified method.
Source§

fn adapt_into(self) -> D

Convert the source color to the destination color using the bradford method by default.
Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T, C> ArraysFrom<C> for T
where C: IntoArrays<T>,

Source§

fn arrays_from(colors: C) -> T

Cast a collection of colors into a collection of arrays.
Source§

impl<T, C> ArraysInto<C> for T
where C: FromArrays<T>,

Source§

fn arrays_into(self) -> C

Cast this collection of arrays into a collection of colors.
Source§

impl<T> Az for T

Source§

fn az<Dst>(self) -> Dst
where T: Cast<Dst>,

Casts the value.
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<WpParam, T, U> Cam16IntoUnclamped<WpParam, T> for U
where T: FromCam16Unclamped<WpParam, U>,

Source§

type Scalar = <T as FromCam16Unclamped<WpParam, U>>::Scalar

The number type that’s used in parameters when converting.
Source§

fn cam16_into_unclamped( self, parameters: BakedParameters<WpParam, <U as Cam16IntoUnclamped<WpParam, T>>::Scalar>, ) -> T

Converts self into C, using the provided parameters.
Source§

impl<Src, Dst> CastFrom<Src> for Dst
where Src: Cast<Dst>,

Source§

fn cast_from(src: Src) -> Dst

Casts the value.
Source§

impl<T> CheckedAs for T

Source§

fn checked_as<Dst>(self) -> Option<Dst>
where T: CheckedCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> CheckedCastFrom<Src> for Dst
where Src: CheckedCast<Dst>,

Source§

fn checked_cast_from(src: Src) -> Option<Dst>

Casts the value.
Source§

impl<T, C> ComponentsFrom<C> for T
where C: IntoComponents<T>,

Source§

fn components_from(colors: C) -> T

Cast a collection of colors into a collection of color components.
Source§

impl<T> Downcast<T> for T

Source§

fn downcast(&self) -> &T

Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> Filterable for T

Source§

fn filterable( self, filter_name: &'static str, ) -> RequestFilterDataProvider<T, fn(DataRequest<'_>) -> bool>

Creates a filterable data provider with the given name for debugging. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromAngle<T> for T

Source§

fn from_angle(angle: T) -> T

Performs a conversion from angle.
Source§

impl<S> FromSample<S> for S

Source§

fn from_sample_(s: S) -> S

Source§

impl<T, U> FromStimulus<U> for T
where U: IntoStimulus<T>,

Source§

fn from_stimulus(other: U) -> T

Converts other into Self, while performing the appropriate scaling, rounding and clamping.
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> IntoAngle<U> for T
where U: FromAngle<T>,

Source§

fn into_angle(self) -> U

Performs a conversion into T.
Source§

impl<WpParam, T, U> IntoCam16Unclamped<WpParam, T> for U
where T: Cam16FromUnclamped<WpParam, U>,

Source§

type Scalar = <T as Cam16FromUnclamped<WpParam, U>>::Scalar

The number type that’s used in parameters when converting.
Source§

fn into_cam16_unclamped( self, parameters: BakedParameters<WpParam, <U as IntoCam16Unclamped<WpParam, T>>::Scalar>, ) -> T

Converts self into C, using the provided parameters.
Source§

impl<T, U> IntoColor<U> for T
where U: FromColor<T>,

Source§

fn into_color(self) -> U

Convert into T with values clamped to the color defined bounds Read more
Source§

impl<T, U> IntoColorUnclamped<U> for T
where U: FromColorUnclamped<T>,

Source§

fn into_color_unclamped(self) -> U

Convert into T. The resulting color might be invalid in its color space Read more
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<F, T> IntoSample<T> for F
where T: FromSample<F>,

Source§

fn into_sample(self) -> T

Source§

impl<T> IntoStimulus<T> for T

Source§

fn into_stimulus(self) -> T

Converts self into T, while performing the appropriate scaling, rounding and clamping.
Source§

impl<T> OverflowingAs for T

Source§

fn overflowing_as<Dst>(self) -> (Dst, bool)
where T: OverflowingCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> OverflowingCastFrom<Src> for Dst
where Src: OverflowingCast<Dst>,

Source§

fn overflowing_cast_from(src: Src) -> (Dst, bool)

Casts the value.
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<R, P> ReadPrimitive<R> for P
where R: Read + ReadEndian<P>, P: Default,

Source§

fn read_from_little_endian(read: &mut R) -> Result<Self, Error>

Read this value from the supplied reader. Same as ReadEndian::read_from_little_endian().
Source§

fn read_from_big_endian(read: &mut R) -> Result<Self, Error>

Read this value from the supplied reader. Same as ReadEndian::read_from_big_endian().
Source§

fn read_from_native_endian(read: &mut R) -> Result<Self, Error>

Read this value from the supplied reader. Same as ReadEndian::read_from_native_endian().
Source§

impl<T> SaturatingAs for T

Source§

fn saturating_as<Dst>(self) -> Dst
where T: SaturatingCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> SaturatingCastFrom<Src> for Dst
where Src: SaturatingCast<Dst>,

Source§

fn saturating_cast_from(src: Src) -> Dst

Casts the value.
Source§

impl<T> StrictAs for T

Source§

fn strict_as<Dst>(self) -> Dst
where T: StrictCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> StrictCastFrom<Src> for Dst
where Src: StrictCast<Dst>,

Source§

fn strict_cast_from(src: Src) -> Dst

Casts the value.
Source§

impl<T, U> ToSample<U> for T
where U: FromSample<T>,

Source§

fn to_sample_(self) -> U

Source§

impl<T, C> TryComponentsInto<C> for T
where C: TryFromComponents<T>,

Source§

type Error = <C as TryFromComponents<T>>::Error

The error for when try_into_colors fails to cast.
Source§

fn try_components_into(self) -> Result<C, <T as TryComponentsInto<C>>::Error>

Try to cast this collection of color components into a collection of colors. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T, U> TryIntoColor<U> for T
where U: TryFromColor<T>,

Source§

fn try_into_color(self) -> Result<U, OutOfBounds<U>>

Convert into T, returning ok if the color is inside of its defined range, otherwise an OutOfBounds error is returned which contains the unclamped color. Read more
Source§

impl<C, U> UintsFrom<C> for U
where C: IntoUints<U>,

Source§

fn uints_from(colors: C) -> U

Cast a collection of colors into a collection of unsigned integers.
Source§

impl<C, U> UintsInto<C> for U
where C: FromUints<U>,

Source§

fn uints_into(self) -> C

Cast this collection of unsigned integers into a collection of colors.
Source§

impl<T> UnwrappedAs for T

Source§

fn unwrapped_as<Dst>(self) -> Dst
where T: UnwrappedCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> UnwrappedCastFrom<Src> for Dst
where Src: UnwrappedCast<Dst>,

Source§

fn unwrapped_cast_from(src: Src) -> Dst

Casts the value.
Source§

impl<T> Upcast<T> for T

Source§

fn upcast(&self) -> Option<&T>

Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WrappingAs for T

Source§

fn wrapping_as<Dst>(self) -> Dst
where T: WrappingCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> WrappingCastFrom<Src> for Dst
where Src: WrappingCast<Dst>,

Source§

fn wrapping_cast_from(src: Src) -> Dst

Casts the value.
Source§

impl<S, T> Duplex<S> for T
where T: FromSample<S> + ToSample<S>,

Source§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T> MaybeSendSync for T
where T: Send + Sync,

Source§

impl<T> WasmNotSend for T
where T: Send,

Source§

impl<T> WasmNotSendSync for T

Source§

impl<T> WasmNotSync for T
where T: Sync,