designs.rs
7.5 kB · rust · 219 lines
1use super::models::Cell2d;2use crate::bang::factory;3use crate::bang::universe::Code;4use mrlycore::atoms;5use mrlycore::errors::{value_error, Result};6use mrlycore::state;7use mrlycore::tensor::Tensor;8use mrlycore::tile::Design;910pub use crate::bang::factory::levels_code;1112fn build(pattern: Tensor, level: usize, rotation: usize) -> Result<Cell2d> {13 let mut cell = crate::dim::grow::<2>(pattern, level)?;14 if rotation != 0 {15 cell = cell.rotate(rotation);16 }17 Ok(cell)18}1920/// Builds the design a universe code names, deepened to the level and rotated by quarter-turns.21pub fn create(22 code: Code,23 number: usize,24 level: usize,25 rotation: usize,26 base: usize,27) -> Result<Cell2d> {28 build(factory::create(code, number, 2, base, 1)?, level, rotation)29}3031/// Builds the design straight from its filled residue corners, deepened to the level and rotated by quarter-turns.32pub fn from_corners(33 corners: &[Vec<u8>],34 number: usize,35 level: usize,36 rotation: usize,37 base: usize,38) -> Result<Cell2d> {39 build(40 factory::create_from_corners(corners, number, 2, base, 1)?,41 level,42 rotation,43 )44}4546/// Builds the level-set design, filling every residue corner whose digits sum to a named level.47///48/// ```49/// let n = 5;50/// assert_eq!(51/// mrlymath::two::level_set(n, &[0, 1], 1, 0, 2).unwrap(),52/// mrlymath::two::carpet(n, 1).unwrap()53/// );54/// ```55pub fn level_set(56 number: usize,57 levels: &[usize],58 level: usize,59 rotation: usize,60 base: usize,61) -> Result<Cell2d> {62 create(levels_code(2, base, levels), number, level, rotation, base)63}6465/// Builds the design the name picks, deepened to the level and rotated by quarter-turns.66pub fn named(design: Design, number: usize, level: usize, rotation: usize) -> Result<Cell2d> {67 let pattern = match design {68 Design::Carpet => atoms::carpet_2d(number),69 Design::Net => atoms::net_2d(number),70 Design::Htree => atoms::htree_2d(number),71 Design::Vtree => atoms::vtree_2d(number),72 Design::Void => atoms::void_2d(number),73 Design::Point => atoms::point_2d(number),74 Design::Dust => atoms::dust_2d(number),75 Design::Hline => atoms::hline_2d(number),76 Design::Vline => atoms::vline_2d(number),77 Design::Star => atoms::star_2d(number),78 other => return value_error(format!("design {} is not 2d.", other.name())),79 };80 build(pattern, level, rotation)81}8283/// Builds an all-empty cell of the given size and level.84pub fn zeros(number: usize, level: usize) -> Result<Cell2d> {85 build(atoms::zeros_2d(number), level, 0)86}8788/// Builds an all-filled cell of the given size and level.89pub fn ones(number: usize, level: usize) -> Result<Cell2d> {90 build(atoms::ones_2d(number), level, 0)91}9293/// Draws a noise cell whose sites fill with the given probability, deepened to the level.94pub fn noise(number: usize, level: usize, density: f64) -> Result<Cell2d> {95 build(atoms::noise_2d(number, density), level, 0)96}9798/// Draws a random universe code and builds its design at the given size and level.99pub fn random(number: usize, level: usize, base: usize) -> Result<Cell2d> {100 let total = factory::total_codes(2, base);101 let code = state::randint(0, (total - 1) as i64) as Code;102 create(code, number, level, 0, base)103}104105/// Builds the carpet fractal, its seed pierced at every odd-odd site, deepened to the level.106pub fn carpet(number: usize, level: usize) -> Result<Cell2d> {107 build(atoms::carpet_2d(number), level, 0)108}109110/// Builds the net fractal, its seed on wherever a coordinate is odd, deepened to the level.111pub fn net(number: usize, level: usize) -> Result<Cell2d> {112 build(atoms::net_2d(number), level, 0)113}114115/// Builds the htree fractal, its seed striped along even rows, deepened to the level.116pub fn htree(number: usize, level: usize) -> Result<Cell2d> {117 build(atoms::htree_2d(number), level, 0)118}119120/// Builds the vtree fractal, its seed striped along even columns, deepened to the level.121pub fn vtree(number: usize, level: usize) -> Result<Cell2d> {122 build(atoms::vtree_2d(number), level, 0)123}124125/// Builds the void fractal, its seed a checkerboard on even parity, deepened to the level.126pub fn void(number: usize, level: usize) -> Result<Cell2d> {127 build(atoms::void_2d(number), level, 0)128}129130/// Builds the point fractal, its seed on at every odd-odd site, deepened to the level.131pub fn point(number: usize, level: usize) -> Result<Cell2d> {132 build(atoms::point_2d(number), level, 0)133}134135/// Builds the dust fractal, its seed on at every even-even site, deepened to the level.136pub fn dust(number: usize, level: usize) -> Result<Cell2d> {137 build(atoms::dust_2d(number), level, 0)138}139140/// Builds the hline fractal, its seed striped along odd rows, deepened to the level.141pub fn hline(number: usize, level: usize) -> Result<Cell2d> {142 build(atoms::hline_2d(number), level, 0)143}144145/// Builds the vline fractal, its seed striped along odd columns, deepened to the level.146pub fn vline(number: usize, level: usize) -> Result<Cell2d> {147 build(atoms::vline_2d(number), level, 0)148}149150/// Builds the star fractal, its seed on where exactly one coordinate is odd, deepened to the level.151pub fn star(number: usize, level: usize) -> Result<Cell2d> {152 build(atoms::star_2d(number), level, 0)153}154155#[cfg(test)]156mod tests {157 use super::*;158 #[test]159 fn carpet_fractal_growth() {160 let c = carpet(3, 3).unwrap();161 assert_eq!(c.width(), 27);162 assert_eq!(c.types().sum(), carpet(3, 1).unwrap().types().sum().pow(3));163 }164 #[test]165 fn create_matches_carpet() {166 let by_name = carpet(5, 2).unwrap();167 let by_code = create(7, 5, 2, 0, 2).unwrap();168 assert_eq!(by_name, by_code);169 }170 #[test]171 fn random_is_seeded() {172 let _g = mrlycore::state::guard();173 mrlycore::state::seed(99);174 let a = random(4, 1, 2).unwrap();175 mrlycore::state::seed(99);176 let b = random(4, 1, 2).unwrap();177 assert_eq!(a, b);178 }179 #[test]180 fn level_sets_reproduce_the_symmetric_designs() {181 for n in 1..8 {182 assert_eq!(183 level_set(n, &[0, 1], 1, 0, 2).unwrap(),184 carpet(n, 1).unwrap()185 );186 assert_eq!(level_set(n, &[1, 2], 1, 0, 2).unwrap(), net(n, 1).unwrap());187 assert_eq!(level_set(n, &[0, 2], 1, 0, 2).unwrap(), void(n, 1).unwrap());188 }189 assert_eq!(level_set(4, &[], 1, 0, 2).unwrap(), zeros(4, 1).unwrap());190 assert_eq!(191 level_set(4, &[0, 1, 2], 1, 0, 2).unwrap(),192 ones(4, 1).unwrap()193 );194 }195 #[test]196 fn level_sets_carry_level_and_rotation() {197 let deep = level_set(3, &[0, 1], 2, 0, 2).unwrap();198 assert_eq!(deep, carpet(3, 2).unwrap());199 let turned = level_set(5, &[1, 2], 1, 1, 2).unwrap();200 assert_eq!(turned, net(5, 1).unwrap().rotate(1));201 }202 #[test]203 fn named_builders_take_a_rotation() {204 for (design, plain) in [205 (Design::Carpet, carpet(5, 1).unwrap()),206 (Design::Net, net(5, 1).unwrap()),207 (Design::Htree, htree(5, 1).unwrap()),208 (Design::Vtree, vtree(5, 1).unwrap()),209 (Design::Void, void(5, 1).unwrap()),210 ] {211 assert_eq!(named(design, 5, 1, 0).unwrap(), plain);212 for k in 1..4 {213 assert_eq!(named(design, 5, 1, k).unwrap(), plain.clone().rotate(k));214 }215 }216 assert_eq!(named(Design::Vtree, 5, 1, 1).unwrap(), htree(5, 1).unwrap());217 assert!(named(Design::Ztree, 5, 1, 0).is_err());218 }219}