Early days: oxframe is under very active development. The API is not stable yet and may change from version to version. Pin the exact version in your Cargo.toml and check the changelog before upgrading.

Animated charts and plots

Axes, number lines, function plots and bars. Data coordinates map into a box, and every part follows when a range animates.

Loading live preview…

Source of charts — edit in the playground
//! Axes and plots: a curve with a dot riding on it, a morph into another
//! function, a zoom out, then a benchmark whose bars grow with their labels.

use oxframe::prelude::*;

#[oxframe::scene]
pub async fn functions(view: View) {
    let axes = view.add(Axes::new().x_range(-5.0..5.0).y_range(-2.0..2.0));
    let wave = axes.plot(|x| x.sin());
    wave.draw_on().await;

    // A signal drives the dot along the curve.
    let x = Signal::new(-5.0);
    axes.point_on(&wave, x.clone());
    x.to(5.0, 2.0).await;

    wave.to_fn(|x| (2.0 * x).sin() * 1.5, 1.0).await;
    // The grid, ticks, labels and the curve follow the range.
    axes.x_range().to(-10.0..10.0, 1.2).await;
    wait_for(0.4).await;
}

#[oxframe::scene]
pub async fn build_times(view: View) {
    transition(Transition::fade()).await;
    let title = view.add(Txt::new().text("Build time, s").h1().to_edge(Edge::Top));
    let axes = view.add(
        Axes::new()
            .x_range(-0.5..3.5)
            .y_range(0.0..10.0)
            .height(pct(55))
            .y(40.0),
    );
    let bars = axes
        .bars([0.0, 0.0, 0.0, 0.0])
        .labels(["rustc", "gcc", "clang", "zig"]);
    title.fade_in().await;
    stagger(
        0.15,
        [3.2, 7.5, 5.1, 9.4].into_iter().enumerate(),
        |(i, v)| bars.value(i).to(v, 1.0),
    )
    .await;
    wait_for(0.6).await;
}

pub fn project() -> Project {
    Project::new("charts")
        .scene(functions())
        .scene(build_times())
}

Axes

Axes::new() is a plotting box with a grid, two axes with arrows, ticks and labels. .x_range(-5.0..5.0) and .y_range(-2.0..2.0) set the data ranges; .width() and .height() set the box in pixels or pct() of the frame (80 % by 60 % by default); .no_grid() drops the grid. The axes cross at 0 when 0 is in range. Lines and labels take their colors from the theme.

let axes = view.add(Axes::new().x_range(-5.0..5.0).y_range(-2.0..2.0));
axes.x_range().to(-10.0..10.0, 1.0).await;

axes.x_range() is a signal of Interval, so axes.x_range().to(-10.0..10.0, 1.0) zooms out. Ticks sit on round steps (1, 2 or 5 times a power of ten), and each label keeps its value while the range slides, so nothing flickers.

Function plots

axes.plot(|x| x.sin()) draws a function and returns a Plot. It is sampled again every frame, finer where it bends, so it follows the ranges. It breaks where it jumps — tan has no vertical line across its gaps — and it is cut at the edge of the box. .stroke(Accent) and .width(4.0) style it, .draw_on() draws it like any path, and plot.to_fn(|x| x.cos(), 1.0) morphs it: every x moves from the old value to the new one.

let wave = axes.plot(|x| x.sin()).stroke(Accent);
wave.draw_on().await;
let x = Signal::new(-5.0);
axes.point_on(&wave, x.clone());
x.to(5.0, 2.0).await;
wave.to_fn(|x| (2.0 * x).sin(), 1.0).await;

axes.point_on(&plot, x) keeps a dot on the curve at a signal x. axes.c2p(x, y) gives the scene point of a data point, and axes.place(node, x, y) keeps any node there while the ranges move.

Bars and data

axes.bars([3.0, 7.5]) draws a bar per value at x = 0, 1, 2… with the value above it; set the x range to -0.5..n - 0.5. With bars the y axis sits at the left edge. bars.value(0).to(9.0, 1.0) grows one bar and its label, and .labels(["a", "b"]) names the bars. axes.line_series(..) draws a line through data points and axes.scatter(..) a dot per point.

let bars = axes.bars([0.0, 0.0, 0.0]).labels(["rustc", "gcc", "clang"]);
stagger(0.15, [3.2, 7.5, 5.1].into_iter().enumerate(), |(i, v)| bars.value(i).to(v, 1.0)).await;

Number lines

NumberLine::new(0.0..10.0) is a single axis with ticks, labels and an arrow. It has the same methods as the axes: x_range, c2p and place.

There is no pie chart yet; draw slices with Custom. Long rows of a BulletList now wrap inside the frame, so a list of results next to a chart stays readable.