research-multilayers.rs

4.6 kB · rust · 156 lines

1use figures::{ink, save, Board, Color, Frame};2use mrlyrs::core::error::Result;34const NA: f64 = 2.3;5const NB: f64 = 1.45;6const BASE: usize = 7;7const LEVELS: usize = 12;8const BINS: usize = 128;9const SAMPLES: usize = 48;1011type C = (f64, f64);12type M = [C; 4];1314fn cm(a: C, b: C) -> C {15    (a.0 * b.0 - a.1 * b.1, a.0 * b.1 + a.1 * b.0)16}1718fn ca(a: C, b: C) -> C {19    (a.0 + b.0, a.1 + b.1)20}2122fn mul(p: &M, q: &M) -> M {23    [24        ca(cm(p[0], q[0]), cm(p[1], q[2])),25        ca(cm(p[0], q[1]), cm(p[1], q[3])),26        ca(cm(p[2], q[0]), cm(p[3], q[2])),27        ca(cm(p[2], q[1]), cm(p[3], q[3])),28    ]29}3031fn lay(n: f64, ph: f64) -> M {32    let (s, c) = ph.sin_cos();33    [(c, 0.0), (0.0, s / n), (0.0, n * s), (c, 0.0)]34}3536fn ratio(m: &M) -> f64 {37    let re = NB * m[0].0 + NB * NB * m[1].0 - m[2].0 - NB * m[3].0;38    let im = NB * m[0].1 + NB * NB * m[1].1 - m[2].1 - NB * m[3].1;39    (re * re + im * im).sqrt() / (2.0 * NB)40}4142fn open_levels(digits: &[usize], delta: f64) -> Vec<bool> {43    let mut m = lay(NA, delta);44    let mut out = Vec::with_capacity(LEVELS);45    let mut dead = false;46    let mut span = delta;47    for _ in 0..LEVELS {48        let spacer = lay(NB, span.rem_euclid(std::f64::consts::TAU));49        let mut n = [(1.0, 0.0), (0.0, 0.0), (0.0, 0.0), (1.0, 0.0)];50        for d in 0..BASE {51            n = mul(&n, if digits.contains(&d) { &m } else { &spacer });52        }53        dead |= n.iter().any(|z| !(z.0.abs() < 1e100 && z.1.abs() < 1e100));54        m = n;55        span *= BASE as f64;56        out.push(!dead && ratio(&m) < 1.0);57    }58    out59}6061fn brute(digits: &[usize], level: u32, delta: f64) -> f64 {62    let mut m = [(1.0, 0.0), (0.0, 0.0), (0.0, 0.0), (1.0, 0.0)];63    for p in 0..BASE.pow(level) {64        let mut q = p;65        let mut on = true;66        for _ in 0..level {67            on &= digits.contains(&(q % BASE));68            q /= BASE;69        }70        m = mul(&m, &lay(if on { NA } else { NB }, delta));71    }72    ratio(&m)73}7475fn recursive(digits: &[usize], level: u32, delta: f64) -> f64 {76    let mut m = lay(NA, delta);77    let mut span = delta;78    for _ in 0..level {79        let spacer = lay(NB, span);80        let mut n = [(1.0, 0.0), (0.0, 0.0), (0.0, 0.0), (1.0, 0.0)];81        for d in 0..BASE {82            n = mul(&n, if digits.contains(&d) { &m } else { &spacer });83        }84        m = n;85        span *= BASE as f64;86    }87    ratio(&m)88}8990fn density(digits: &[usize]) -> Vec<Vec<f64>> {91    let width = std::f64::consts::FRAC_PI_2 / BINS as f64;92    let columns: Vec<Vec<f64>> = (0..BINS)93        .map(|bin| {94            let mut column = vec![0.0; LEVELS];95            for s in 0..SAMPLES {96                let delta = width * (bin as f64 + (s as f64 + 0.5) / SAMPLES as f64);97                for (k, open) in open_levels(digits, delta).into_iter().enumerate() {98                    if open {99                        column[k] += 1.0 / SAMPLES as f64;100                    }101                }102            }103            column104        })105        .collect();106    (0..LEVELS)107        .map(|k| columns.iter().map(|column| column[k]).collect())108        .collect()109}110111fn panel(board: &mut Board, frame: &Frame, grid: &[Vec<f64>], hue: Color) {112    board.rect(frame.x, frame.y, frame.w, frame.h, ink::line());113    let gap = 3.0;114    for (row, cells) in frame.rows(LEVELS).iter().zip(grid) {115        let w = row.w / BINS as f64;116        for (i, t) in cells.iter().enumerate() {117            let c = ink::mix(ink::panel(), hue, *t);118            board.rect(119                row.x + i as f64 * w,120                row.y + gap / 2.0,121                w + 0.5,122                row.h - gap,123                c,124            );125        }126    }127}128129fn main() -> Result<()> {130    let critical = [0, 2, 4, 6];131    let dark = [0, 2, 3, 4, 6];132    for digits in [&critical[..], &dark[..]] {133        for delta in [0.37, 1.21] {134            let (a, b) = (brute(digits, 3, delta), recursive(digits, 3, delta));135            assert!((a - b).abs() <= 1e-9 * a.max(1.0));136        }137    }138    let light = density(&critical);139    let fading = density(&dark);140    assert_eq!(light.len(), LEVELS);141    assert!(light[LEVELS - 1].iter().sum::<f64>() > 1.6 * fading[LEVELS - 1].iter().sum::<f64>());142    let mut board = Board::square();143    let frame = board.frame(0.08);144    let side = frame.w * 0.47;145    let top = Frame::new(frame.x, frame.y, side, side);146    let low = Frame::new(147        frame.x + frame.w - side,148        frame.y + frame.h - side,149        side,150        side,151    );152    panel(&mut board, &top, &light, ink::blue());153    panel(&mut board, &low, &fading, ink::orange());154    save("research-multilayers", &board)?;155    Ok(())156}