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}