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}