plot.rs

3.5 kB · rust · 112 lines

1use crate::board::{Board, Frame};2use mrlycore::Color;34// RANGES56fn span(values: &[f64]) -> (f64, f64) {7    let lo = values.iter().copied().fold(f64::MAX, f64::min);8    let hi = values.iter().copied().fold(f64::MIN, f64::max);9    if hi - lo < 1e-12 {10        (lo - 0.5, hi + 0.5)11    } else {12        (lo, hi)13    }14}1516// MARKS1718/// Draws one bar per value from the foot of the frame, the tallest filling its height.19pub fn bars(board: &mut Board, frame: Frame, values: &[f64], gap: f64, color: Color) {20    if values.is_empty() {21        return;22    }23    let peak = values.iter().copied().fold(0.0f64, |a, b| a.max(b.abs()));24    if peak <= 0.0 {25        return;26    }27    let slot = frame.w / values.len() as f64;28    let pad = gap * slot / 2.0;29    for (i, value) in values.iter().enumerate() {30        let h = frame.h * (value.abs() / peak);31        board.rect(32            frame.x + i as f64 * slot + pad,33            frame.y + frame.h - h,34            slot - 2.0 * pad,35            h,36            color,37        );38    }39}4041/// Draws one disc at each point, in board pixels.42pub fn dots(board: &mut Board, pts: &[(f64, f64)], r: f64, color: Color) {43    for (x, y) in pts {44        board.disc(*x, *y, r, color);45    }46}4748/// Strokes one circle per radius about the same centre, in board pixels.49pub fn rings(board: &mut Board, center: (f64, f64), radii: &[f64], thick: f64, color: Color) {50    for r in radii {51        board.ring(center.0, center.1, *r, thick, color);52    }53}5455/// Strokes the values as a staircase across the frame, one tread per value.56pub fn staircase(board: &mut Board, frame: Frame, values: &[f64], thick: f64, color: Color) {57    if values.is_empty() {58        return;59    }60    let (lo, hi) = span(values);61    let slot = frame.w / values.len() as f64;62    let mut pts = Vec::with_capacity(values.len() * 2);63    for (i, value) in values.iter().enumerate() {64        let y = frame.y + frame.h * (1.0 - (value - lo) / (hi - lo));65        pts.push((frame.x + i as f64 * slot, y));66        pts.push((frame.x + (i + 1) as f64 * slot, y));67    }68    board.polyline(&pts, thick, color);69}7071/// Strokes a curve through the paired data, both axes mapped from their own range into the frame.72pub fn curve(board: &mut Board, frame: Frame, xs: &[f64], ys: &[f64], thick: f64, color: Color) {73    let n = xs.len().min(ys.len());74    if n < 2 {75        return;76    }77    let (x_lo, x_hi) = span(&xs[..n]);78    let (y_lo, y_hi) = span(&ys[..n]);79    let pts: Vec<(f64, f64)> = (0..n)80        .map(|i| {81            (82                frame.x + frame.w * (xs[i] - x_lo) / (x_hi - x_lo),83                frame.y + frame.h * (1.0 - (ys[i] - y_lo) / (y_hi - y_lo)),84            )85        })86        .collect();87    board.polyline(&pts, thick, color);88}8990/// Strokes the bare hairline box of the frame, without a tick or a label.91pub fn axis(board: &mut Board, frame: Frame, color: Color) {92    let thick = (frame.w.min(frame.h) / 512.0).max(1.0);93    let corners = [94        (frame.x, frame.y),95        (frame.x + frame.w, frame.y),96        (frame.x + frame.w, frame.y + frame.h),97        (frame.x, frame.y + frame.h),98        (frame.x, frame.y),99    ];100    board.polyline(&corners, thick, color);101}102103/// Strokes the hairline foot of the frame alone.104pub fn baseline(board: &mut Board, frame: Frame, color: Color) {105    let thick = (frame.w.min(frame.h) / 512.0).max(1.0);106    board.segment(107        (frame.x, frame.y + frame.h),108        (frame.x + frame.w, frame.y + frame.h),109        thick,110        color,111    );112}