field.rs
2.9 kB · rust · 103 lines
1use crate::board::{Board, Frame};2use crate::ink::Ramp;34// FIELDS56fn range(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, lo + 1.0)11 } else {12 (lo, hi)13 }14}1516/// Paints a scalar field into the frame, nearest sampled, normalised to its own range.17pub fn draw(18 board: &mut Board,19 frame: Frame,20 width: usize,21 height: usize,22 values: &[f64],23 ramp: &Ramp,24) {25 draw_range(board, frame, width, height, values, range(values), ramp);26}2728/// Paints a scalar field into the frame, nearest sampled, normalised to the given range.29pub fn draw_range(30 board: &mut Board,31 frame: Frame,32 width: usize,33 height: usize,34 values: &[f64],35 span: (f64, f64),36 ramp: &Ramp,37) {38 if width == 0 || height == 0 || values.len() < width * height {39 return;40 }41 let (lo, hi) = span;42 let reach = if (hi - lo).abs() < 1e-12 {43 1.044 } else {45 hi - lo46 };47 let x0 = frame.x.ceil().max(0.0) as usize;48 let y0 = frame.y.ceil().max(0.0) as usize;49 let x1 = ((frame.x + frame.w).floor().max(0.0) as usize).min(board.width);50 let y1 = ((frame.y + frame.h).floor().max(0.0) as usize).min(board.height);51 for py in y0..y1 {52 for px in x0..x1 {53 let u = (px as f64 + 0.5 - frame.x) / frame.w;54 let v = (py as f64 + 0.5 - frame.y) / frame.h;55 let col = ((u * width as f64) as usize).min(width - 1);56 let row = ((v * height as f64) as usize).min(height - 1);57 let t = (values[row * width + col] - lo) / reach;58 board.blend(px, py, ramp.at(t), 1.0);59 }60 }61}6263/// Paints a function over the unit square into the frame, sampled on a resolution by resolution grid.64pub fn sample(65 board: &mut Board,66 frame: Frame,67 resolution: usize,68 f: impl Fn(f64, f64) -> f64,69 ramp: &Ramp,70) {71 if resolution == 0 {72 return;73 }74 let mut values = Vec::with_capacity(resolution * resolution);75 for row in 0..resolution {76 for col in 0..resolution {77 let u = (col as f64 + 0.5) / resolution as f64;78 let v = (row as f64 + 0.5) / resolution as f64;79 values.push(f(u, v));80 }81 }82 draw(board, frame, resolution, resolution, &values, ramp);83}8485#[cfg(test)]86mod tests {87 use super::*;88 use crate::ink;89 #[test]90 fn a_flat_field_paints_the_ramps_low_end_everywhere() {91 let mut board = Board::new(64, 64, ink::ground());92 let frame = Frame::new(0.0, 0.0, 64.0, 64.0);93 sample(94 &mut board,95 frame,96 8,97 |_, _| 1.0,98 &Ramp::tone(ink::blue(), ink::yellow()),99 );100 let blue = ink::blue();101 assert_eq!(board.pixels[0], [blue.r, blue.g, blue.b, 255]);102 }103}