A function plot, drawn on

chart

The brief. Plot a function: axes, a curve that draws itself on, and a dot that rides the curve.

Open in the playground Source on GitHub

What it uses

The code

examples/src/chart.rs in oxframe-sdk, for oxframe 0.20.2. In the playground it compiles in the browser and you can change it.

//! A function plot: grid, axes with arrows, a curve drawn on with `end`,
//! a dot following it — timed by markers that can be moved in the editor.

use oxframe::prelude::*;

const W: f64 = 1400.0;
const H: f64 = 700.0;

fn curve(phase: f64) -> Vec<Vec2> {
    (0..=200)
        .map(|i| {
            let t = i as f64 / 200.0;
            let x = -W / 2.0 + t * W;
            let y = -(t * std::f64::consts::TAU * 2.0 + phase).sin() * H * 0.3;
            Vec2::new(x, y)
        })
        .collect()
}

#[oxframe::scene]
pub async fn chart(view: View) {
    view.add(Grid::new().width(W).height(H).spacing(70.0).opacity(0.0));
    let grid = view.children()[0];
    let axes = [
        view.add(
            Line::new()
                .points([(-W / 2.0 - 20.0, 0.0), (W / 2.0 + 30.0, 0.0)])
                .end_arrow(true)
                .end(0.0),
        ),
        view.add(
            Line::new()
                .points([(-W / 2.0, H / 2.0), (-W / 2.0, -H / 2.0 - 30.0)])
                .end_arrow(true)
                .end(0.0),
        ),
    ];
    let plot = view.add(
        Line::new()
            .points(curve(0.0))
            .stroke(color!("#68abdf"))
            .line_width(6.0)
            .end(0.0),
    );
    let label = view.add(
        Txt::new()
            .text("y = sin(4πx)")
            .font_size(44.0)
            .position((0.0, -H / 2.0 - 60.0))
            .opacity(0.0),
    );

    all((
        grid.opacity().to(1.0, 0.6),
        axes[0].end().to(1.0, 0.6),
        axes[1].end().to(1.0, 0.6),
    ))
    .await;

    // Marker: move it in the timeline to retime everything after it.
    wait_until("draw").await;
    all((
        plot.end()
            .to(1.0, 2.0)
            .ease(ease!("draw", presets::IN_OUT_SINE)),
        label.opacity().to(1.0, 1.0),
    ))
    .await;

    let dot = view.add(
        Circle::new()
            .size(28.0)
            .fill(color!("#f5c242"))
            .position(curve(0.0)[0]),
    );
    let pts = curve(0.0);
    let t = Signal::new(0.0f64);
    {
        // Bind the dot's position to a progress signal.
        let (t, pts) = (t.clone(), pts.clone());
        let pos = Signal::derived(move || {
            let u = t.get().clamp(0.0, 1.0) * (pts.len() - 1) as f64;
            let i = (u as usize).min(pts.len() - 2);
            pts[i].lerp(pts[i + 1], u - i as f64)
        });
        let p = pos.clone();
        dot.x().bind(move || p.get().x);
        dot.y().bind(move || pos.get().y);
    }
    wait_until("dot").await;
    t.to(1.0, tune!("dot_seconds", 2.5)).await;

    // Shift the phase: points tween element-wise.
    plot.points().to(curve(std::f64::consts::PI), 1.2).await;
    wait_for(0.5).await;
}

pub fn project() -> Project {
    Project::new("chart").scene(chart())
}

More examples