moire-local-limit.rs
4.2 kB · rust · 132 lines
1use figures::board::{Board, Frame};2use figures::ink::Ramp;3use figures::{field, ink, save};4use mrlyrs::core::error::Result;5use mrlyrs::math::two::{carpet, Cell2d};67const LOW: usize = 21;8const HIGH: usize = 23;9const PANEL: usize = 410;10const EDGE: f64 = 82.0;11const STEPS: usize = 400;1213fn inked(cell: &Cell2d) -> Result<Vec<Vec<bool>>> {14 let n = cell.width();15 let mut rows = vec![vec![false; n]; n];16 for (i, row) in rows.iter_mut().enumerate() {17 for (j, slot) in row.iter_mut().enumerate() {18 *slot = cell.types().get(&[i, j])? != 0;19 }20 }21 Ok(rows)22}2324fn pieces(lo: f64, hi: f64) -> Vec<(f64, usize, usize)> {25 let mut cuts = vec![lo, hi];26 for n in [LOW, HIGH] {27 for k in 1..n {28 let x = k as f64 / n as f64;29 if x > lo && x < hi {30 cuts.push(x);31 }32 }33 }34 cuts.sort_by(|a, b| a.total_cmp(b));35 cuts.windows(2)36 .filter(|w| w[1] > w[0])37 .map(|w| {38 let mid = 0.5 * (w[0] + w[1]);39 let a = ((mid * LOW as f64) as usize).min(LOW - 1);40 let b = ((mid * HIGH as f64) as usize).min(HIGH - 1);41 (w[1] - w[0], a, b)42 })43 .collect()44}4546fn overlay(low: &[Vec<bool>], high: &[Vec<bool>]) -> Vec<f64> {47 let side = PANEL as f64;48 let spans: Vec<Vec<(f64, usize, usize)>> = (0..PANEL)49 .map(|k| pieces(k as f64 / side, (k + 1) as f64 / side))50 .collect();51 let mut values = Vec::with_capacity(PANEL * PANEL);52 for rows in &spans {53 for cols in &spans {54 let mut area = 0.0;55 for &(ly, ra, rb) in rows {56 for &(lx, ca, cb) in cols {57 if low[ra][ca] != high[rb][cb] {58 area += lx * ly;59 }60 }61 }62 values.push(area * side * side);63 }64 }65 values66}6768fn limit(u: f64, v: f64) -> f64 {69 0.5 * (1.0 - (1.0 - 2.0 * u).abs() * (1.0 - 2.0 * v).abs())70}7172fn fill(n: usize) -> f64 {73 let q = (n - 1) as f64 / (2 * n) as f64;74 1.0 - q * q75}7677fn arcs(board: &mut Board, frame: Frame, kappa: f64, thick: f64, chords: bool) {78 for (sx, sy) in [(1.0, 1.0), (1.0, -1.0), (-1.0, 1.0), (-1.0, -1.0)] {79 if chords {80 let a = frame.at(0.5 * (1.0 - sx), 0.5 * (1.0 - sy * kappa));81 let b = frame.at(0.5 * (1.0 - sx * kappa), 0.5 * (1.0 - sy));82 board.segment(a, b, 2.0, ink::dim());83 }84 let arc: Vec<(f64, f64)> = (0..=STEPS)85 .map(|k| {86 let x = kappa + (1.0 - kappa) * k as f64 / STEPS as f64;87 frame.at(0.5 * (1.0 - sx * x), 0.5 * (1.0 - sy * kappa / x))88 })89 .collect();90 board.polyline(&arc, thick, ink::orange());91 }92}9394fn main() -> Result<()> {95 let low = inked(&carpet(LOW, 1)?)?;96 let high = inked(&carpet(HIGH, 1)?)?;97 assert_eq!(low.iter().flatten().filter(|&&on| on).count(), 341);98 assert_eq!(high.iter().flatten().filter(|&&on| on).count(), 408);99100 let raw = overlay(&low, &high);101 let mean = raw.iter().sum::<f64>() / raw.len() as f64;102 let (a, b) = (fill(LOW), fill(HIGH));103 assert!((mean - (a + b - 2.0 * a * b)).abs() < 1e-9);104105 let side = PANEL as f64;106 let smooth: Vec<f64> = (0..PANEL * PANEL)107 .map(|k| {108 limit(109 ((k % PANEL) as f64 + 0.5) / side,110 ((k / PANEL) as f64 + 0.5) / side,111 )112 })113 .collect();114 let smean = smooth.iter().sum::<f64>() / smooth.len() as f64;115 assert!((smean - 0.375).abs() < 1e-12);116117 let kappa = 2f64.sqrt() - 1.0;118 assert!((2.0 * kappa - 2f64.sqrt() * (1.0 - kappa)).abs() < 1e-12);119120 let ramp = Ramp::tone(ink::panel(), ink::blue());121 let mut board = Board::square();122 let first = Frame::new(EDGE, EDGE, side, side);123 let far = board.width as f64 - EDGE - side;124 let second = Frame::new(far, far, side, side);125 field::draw_range(&mut board, first, PANEL, PANEL, &raw, (0.0, 1.0), &ramp);126 field::draw_range(&mut board, second, PANEL, PANEL, &smooth, (0.0, 0.5), &ramp);127128 arcs(&mut board, second, kappa, 4.0, true);129 arcs(&mut board, first, kappa, 3.0, false);130 save("moire-local-limit", &board)?;131 Ok(())132}