apollonian.rs
4.5 kB · rust · 123 lines
1use crate::Fault;2use mrlycore::{json, Json};3use mrlynum::apollonian as gasket;4use mrlynum::apollonian::{Circle, Packing};5use wasm_bindgen::prelude::*;67const STRIDE: usize = 5;89fn drawn(c: Circle) -> [f64; STRIDE] {10 let (x, y) = c.centre().unwrap_or((0.0, 0.0));11 let den = if gasket::is_ford(c) {12 ((c.k / 2) as f64).sqrt().round()13 } else {14 0.015 };16 [x, y, c.radius().unwrap_or(0.0), c.k as f64, den]17}1819fn sheet(circles: impl Iterator<Item = Circle>) -> Vec<f64> {20 let mut out = Vec::new();21 for c in circles.filter(|c| !c.is_line()) {22 out.extend_from_slice(&drawn(c));23 }24 out25}2627fn line_tangent(p: &Packing) -> (usize, usize) {28 let on = p.circles.iter().filter(|c| gasket::on_line(**c)).count();29 let ford = p.circles.iter().filter(|c| gasket::is_ford(**c)).count();30 (on, ford)31}3233/// Grows the named packing to the curvature cap and returns the circles it made, the root quadruple excluded, five doubles each: the centre, the radius, the curvature, and the denominator of the fraction the circle rests on when it is a Ford circle, zero when it is not. Lines carry no centre and are left out.34#[wasm_bindgen]35pub fn apollonian(root: &str, cap: u32) -> Result<Vec<f64>, Fault> {36 let p = gasket::grow(root, i64::from(cap))?;37 Ok(sheet(p.circles.into_iter()))38}3940/// The finite circles of the named root quadruple, in the encoding of `apollonian`.41#[wasm_bindgen]42pub fn apollonian_root(root: &str) -> Result<Vec<f64>, Fault> {43 Ok(sheet(gasket::root(root)?.into_iter()))44}4546/// The tangency points the packing rests on the line `y = 0`, ascending, four doubles each: the point, the numerator and the denominator of the reduced fraction it is, and the curvature of the circle resting there. Empty off the strip.47#[wasm_bindgen]48pub fn apollonian_touches(root: &str, cap: u32) -> Result<Vec<f64>, Fault> {49 let p = gasket::grow(root, i64::from(cap))?;50 let mut out = Vec::new();51 for t in gasket::touches(&p) {52 out.extend_from_slice(&[53 t.num as f64 / t.den as f64,54 t.num as f64,55 t.den as f64,56 t.k as f64,57 ]);58 }59 Ok(out)60}6162/// Reads the packing: its root's curvatures, whether it is the strip, the census `N(T)` with the root excluded, the quadruples grown and how many failed one of the six invariants, the circles centred outside the period, the line-tangent circles and how many of them are Ford circles, the box the picture sits in, the local census exponent read over the two octaves below the cap, and the Farey stack of the depth read against the tangency points, as JSON.63#[wasm_bindgen]64pub fn apollonian_read(root: &str, cap: u32, order: usize) -> Result<String, Fault> {65 let cap = i64::from(cap);66 let p = gasket::grow(root, cap)?;67 let shadow = gasket::shadow(&p, order)?;68 let (on, ford) = line_tangent(&p);69 let roots: Vec<Json> = p.root.iter().map(|c| json!(c.k)).collect();70 let finite = p.root.iter().filter(|c| !c.is_line()).count();71 let back = cap / 4;72 let exponent = if back >= 2 {73 let old = gasket::grow(root, back)?.circles.len();74 if old > 0 && !p.circles.is_empty() {75 json!((p.circles.len() as f64 / old as f64).ln() / 4f64.ln())76 } else {77 Json::Null78 }79 } else {80 Json::Null81 };82 Ok(json!({83 "root": root,84 "curvatures": roots,85 "strip": p.strip,86 "cap": cap,87 "circles": p.circles.len(),88 "drawn": p.circles.len() + finite,89 "quads": p.quads,90 "broken": p.broken,91 "strayed": p.strayed,92 "line": on,93 "ford": ford,94 "frame": gasket::frame(&p).to_vec(),95 "exponent": exponent,96 "from": back,97 "shadow": {98 "order": shadow.order,99 "reach": shadow.reach,100 "covered": shadow.covered,101 "nodes": shadow.nodes,102 "touched": shadow.touched,103 "missed": shadow.missed,104 "offford": shadow.offford,105 "bright": shadow.bright as u64,106 "want": shadow.want as u64,107 },108 })109 .to_string())110}111112/// The caps: the largest curvature a packing is grown to, the most circles one growth makes, the deepest the stack is read, and the root quadruples on offer, as JSON.113#[wasm_bindgen]114pub fn apollonian_caps() -> String {115 let roots: Vec<Json> = gasket::ROOTS.iter().map(|&name| json!(name)).collect();116 json!({117 "curvature": gasket::CURVATURE_CAP,118 "circles": gasket::CIRCLE_CAP,119 "order": gasket::ORDER_CAP,120 "roots": roots,121 })122 .to_string()123}