six.rs
6.6 kB · rust · 200 lines
1use crate::{checked, code_of, Fault};2use mrlyrs::core::{json, Json};3use mrlyrs::math::bang::Code;4use mrlyrs::math::counts::six::{5 solid_slice_boundary, solid_slice_edges, solid_slice_triangles, solid_slice_vertices,6};7use mrlyrs::math::six::{self, Cell6d};8use mrlyrs::math::three;9use mrlyrs::num::boolean;10use wasm_bindgen::prelude::*;1112/// Projects the cube the code names to a hexagon, iso, pro or cut, and renders it as SVG at the scale.13#[wasm_bindgen]14pub fn hex_svg(15 code: &str,16 number: usize,17 level: usize,18 base: usize,19 projection: &str,20 scale: usize,21) -> Result<String, Fault> {22 let code = code_of(code)?;23 let cell = match projection {24 "pro" => six::pro_design(Code::from(code), number, level, base)?,25 "cut" => six::cut_design(Code::from(code), number, level, base)?,26 _ => six::iso_design(Code::from(code), number, level, base)?,27 };28 Ok(six::svg(&cell, scale, None, 0)?)29}3031// SLICE3233fn slice(code: &str, number: usize, level: usize, base: usize) -> Result<Cell6d, Fault> {34 Ok(six::cut(&three::create(35 Code::from(code_of(code)?),36 number,37 level,38 base,39 )?)?)40}4142/// Tallies the diagonal section of the cube the code names: the mesh, the fill, its pieces and holes, and the solid closed forms at that side, as JSON.43#[wasm_bindgen]44pub fn slice_census(code: &str, number: usize, level: usize, base: usize) -> Result<String, Fault> {45 let cell = slice(code, number, level, base)?;46 let tally = six::census(&cell, false);47 let side = number.pow(level as u32);48 Ok(json!({49 "side": side,50 "triangles": tally.triangles,51 "boundary": tally.boundary_edges,52 "edges": tally.edges,53 "interior": tally.interior_edges,54 "vertices": tally.vertices,55 "euler": tally.euler,56 "fills": tally.fills,57 "voids": tally.voids,58 "components": six::components(&cell)?,59 "holes": six::holes(&cell)?,60 "giant": six::giant(&cell)?,61 "closed": {62 "triangles": solid_slice_triangles(side)?.to_string(),63 "boundary": solid_slice_boundary(side)?.to_string(),64 "edges": solid_slice_edges(side)?.to_string(),65 "vertices": solid_slice_vertices(side)?.to_string(),66 },67 })68 .to_string())69}7071/// Walks the level-one slice of the code at odd side `2k-1`, one row per `k`, as JSON.72#[wasm_bindgen]73pub fn slice_series(code: &str, max_k: usize) -> Result<String, Fault> {74 if !(1..=16).contains(&max_k) {75 return Err(Fault::new("max_k must be between 1 and 16."));76 }77 let code = code_of(code)?;78 let mut rows = Vec::new();79 for k in 1..=max_k {80 let number = 2 * k - 1;81 let cell = six::cut(&three::create(Code::from(code), number, 1, 2)?)?;82 rows.push(json!({83 "k": k,84 "n": number,85 "fills": six::census(&cell, false).fills,86 "components": six::components(&cell)?,87 "holes": six::holes(&cell)?,88 }));89 }90 Ok(json!(rows).to_string())91}9293/// Splits the level-one hexagon of the side between the carpet and the net: their filled triangles, the two together, the hexagon's triangles and whether the partition is exact, as JSON.94#[wasm_bindgen]95pub fn slice_partition(number: usize) -> Result<String, Fault> {96 let carpet = six::census(&slice("23", number, 1, 2)?, false);97 let net = six::census(&slice("232", number, 1, 2)?, false);98 let together = carpet.fills + net.fills;99 Ok(json!({100 "carpet": carpet.fills,101 "net": net.fills,102 "together": together,103 "hexagon": carpet.triangles,104 "exact": together == carpet.triangles,105 })106 .to_string())107}108109// SPECTROMETER110111fn sixteenths(walsh: &[i64]) -> Vec<i64> {112 walsh113 .iter()114 .enumerate()115 .map(|(mask, &value)| if mask == 0 { 8 - value } else { -value })116 .collect()117}118119fn level_sums(parts: &[i64]) -> [i64; 4] {120 let mut sums = [0i64; 4];121 for (mask, &part) in parts.iter().enumerate() {122 sums[mask.count_ones() as usize] += part;123 }124 sums125}126127fn ink_numerator(sums: &[i64; 4], number: i64) -> i64 {128 let sign = if number % 4 == 1 { -1 } else { 1 };129 (6 * sums[0] - 3 * sums[3] * sign) * number * number130 + (4 * sums[1] - 2 * sums[2] * sign) * number131 + 4 * sums[2]132 - (2 * sums[1] + 3 * sums[3]) * sign133}134135/// Reads the Walsh spectrum of the cube design the code names, its four level sums, and the exact diagonal-slice ink those sums set at every odd side `2k-1`, as JSON.136#[wasm_bindgen]137pub fn walsh_spectrum(code: &str, max_k: usize) -> Result<String, Fault> {138 if !(1..=16).contains(&max_k) {139 return Err(Fault::new("max_k must be between 1 and 16."));140 }141 let code = checked(code, 3, 2)?;142 let walsh = boolean::walsh_spectrum(code, 3);143 let parts = sixteenths(&walsh);144 let sums = level_sums(&parts);145 let mut weights = [0u32; 4];146 for corner in 0..8usize {147 if (code >> corner) & 1 == 1 {148 weights[corner.count_ones() as usize] += 1;149 }150 }151 let coefficients: Vec<Json> = (0..8usize)152 .map(|mask| {153 json!({154 "mask": mask,155 "level": mask.count_ones(),156 "walsh": walsh[mask],157 "sixteenths": parts[mask],158 "value": parts[mask] as f64 / 16.0,159 })160 })161 .collect();162 let levels: Vec<Json> = (0..4usize)163 .map(|level| {164 json!({165 "level": level,166 "sixteenths": sums[level],167 "eighths": sums[level] / 2,168 "sigma": sums[level] as f64 / 16.0,169 })170 })171 .collect();172 let law: Vec<Json> = (1..=max_k)173 .map(|k| {174 let number = 2 * k as i64 - 1;175 let numerator = ink_numerator(&sums, number);176 json!({177 "k": k,178 "n": number,179 "s": if number % 4 == 1 { -1 } else { 1 },180 "numerator": numerator,181 "denominator": 96 * number * number,182 "ink": numerator as f64 / (96 * number * number) as f64,183 "fills": numerator / 16,184 "triangles": 6 * number * number,185 })186 })187 .collect();188 Ok(json!({189 "code": code.to_string(),190 "corners": code.count_ones(),191 "background": sums[0] as f64 / 16.0,192 "blink": -(sums[3] as f64) / 32.0,193 "spectrum": walsh,194 "coefficients": coefficients,195 "levels": levels,196 "weights": weights.to_vec(),197 "law": law,198 })199 .to_string())200}