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}