designs.rs

8.7 kB · rust · 243 lines

1use super::models::Cell3d;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) -> Result<Cell3d> {13    crate::dim::grow::<3>(pattern, level)14}1516/// Builds the cube the universe code names, deepened to the given fractal level.17pub fn create(code: Code, number: usize, level: usize, base: usize) -> Result<Cell3d> {18    build(factory::create(code, number, 3, base, 1)?, level)19}2021/// Builds a cube from its corner patterns, deepened to the given fractal level.22pub fn from_corners(23    corners: &[Vec<u8>],24    number: usize,25    level: usize,26    base: usize,27) -> Result<Cell3d> {28    build(29        factory::create_from_corners(corners, number, 3, base, 1)?,30        level,31    )32}3334/// Builds the all-void cube at the given size and level.35pub fn zeros(number: usize, level: usize) -> Result<Cell3d> {36    build(atoms::zeros_3d(number), level)37}3839/// Builds the solid cube at the given size and level.40pub fn ones(number: usize, level: usize) -> Result<Cell3d> {41    build(atoms::ones_3d(number), level)42}4344/// Builds a random cube of the given density at the given size and level.45pub fn noise(number: usize, level: usize, density: f64) -> Result<Cell3d> {46    build(atoms::noise_3d(number, density), level)47}4849/// Draws a random universe code and builds its cube.50pub fn random(number: usize, level: usize, base: usize) -> Result<Cell3d> {51    let total = factory::total_codes(3, base);52    let code = state::randint(0, (total - 1) as i64) as Code;53    create(code, number, level, base)54}5556/// Builds the Menger sponge, filled where at most one coordinate is odd, at the given level.57///58/// ```59/// let sponge = mrlymath::three::carpet(3, 1).unwrap();60/// assert_eq!(sponge.types().sum(), 20);61/// ```62pub fn carpet(number: usize, level: usize) -> Result<Cell3d> {63    build(atoms::carpet_3d(number), level)64}6566/// Builds the net cube, filled where at least two coordinates are odd, at the given level.67pub fn net(number: usize, level: usize) -> Result<Cell3d> {68    build(atoms::net_3d(number), level)69}7071/// Builds the cube of beams along the x axis at the given size and level.72pub fn xtree(number: usize, level: usize) -> Result<Cell3d> {73    build(atoms::xtree_3d(number), level)74}7576/// Builds the cube of beams along the y axis at the given size and level.77pub fn ytree(number: usize, level: usize) -> Result<Cell3d> {78    build(atoms::ytree_3d(number), level)79}8081/// Builds the cube of beams along the z axis at the given size and level.82pub fn ztree(number: usize, level: usize) -> Result<Cell3d> {83    build(atoms::ztree_3d(number), level)84}8586/// Builds the checkerboard cube, filled where all coordinate parities agree, at the given level.87pub fn void(number: usize, level: usize) -> Result<Cell3d> {88    build(atoms::void_3d(number), level)89}9091/// Builds the point cube, filled where every coordinate is odd, at the given level.92pub fn point(number: usize, level: usize) -> Result<Cell3d> {93    build(atoms::point_3d(number), level)94}9596/// Builds the dust cube, filled where every coordinate is even, at the given level.97pub fn dust(number: usize, level: usize) -> Result<Cell3d> {98    build(atoms::dust_3d(number), level)99}100101/// Builds the cube of rods along the x axis at the given size and level.102pub fn xline(number: usize, level: usize) -> Result<Cell3d> {103    build(atoms::xline_3d(number), level)104}105106/// Builds the cube of rods along the y axis at the given size and level.107pub fn yline(number: usize, level: usize) -> Result<Cell3d> {108    build(atoms::yline_3d(number), level)109}110111/// Builds the cube of rods along the z axis at the given size and level.112pub fn zline(number: usize, level: usize) -> Result<Cell3d> {113    build(atoms::zline_3d(number), level)114}115116/// Builds the star cube, filled where exactly one coordinate is odd, at the given level.117pub fn star(number: usize, level: usize) -> Result<Cell3d> {118    build(atoms::star_3d(number), level)119}120121// LEVEL SET122123/// Builds the cube filled wherever the residue sum lands in the levels, at the given level.124///125/// Carpet, net and void are the three presets of this one engine: the levels are all a126/// symmetric cube design ever names.127///128/// ```129/// let sponge = mrlymath::three::level_set(3, &[0, 1], 1, 2).unwrap();130/// assert_eq!(sponge, mrlymath::three::carpet(3, 1).unwrap());131/// ```132pub fn level_set(number: usize, levels: &[usize], level: usize, base: usize) -> Result<Cell3d> {133    create(levels_code(3, base, levels), number, level, base)134}135136// NAMED137138/// Builds the cube the name picks, deepened to the given fractal level.139pub fn named(design: Design, number: usize, level: usize) -> Result<Cell3d> {140    let pattern = match design {141        Design::Carpet => atoms::carpet_3d(number),142        Design::Net => atoms::net_3d(number),143        Design::Xtree => atoms::xtree_3d(number),144        Design::Ytree => atoms::ytree_3d(number),145        Design::Ztree => atoms::ztree_3d(number),146        Design::Void => atoms::void_3d(number),147        Design::Point => atoms::point_3d(number),148        Design::Dust => atoms::dust_3d(number),149        Design::Xline => atoms::xline_3d(number),150        Design::Yline => atoms::yline_3d(number),151        Design::Zline => atoms::zline_3d(number),152        Design::Star => atoms::star_3d(number),153        other => return value_error(format!("design {} is not 3d.", other.name())),154    };155    build(pattern, level)156}157158/// Draws one of the six named cube designs and builds it at the given size and level.159pub fn random_classic(number: usize, level: usize) -> Result<Cell3d> {160    let classics = mrlycore::tile::classics(3);161    let pick = state::randint(0, classics.len() as i64 - 1) as usize;162    named(classics[pick], number, level)163}164165#[cfg(test)]166mod tests {167    use super::*;168    #[test]169    fn carpet_is_menger() {170        let c = carpet(3, 1).unwrap();171        assert_eq!(c.types().sum(), 20);172        assert_eq!(carpet(3, 2).unwrap().types().sum(), 400);173        assert_eq!(create(23, 3, 1, 2).unwrap(), c);174    }175    #[test]176    fn the_level_sets_name_the_symmetric_three() {177        for (levels, preset) in [178            (vec![0, 1], carpet(3, 2).unwrap()),179            (vec![2, 3], net(3, 2).unwrap()),180            (vec![0, 3], void(3, 2).unwrap()),181        ] {182            assert_eq!(level_set(3, &levels, 2, 2).unwrap(), preset);183        }184        assert_eq!(levels_code(3, 2, &[0, 1]), 23);185        assert_eq!(level_set(3, &[], 1, 2).unwrap().types().sum(), 0);186        assert_eq!(187            level_set(3, &[0, 1, 2, 3], 1, 2).unwrap(),188            ones(3, 1).unwrap()189        );190    }191    #[test]192    fn level_sets_take_a_wider_base() {193        let corners: Vec<Vec<u8>> = factory::residue_corners(3, 3)194            .into_iter()195            .filter(|corner| corner.iter().map(|&b| b as usize).sum::<usize>() <= 1)196            .collect();197        let by_hand = from_corners(&corners, 3, 1, 3).unwrap();198        assert_eq!(level_set(3, &[0, 1], 1, 3).unwrap(), by_hand);199    }200    #[test]201    fn the_named_builders_answer_to_the_classics() {202        for (design, plain) in [203            (Design::Carpet, carpet(3, 1).unwrap()),204            (Design::Net, net(3, 1).unwrap()),205            (Design::Xtree, xtree(3, 1).unwrap()),206            (Design::Ytree, ytree(3, 1).unwrap()),207            (Design::Ztree, ztree(3, 1).unwrap()),208            (Design::Void, void(3, 1).unwrap()),209            (Design::Point, point(3, 1).unwrap()),210            (Design::Dust, dust(3, 1).unwrap()),211            (Design::Xline, xline(3, 1).unwrap()),212            (Design::Yline, yline(3, 1).unwrap()),213            (Design::Zline, zline(3, 1).unwrap()),214            (Design::Star, star(3, 1).unwrap()),215        ] {216            assert_eq!(named(design, 3, 1).unwrap(), plain);217        }218        assert!(named(Design::Htree, 3, 1).is_err());219        assert!(named(Design::Hline, 3, 1).is_err());220    }221    #[test]222    fn random_classic_draws_one_of_the_named_six() {223        let _g = state::guard();224        state::seed(7);225        let a = random_classic(3, 1).unwrap();226        state::seed(7);227        assert_eq!(random_classic(3, 1).unwrap(), a);228        let classics: Vec<Cell3d> = mrlycore::tile::classics(3)229            .into_iter()230            .map(|design| named(design, 3, 1).unwrap())231            .collect();232        assert!(classics.contains(&a));233    }234    #[test]235    fn trees_are_orientations_of_each_other() {236        let x = xtree(3, 1).unwrap();237        let z = ztree(3, 1).unwrap();238        let images: Vec<Vec<u8>> = (0..24)239            .map(|i| x.clone().orient(i).unwrap().types().bytes().to_vec())240            .collect();241        assert!(images.contains(&z.types().bytes().to_vec()));242    }243}