wiki-space-filling-curve.rs

2.1 kB · rust · 72 lines

1use figures::{ink, save, Board, Ramp};2use mrlyrs::core::error::Result;3use std::collections::HashSet;45const LEVEL: usize = 5;6const SIDE: i64 = 32;7const CELLS: usize = 1024;8const STEPS: usize = 1023;9const MARGIN: f64 = 0.08;10const WEIGHT: f64 = 0.42;1112fn rule(c: char) -> &'static str {13    match c {14        'A' => "+BF-AFA-FB+",15        'B' => "-AF+BFB+FA-",16        _ => "",17    }18}1920fn expand(word: &str) -> String {21    word.chars()22        .map(|c| match c {23            'A' | 'B' => rule(c).to_string(),24            _ => c.to_string(),25        })26        .collect()27}2829fn main() -> Result<()> {30    let mut word = String::from("A");31    for _ in 0..LEVEL {32        word = expand(&word);33    }34    let heads = [(1i64, 0i64), (0, 1), (-1, 0), (0, -1)];35    let mut head = 0usize;36    let mut pts = vec![(0i64, 0i64)];37    for c in word.chars() {38        match c {39            '+' => head = (head + 1) % 4,40            '-' => head = (head + 3) % 4,41            'F' => {42                let (x, y) = pts[pts.len() - 1];43                pts.push((x + heads[head].0, y + heads[head].1));44            }45            _ => {}46        }47    }48    assert_eq!(pts.len() - 1, STEPS);49    let x0 = pts.iter().map(|p| p.0).min().unwrap_or(0);50    let y0 = pts.iter().map(|p| p.1).min().unwrap_or(0);51    let x1 = pts.iter().map(|p| p.0).max().unwrap_or(0);52    let y1 = pts.iter().map(|p| p.1).max().unwrap_or(0);53    assert_eq!((x1 - x0 + 1, y1 - y0 + 1), (SIDE, SIDE));54    let seen: HashSet<(i64, i64)> = pts.iter().copied().collect();55    assert_eq!(seen.len(), CELLS);56    let mut board = Board::square();57    let area = board.frame(MARGIN);58    let cell = area.w / SIDE as f64;59    let at = |(x, y): (i64, i64)| {60        (61            area.x + ((x - x0) as f64 + 0.5) * cell,62            area.y + ((y1 - y) as f64 + 0.5) * cell,63        )64    };65    let ramp = Ramp::tone(ink::blue(), ink::yellow());66    for i in 0..STEPS {67        let t = i as f64 / (STEPS - 1) as f64;68        board.segment(at(pts[i]), at(pts[i + 1]), WEIGHT * cell, ramp.at(t));69    }70    save("wiki-space-filling-curve", &board)?;71    Ok(())72}