demo-tube.rs

4.1 kB · rust · 135 lines

1use mrlycore::errors::Result;2use mrlyfig::board::{Board, Frame};3use mrlyfig::{ink, plot, save, Grid};4use mrlymath::two;56const CODE: u128 = 495;7const SIDE: usize = 3;8const LEVEL: usize = 5;9const SPAN: usize = 243;10const EPS: f64 = 7.0;11const CELL: f64 = 2.0;12const STEPS: usize = 1200;1314fn lower(f: &[f64], d: &mut [f64], hull: &mut [usize], edge: &mut [f64]) {15    let n = f.len();16    let mut k = 0;17    hull[0] = 0;18    edge[0] = f64::NEG_INFINITY;19    edge[1] = f64::INFINITY;20    for q in 1..n {21        let mut cut;22        loop {23            let p = hull[k];24            cut = ((f[q] + (q * q) as f64) - (f[p] + (p * p) as f64)) / (2 * (q - p)) as f64;25            if k > 0 && cut <= edge[k] {26                k -= 1;27            } else {28                break;29            }30        }31        k += 1;32        hull[k] = q;33        edge[k] = cut;34        edge[k + 1] = f64::INFINITY;35    }36    k = 0;37    for (q, slot) in d.iter_mut().enumerate() {38        while edge[k + 1] < q as f64 {39            k += 1;40        }41        let gap = q as f64 - hull[k] as f64;42        *slot = gap * gap + f[hull[k]];43    }44}4546fn transform(types: &[u8], side: usize) -> Vec<f64> {47    let far = (4 * side * side) as f64;48    let mut square: Vec<f64> = types49        .iter()50        .map(|&b| if b != 0 { 0.0 } else { far })51        .collect();52    let mut lane = vec![0.0; side];53    let mut out = vec![0.0; side];54    let mut hull = vec![0usize; side];55    let mut edge = vec![0.0; side + 1];56    for col in 0..side {57        for row in 0..side {58            lane[row] = square[row * side + col];59        }60        lower(&lane, &mut out, &mut hull, &mut edge);61        for row in 0..side {62            square[row * side + col] = out[row];63        }64    }65    for row in 0..side {66        lane.copy_from_slice(&square[row * side..(row + 1) * side]);67        lower(&lane, &mut out, &mut hull, &mut edge);68        square[row * side..(row + 1) * side].copy_from_slice(&out);69    }70    square.iter().map(|v| v.sqrt()).collect()71}7273fn limit(t: f64) -> f64 {74    let dimension = 8f64.ln() / 3f64.ln();75    let (flat, slope, bend) = if t < 0.5 {76        (1.0, 4.0 / 5.0, -4.0 / 7.0)77    } else {78        (9.0 / 8.0, 3.0 / 10.0, -1.0 / 14.0)79    };80    t.powf(dimension - 2.0) * (flat + slope * t + bend * t * t)81}8283fn main() -> Result<()> {84    let cells = two::create(CODE, SIDE, LEVEL, 0, SIDE)?;85    let types = cells.types().bytes().to_vec();86    assert_eq!(types.len(), SPAN * SPAN);87    assert_eq!(types.iter().filter(|&&b| b != 0).count(), 32768);88    let dist = transform(&types, SPAN);89    assert_eq!(dist.iter().filter(|&&v| v > 0.0 && v <= EPS).count(), 20440);9091    let seam = limit(1.0 / 3.0);92    assert!((seam - 379.0 / 280.0).abs() < 1e-12);93    assert!((limit(1.0) - seam).abs() < 1e-12);94    let phases: Vec<f64> = (0..=STEPS)95        .map(|i| 1.0 / 3.0 + (2.0 / 3.0) * i as f64 / STEPS as f64)96        .collect();97    let profile: Vec<f64> = phases.iter().map(|&t| limit(t)).collect();98    let high = profile.iter().copied().fold(f64::MIN, f64::max);99    let low = profile.iter().copied().fold(f64::MAX, f64::min);100    assert!(((high - low) / low - 0.003662).abs() < 1e-5);101102    let mut board = Board::square();103    let margin = (board.width as f64 * 0.08).round();104    let plate = CELL * SPAN as f64;105    let sheet = Frame::new(margin, margin, plate, plate);106    let lattice = Grid::new(sheet, SPAN, SPAN, 0.0);107    for row in 0..SPAN {108        for col in 0..SPAN {109            let at = row * SPAN + col;110            if types[at] != 0 {111                lattice.fill(&mut board, col, row, ink::fg());112            } else if dist[at] <= EPS {113                lattice.fill(&mut board, col, row, ink::blue());114            }115        }116    }117    let tall = 320.0;118    let panel = Frame::new(119        board.width as f64 - margin - plate,120        board.height as f64 - margin - tall,121        plate,122        tall,123    );124    plot::axis(&mut board, panel, ink::line());125    plot::curve(126        &mut board,127        panel.inset(24.0),128        &phases,129        &profile,130        4.0,131        ink::yellow(),132    );133    save("demo-tube", &board)?;134    Ok(())135}