six.rs
6.4 kB · rust · 197 lines
1use crate::{checked, code_of, Fault};2use mrlycore::{json, Json};3use mrlymath::formulas::six as formulas;4use mrlymath::six::{self, Cell6d};5use mrlymath::three;6use mrlynum::boolean;7use wasm_bindgen::prelude::*;89/// Projects the cube the code names to a hexagon, iso, pro or cut, and renders it as SVG at the scale.10#[wasm_bindgen]11pub fn hex_svg(12 code: &str,13 number: usize,14 level: usize,15 base: usize,16 projection: &str,17 scale: usize,18) -> Result<String, Fault> {19 let code = code_of(code)?;20 let cell = match projection {21 "pro" => six::pro_design(code, number, level, base)?,22 "cut" => six::cut_design(code, number, level, base)?,23 _ => six::iso_design(code, number, level, base)?,24 };25 Ok(six::svg(&cell, scale, None, 0)?)26}2728// SLICE2930fn slice(code: &str, number: usize, level: usize, base: usize) -> Result<Cell6d, Fault> {31 Ok(six::cut(&three::create(32 code_of(code)?,33 number,34 level,35 base,36 )?)?)37}3839/// 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.40#[wasm_bindgen]41pub fn slice_census(code: &str, number: usize, level: usize, base: usize) -> Result<String, Fault> {42 let cell = slice(code, number, level, base)?;43 let tally = six::census(&cell, false);44 let side = number.pow(level as u32);45 Ok(json!({46 "side": side,47 "triangles": tally.triangles,48 "boundary": tally.boundary_edges,49 "edges": tally.edges,50 "interior": tally.interior_edges,51 "vertices": tally.vertices,52 "euler": tally.euler,53 "fills": tally.fills,54 "voids": tally.voids,55 "components": six::components(&cell)?,56 "holes": six::holes(&cell)?,57 "giant": six::giant(&cell)?,58 "closed": {59 "triangles": formulas::solid_slice_triangles(side)?.to_string(),60 "boundary": formulas::solid_slice_boundary(side)?.to_string(),61 "edges": formulas::solid_slice_edges(side)?.to_string(),62 "vertices": formulas::solid_slice_vertices(side)?.to_string(),63 },64 })65 .to_string())66}6768/// Walks the level-one slice of the code at odd side `2k-1`, one row per `k`, as JSON.69#[wasm_bindgen]70pub fn slice_series(code: &str, max_k: usize) -> Result<String, Fault> {71 if !(1..=16).contains(&max_k) {72 return Err(Fault::new("max_k must be between 1 and 16."));73 }74 let code = code_of(code)?;75 let mut rows = Vec::new();76 for k in 1..=max_k {77 let number = 2 * k - 1;78 let cell = six::cut(&three::create(code, number, 1, 2)?)?;79 rows.push(json!({80 "k": k,81 "n": number,82 "fills": six::census(&cell, false).fills,83 "components": six::components(&cell)?,84 "holes": six::holes(&cell)?,85 }));86 }87 Ok(json!(rows).to_string())88}8990/// 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.91#[wasm_bindgen]92pub fn slice_partition(number: usize) -> Result<String, Fault> {93 let carpet = six::census(&slice("23", number, 1, 2)?, false);94 let net = six::census(&slice("232", number, 1, 2)?, false);95 let together = carpet.fills + net.fills;96 Ok(json!({97 "carpet": carpet.fills,98 "net": net.fills,99 "together": together,100 "hexagon": carpet.triangles,101 "exact": together == carpet.triangles,102 })103 .to_string())104}105106// SPECTROMETER107108fn sixteenths(walsh: &[i64]) -> Vec<i64> {109 walsh110 .iter()111 .enumerate()112 .map(|(mask, &value)| if mask == 0 { 8 - value } else { -value })113 .collect()114}115116fn level_sums(parts: &[i64]) -> [i64; 4] {117 let mut sums = [0i64; 4];118 for (mask, &part) in parts.iter().enumerate() {119 sums[mask.count_ones() as usize] += part;120 }121 sums122}123124fn ink_numerator(sums: &[i64; 4], number: i64) -> i64 {125 let sign = if number % 4 == 1 { -1 } else { 1 };126 (6 * sums[0] - 3 * sums[3] * sign) * number * number127 + (4 * sums[1] - 2 * sums[2] * sign) * number128 + 4 * sums[2]129 - (2 * sums[1] + 3 * sums[3]) * sign130}131132/// 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.133#[wasm_bindgen]134pub fn walsh_spectrum(code: &str, max_k: usize) -> Result<String, Fault> {135 if !(1..=16).contains(&max_k) {136 return Err(Fault::new("max_k must be between 1 and 16."));137 }138 let code = checked(code, 3, 2)?;139 let walsh = boolean::walsh_spectrum(code, 3);140 let parts = sixteenths(&walsh);141 let sums = level_sums(&parts);142 let mut weights = [0u32; 4];143 for corner in 0..8usize {144 if (code >> corner) & 1 == 1 {145 weights[corner.count_ones() as usize] += 1;146 }147 }148 let coefficients: Vec<Json> = (0..8usize)149 .map(|mask| {150 json!({151 "mask": mask,152 "level": mask.count_ones(),153 "walsh": walsh[mask],154 "sixteenths": parts[mask],155 "value": parts[mask] as f64 / 16.0,156 })157 })158 .collect();159 let levels: Vec<Json> = (0..4usize)160 .map(|level| {161 json!({162 "level": level,163 "sixteenths": sums[level],164 "eighths": sums[level] / 2,165 "sigma": sums[level] as f64 / 16.0,166 })167 })168 .collect();169 let law: Vec<Json> = (1..=max_k)170 .map(|k| {171 let number = 2 * k as i64 - 1;172 let numerator = ink_numerator(&sums, number);173 json!({174 "k": k,175 "n": number,176 "s": if number % 4 == 1 { -1 } else { 1 },177 "numerator": numerator,178 "denominator": 96 * number * number,179 "ink": numerator as f64 / (96 * number * number) as f64,180 "fills": numerator / 16,181 "triangles": 6 * number * number,182 })183 })184 .collect();185 Ok(json!({186 "code": code.to_string(),187 "corners": code.count_ones(),188 "background": sums[0] as f64 / 16.0,189 "blink": -(sums[3] as f64) / 32.0,190 "spectrum": walsh,191 "coefficients": coefficients,192 "levels": levels,193 "weights": weights.to_vec(),194 "law": law,195 })196 .to_string())197}