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}