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}