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}