ArcLen

#![allow(unused)]
fn main() {
pub struct ArcLen { /* ... */ }

impl ArcLen {
    pub fn new(c: &CubicBez) -> Self;
    pub fn total(&self) -> f64;
    pub fn t_at_frac(&self, s: f64) -> f64;
}
}

Precomputes a chord-length LUT of a cubic Bézier (256 segments) so you can convert "I want to be halfway along the arc" into the parameter t that puts you there. t_at_frac(s) maps s ∈ [0,1] (fraction of total arc length) to t ∈ [0,1] (curve parameter) via binary search + linear interpolation between samples.

Compare a dot driven by eval(t) (top) with one driven by eval(t_at_frac(t)) (bottom). The top dot slows down and speeds up as the curve bends; the bottom dot glides at constant screen speed.

Source

#![allow(unused)]
fn main() {
use anim_core::*;

pub const DURATION: f64 = 4.0;

fn base() -> CubicBez {
    CubicBez::new(
        Vec2::new(-260.0, 0.0),
        Vec2::new(-120.0, 220.0),
        Vec2::new(120.0, -220.0),
        Vec2::new(260.0, 0.0),
    )
}

fn shift(c: CubicBez, dy: f64) -> CubicBez {
    let s = Vec2::new(0.0, dy);
    CubicBez::new(c.p0 + s, c.p1 + s, c.p2 + s, c.p3 + s)
}

pub fn scene(t: f64) -> DrawList {
    let top = shift(base(), 70.0);
    let bot = shift(base(), -70.0);
    let al_bot = ArcLen::new(&bot);

    let phase = (t / DURATION).clamp(0.0, 1.0);

    // Top: raw parameter — bunches through curvy regions.
    let p_top = top.eval(phase);
    // Bottom: arc-length reparameterization — constant screen speed.
    let p_bot = bot.eval(al_bot.t_at_frac(phase));

    let curve_col = Color::rgb(150, 150, 160);
    let dot_raw = Color::rgb(255, 180, 80);
    let dot_arc = Color::rgb(80, 200, 255);

    vec![
        Cmd::Stroke {
            path: top,
            width: 2.0,
            color: curve_col,
        },
        Cmd::Stroke {
            path: bot,
            width: 2.0,
            color: curve_col,
        },
        Cmd::FillCircle {
            center: p_top,
            r: 10.0,
            color: dot_raw,
        },
        Cmd::FillCircle {
            center: p_bot,
            r: 10.0,
            color: dot_arc,
        },
    ]
}
}