designs.rs

7.4 kB · rust · 210 lines

1use super::Cell3d;2use crate::core::atoms;3use crate::core::errors::{value_error, Result};4use crate::core::tensor::Tensor;5use crate::core::tile::Design;6use crate::math::bang::factory;7use crate::math::bang::universe::Code;89pub use crate::math::bang::factory::levels_code;1011fn build(pattern: Tensor, level: usize) -> Result<Cell3d> {12    crate::math::dim::grow::<3>(pattern, level)13}1415/// Builds the cube the universe code names, deepened to the given fractal level.16pub fn create(code: Code, number: usize, level: usize, base: usize) -> Result<Cell3d> {17    build(factory::create(code, number, 3, base, 1)?, level)18}1920/// Builds a cube from its corner patterns, deepened to the given fractal level.21pub fn from_corners(22    corners: &[Vec<u8>],23    number: usize,24    level: usize,25    base: usize,26) -> Result<Cell3d> {27    build(28        factory::create_from_corners(corners, number, 3, base, 1)?,29        level,30    )31}3233/// Builds the all-void cube at the given size and level.34pub fn zeros(number: usize, level: usize) -> Result<Cell3d> {35    build(atoms::zeros_3d(number), level)36}3738/// Builds the solid cube at the given size and level.39pub fn ones(number: usize, level: usize) -> Result<Cell3d> {40    build(atoms::ones_3d(number), level)41}4243/// Builds the Menger sponge, filled where at most one coordinate is odd, at the given level.44///45/// ```46/// let sponge = mrlyrs::math::three::carpet(3, 1).unwrap();47/// assert_eq!(sponge.types().sum(), 20);48/// ```49pub fn carpet(number: usize, level: usize) -> Result<Cell3d> {50    build(atoms::carpet_3d(number), level)51}5253/// Builds the net cube, filled where at least two coordinates are odd, at the given level.54pub fn net(number: usize, level: usize) -> Result<Cell3d> {55    build(atoms::net_3d(number), level)56}5758/// Builds the cube of beams along the x axis at the given size and level.59pub fn xtree(number: usize, level: usize) -> Result<Cell3d> {60    build(atoms::xtree_3d(number), level)61}6263/// Builds the cube of beams along the y axis at the given size and level.64pub fn ytree(number: usize, level: usize) -> Result<Cell3d> {65    build(atoms::ytree_3d(number), level)66}6768/// Builds the cube of beams along the z axis at the given size and level.69pub fn ztree(number: usize, level: usize) -> Result<Cell3d> {70    build(atoms::ztree_3d(number), level)71}7273/// Builds the checkerboard cube, filled where all coordinate parities agree, at the given level.74pub fn void(number: usize, level: usize) -> Result<Cell3d> {75    build(atoms::void_3d(number), level)76}7778/// Builds the point cube, filled where every coordinate is odd, at the given level.79pub fn point(number: usize, level: usize) -> Result<Cell3d> {80    build(atoms::point_3d(number), level)81}8283/// Builds the dust cube, filled where every coordinate is even, at the given level.84pub fn dust(number: usize, level: usize) -> Result<Cell3d> {85    build(atoms::dust_3d(number), level)86}8788/// Builds the cube of rods along the x axis at the given size and level.89pub fn xline(number: usize, level: usize) -> Result<Cell3d> {90    build(atoms::xline_3d(number), level)91}9293/// Builds the cube of rods along the y axis at the given size and level.94pub fn yline(number: usize, level: usize) -> Result<Cell3d> {95    build(atoms::yline_3d(number), level)96}9798/// Builds the cube of rods along the z axis at the given size and level.99pub fn zline(number: usize, level: usize) -> Result<Cell3d> {100    build(atoms::zline_3d(number), level)101}102103/// Builds the star cube, filled where exactly one coordinate is odd, at the given level.104pub fn star(number: usize, level: usize) -> Result<Cell3d> {105    build(atoms::star_3d(number), level)106}107108// LEVEL SET109110/// Builds the cube filled wherever the residue sum lands in the levels, at the given level.111///112/// Carpet, net and void are the three presets of this one engine: the levels are all a113/// symmetric cube design ever names.114///115/// ```116/// let sponge = mrlyrs::math::three::level_set(3, &[0, 1], 1, 2).unwrap();117/// assert_eq!(sponge, mrlyrs::math::three::carpet(3, 1).unwrap());118/// ```119pub fn level_set(number: usize, levels: &[usize], level: usize, base: usize) -> Result<Cell3d> {120    create(levels_code(3, base, levels), number, level, base)121}122123// NAMED124125/// Builds the cube the name picks, deepened to the given fractal level.126pub fn named(design: Design, number: usize, level: usize) -> Result<Cell3d> {127    let pattern = match design {128        Design::Carpet => atoms::carpet_3d(number),129        Design::Net => atoms::net_3d(number),130        Design::Xtree => atoms::xtree_3d(number),131        Design::Ytree => atoms::ytree_3d(number),132        Design::Ztree => atoms::ztree_3d(number),133        Design::Void => atoms::void_3d(number),134        Design::Point => atoms::point_3d(number),135        Design::Dust => atoms::dust_3d(number),136        Design::Xline => atoms::xline_3d(number),137        Design::Yline => atoms::yline_3d(number),138        Design::Zline => atoms::zline_3d(number),139        Design::Star => atoms::star_3d(number),140        other => return value_error(format!("design {} is not 3d.", other.name())),141    };142    build(pattern, level)143}144145#[cfg(test)]146mod tests {147    use super::*;148    #[test]149    fn carpet_is_menger() {150        let c = carpet(3, 1).unwrap();151        assert_eq!(c.types().sum(), 20);152        assert_eq!(carpet(3, 2).unwrap().types().sum(), 400);153        assert_eq!(create(23, 3, 1, 2).unwrap(), c);154    }155    #[test]156    fn the_level_sets_name_the_symmetric_three() {157        for (levels, preset) in [158            (vec![0, 1], carpet(3, 2).unwrap()),159            (vec![2, 3], net(3, 2).unwrap()),160            (vec![0, 3], void(3, 2).unwrap()),161        ] {162            assert_eq!(level_set(3, &levels, 2, 2).unwrap(), preset);163        }164        assert_eq!(levels_code(3, 2, &[0, 1]), 23);165        assert_eq!(level_set(3, &[], 1, 2).unwrap().types().sum(), 0);166        assert_eq!(167            level_set(3, &[0, 1, 2, 3], 1, 2).unwrap(),168            ones(3, 1).unwrap()169        );170    }171    #[test]172    fn level_sets_take_a_wider_base() {173        let corners: Vec<Vec<u8>> = factory::residue_corners(3, 3)174            .into_iter()175            .filter(|corner| corner.iter().map(|&b| b as usize).sum::<usize>() <= 1)176            .collect();177        let by_hand = from_corners(&corners, 3, 1, 3).unwrap();178        assert_eq!(level_set(3, &[0, 1], 1, 3).unwrap(), by_hand);179    }180    #[test]181    fn the_named_builders_answer_to_the_classics() {182        for (design, plain) in [183            (Design::Carpet, carpet(3, 1).unwrap()),184            (Design::Net, net(3, 1).unwrap()),185            (Design::Xtree, xtree(3, 1).unwrap()),186            (Design::Ytree, ytree(3, 1).unwrap()),187            (Design::Ztree, ztree(3, 1).unwrap()),188            (Design::Void, void(3, 1).unwrap()),189            (Design::Point, point(3, 1).unwrap()),190            (Design::Dust, dust(3, 1).unwrap()),191            (Design::Xline, xline(3, 1).unwrap()),192            (Design::Yline, yline(3, 1).unwrap()),193            (Design::Zline, zline(3, 1).unwrap()),194            (Design::Star, star(3, 1).unwrap()),195        ] {196            assert_eq!(named(design, 3, 1).unwrap(), plain);197        }198        assert!(named(Design::Htree, 3, 1).is_err());199        assert!(named(Design::Hline, 3, 1).is_err());200    }201    #[test]202    fn trees_are_orientations_of_each_other() {203        let x = xtree(3, 1).unwrap();204        let z = ztree(3, 1).unwrap();205        let images: Vec<Vec<u8>> = (0..24)206            .map(|i| x.clone().orient(i).unwrap().types().bytes().to_vec())207            .collect();208        assert!(images.contains(&z.types().bytes().to_vec()));209    }210}