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}