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}