rust-ppm 0.1.1

Small RGB image and plotting library for generating PPM graphics
Documentation
use std::io;
use std::time::Instant;

use rust_ppm::{Axes, Image, LineStyle, MarkerStyle, Pixel, Plot};

const IMAGE_SIZE: usize = 612;

fn main() -> io::Result<()> {
    rings_image().save("output.ppm")?;

    let points = [
        (-1.0, 1.0),
        (-0.5, 0.25),
        (0.0, 0.0),
        (0.5, 0.25),
        (1.0, 1.0),
        (2.0, 4.0),
        (2.5, 6.25),
        (3.0, 9.0),
        (3.5, 12.25),
        (4.0, 16.0),
    ];
    Plot::new()
        .size(800, 600)
        .margins(80, 60)
        .axes(
            Axes::new()
                .x_limits((-1.0, 4.0))
                .y_limits((-2.0, 16.0))
                .labels("x", "x squared")
                .title("Quadratic samples"),
        )
        .markers(
            &points,
            MarkerStyle::new().size(5).color(Pixel::rgb(210, 45, 45)),
        )
        .save("scatter_plot.ppm")?;

    let curve_points: Vec<(f64, f64)> = (-20..=20)
        .map(|x| x as f64 / 5.0)
        .map(|x| (x, x * x - 2.0 * x - 1.0))
        .collect();
    Plot::new()
        .axes(Axes::new().labels("x", "y").title("Automatic limits"))
        .line(
            &curve_points,
            LineStyle::new().width(2).color(Pixel::rgb(25, 95, 170)),
        )
        .markers(
            &curve_points,
            MarkerStyle::new().size(3).color(Pixel::rgb(230, 145, 25)),
        )
        .scale(2)
        .save("curve_plot.ppm")?;

    let multi_curve_a: Vec<(f64, f64)> = (-100..=100)
        .map(|i| {
            let x = i as f64 / 20.0;
            let y = x * x - 2.0 * x - 1.0;
            (x, y)
        })
        .collect();

    let multi_curve_b: Vec<(f64, f64)> = (-100..=100)
        .map(|i| {
            let x = i as f64 / 20.0;
            let y = 0.5 * x * x + 3.0 * x - 4.0;
            (x, y)
        })
        .collect();

    let multi_curve_c: Vec<(f64, f64)> = (-100..=100)
        .map(|i| {
            let x = i as f64 / 20.0;
            let y = -(x * x) + 6.0 * x + 2.0;
            (x, y)
        })
        .collect();

    Plot::new()
        .size(800, 600)
        .margins(90, 70)
        .axes(
            Axes::new()
                .labels("x", "y")
                .title("Multiple curves example"),
        )
        .line(
            &multi_curve_a,
            LineStyle::new().width(3).color(Pixel::rgb(220, 60, 60)),
        )
        .line(
            &multi_curve_b,
            LineStyle::new().width(3).color(Pixel::rgb(60, 120, 220)),
        )
        .line(
            &multi_curve_c,
            LineStyle::new().width(3).color(Pixel::rgb(40, 180, 110)),
        )
        .save("multi_curve_plot.ppm")?;

    let huge_curve_points: Vec<(f64, f64)> = (0..200_000)
        .map(|i| {
            let x = i as f64 / 1000.0;
            let y = (x * 0.9).sin() * 15.0 + (x * 0.25).cos() * 7.0 + (x * 0.07).sin() * 4.0;
            (x, y)
        })
        .collect();

    let start = Instant::now();
    let huge_curve = Plot::new()
        .size(800, 600)
        .margins(90, 70)
        .axes(
            Axes::new()
                .labels("x", "y")
                .title("Large multi-point curve"),
        )
        .line(
            &huge_curve_points,
            LineStyle::new().width(2).color(Pixel::rgb(118, 120, 200)),
        )
        .render();
    let render_elapsed = start.elapsed();
    huge_curve.save("huge_curve_plot.ppm")?;
    println!("huge curve render time: {:?}", render_elapsed);

    let line_image = Plot::new()
        .size(800, 600)
        .margins(80, 60)
        .axes(
            Axes::from_limits((-1.0, 4.0), (-2.0, 16.0))
                .x_ticks([-1.0, 0.0, 1.0, 2.0, 3.0, 4.0])
                .y_ticks([-2.0, 0.0, 4.0, 8.0, 12.0, 16.0])
                .labels("x", "x squared")
                .title("Custom ticks and line style"),
        )
        .line(
            &points,
            LineStyle::new().width(4).color(Pixel::rgb(40, 135, 80)),
        )
        .render();
    line_image.save("line_plot.ppm")
}

fn rings_image() -> Image {
    let center = IMAGE_SIZE as f64 / 2.0;

    Image::from_pixel_fn(IMAGE_SIZE, IMAGE_SIZE, |x, y| {
        let dx = x as f64 - center;
        let dy = y as f64 - center;
        let radius = (dx * dx + dy * dy).sqrt();
        let value = if ((radius / 8.0) as u32).is_multiple_of(2) {
            255
        } else {
            0
        };
        Pixel::rgb(value, value, value)
    })
}