demo-star.rs
4.5 kB · rust · 133 lines
1use mrlycore::errors::Result;2use mrlyfig::board::Board;3use mrlyfig::ink::Ramp;4use mrlyfig::{ink, save};5use mrlymath::six::star::Star;67const CODE: u128 = 23;8const LAYERS: usize = 28;9const HALF: i64 = 6;10const CELLS: usize = 400;11const SUPER: usize = 5;12const REACH: f64 = 0.712;13const TAIL: f64 = 0.02;1415struct Look {16 ink: f64,17 arm: bool,18}1920fn place(u: f64, v: f64, n: f64) -> (i64, i64) {21 let side = 4.0 * n;22 let raise = v * 6f64.sqrt() / 3.0;23 let across = 0.5 + (raise + u * 2f64.sqrt()) / 2.0;24 let up = 0.5 - raise;25 if !(0.0..1.0).contains(&across) || !(0.0..1.0).contains(&up) {26 return (-1, -1);27 }28 (29 ((across * side).floor() as i64).min(side as i64 - 1),30 (2 * (up * 2.0 * n).floor() as i64).min(side as i64 - 2),31 )32}3334fn read(star: &Star, u: f64, v: f64) -> Option<Look> {35 let deepest = 2 * LAYERS - 1;36 let n = deepest as i64;37 let (x, z) = place(u, v, deepest as f64);38 if x < 0 {39 return None;40 }41 let y = 6 * n - 2 - x - z;42 star.cell(deepest, x, z)?;43 let arm = (x - y).abs() <= HALF || (y - z).abs() <= HALF || (z - x).abs() <= HALF;44 let (mut total, mut reached) = (0.0, 0.0);45 for step in 0..LAYERS {46 let number = 2 * step + 1;47 let (x, z) = place(u, v, number as f64);48 if x < 0 {49 continue;50 }51 if let Some(fill) = star.cell(number, x, z) {52 total += f64::from(u8::from(fill));53 reached += 1.0;54 }55 }56 Some(Look {57 ink: total / reached,58 arm,59 })60}6162fn window(values: &[(f64, f64, f64)]) -> (f64, f64) {63 let mut whole: Vec<f64> = values64 .iter()65 .filter(|(_, _, cover)| *cover > 0.99)66 .map(|(value, _, _)| *value)67 .collect();68 whole.sort_by(|a, b| a.partial_cmp(b).expect("a field of finite means"));69 let step = ((whole.len() - 1) as f64 * TAIL) as usize;70 (whole[step], whole[whole.len() - 1 - step])71}7273fn main() -> Result<()> {74 let star = Star::new(CODE)?;75 assert_eq!(star.arm(55, 0)?.reduced(), (28, 55));76 assert_eq!(star.hexagon(3)?.reduced(), (7, 9));77 let fine = (CELLS * SUPER) as f64;78 let seats = (SUPER * SUPER) as f64;79 let mut values = vec![(0.0f64, 0.0f64, 0.0f64); CELLS * CELLS];80 for line in 0..CELLS {81 for slot in 0..CELLS {82 let (mut total, mut arms, mut hits) = (0.0, 0.0, 0.0);83 for down in 0..SUPER {84 for right in 0..SUPER {85 let u = (2.0 * ((slot * SUPER + right) as f64 + 0.5) / fine - 1.0) * REACH;86 let v = (1.0 - 2.0 * ((line * SUPER + down) as f64 + 0.5) / fine) * REACH;87 if let Some(look) = read(&star, u, v) {88 total += look.ink;89 arms += f64::from(u8::from(look.arm));90 hits += 1.0;91 }92 }93 }94 values[line * CELLS + slot] = if hits > 0.0 {95 (total / hits, arms / hits, hits / seats)96 } else {97 (0.0, 0.0, 0.0)98 };99 }100 }101 let drawn = values.iter().filter(|(_, _, cover)| *cover > 0.0).count();102 assert!(drawn * 2 > values.len());103 let starred = values.iter().filter(|(_, arm, _)| *arm > 0.5).count();104 assert!(starred * 5 < drawn && starred * 200 > drawn);105 let (low, high) = window(&values);106 assert!(high - low > 0.01 && low < 0.5 && high > 0.5);107108 let bright = Ramp::new(vec![ink::ground(), ink::blue(), ink::yellow()]);109 let quiet = Ramp::tone(ink::ground(), ink::mix(ink::line(), ink::dim(), 0.5));110 let mut board = Board::square();111 let frame = board.frame(0.05);112 let x0 = frame.x.ceil() as usize;113 let y0 = frame.y.ceil() as usize;114 let x1 = ((frame.x + frame.w).floor() as usize).min(board.width);115 let y1 = ((frame.y + frame.h).floor() as usize).min(board.height);116 for py in y0..y1 {117 for px in x0..x1 {118 let u = (px as f64 + 0.5 - frame.x) / frame.w;119 let v = (py as f64 + 0.5 - frame.y) / frame.h;120 let slot = ((u * CELLS as f64) as usize).min(CELLS - 1);121 let line = ((v * CELLS as f64) as usize).min(CELLS - 1);122 let (value, arm, cover) = values[line * CELLS + slot];123 if cover <= 0.0 {124 continue;125 }126 let t = (value - low) / (high - low);127 let shade = ink::mix(quiet.at(t), bright.at(t), arm);128 board.blend(px, py, shade, cover);129 }130 }131 save("demo-star", &board)?;132 Ok(())133}