diff --git a/cetz-core/src/lib.rs b/cetz-core/src/lib.rs index 7c4f26c5..5e1535ce 100644 --- a/cetz-core/src/lib.rs +++ b/cetz-core/src/lib.rs @@ -1,6 +1,7 @@ use ciborium::de::from_reader; use ciborium::ser::into_writer; use serde::Deserialize; +use serde::Serialize; use wasm_minimal_protocol::*; initiate_protocol!(); @@ -103,3 +104,53 @@ pub fn cubic_extrema_func(input: &[u8]) -> Vec { } } } + +#[derive(Serialize, Deserialize)] +struct Bounds { + low: Point, + high: Point, +} + +/// Compute the axis-aligned bounding box (aabb). +fn aabb(init: Option, pts: Vec) -> Result { + let mut bounds = match init { + Some(init) => init, + None => Bounds { + low: pts.first().unwrap().clone(), + high: pts.first().unwrap().clone(), + }, + }; + for pt in pts { + if pt.len() != 3 { + return Err("Point must have 3 dimensions".to_string()); + } + for dim in 0..pt.len() { + if pt[dim] < bounds.low[dim] { + bounds.low[dim] = pt[dim]; + } + if bounds.high[dim] < pt[dim] { + bounds.high[dim] = pt[dim]; + } + } + } + Ok(bounds) +} + +#[derive(Deserialize)] +struct AabbArgs { + pts: Vec, + init: Option, +} + +#[wasm_func] +pub fn aabb_func(input: &[u8]) -> Result, String> { + match from_reader::(input) { + Ok(input) => { + let mut buf = Vec::new(); + let bounds = aabb(input.init, input.pts)?; + into_writer(&bounds, &mut buf).unwrap(); + Ok(buf) + } + Err(e) => Err(e.to_string()), + } +} diff --git a/src/aabb.typ b/src/aabb.typ index 42a972f9..4b2b8edb 100644 --- a/src/aabb.typ +++ b/src/aabb.typ @@ -1,4 +1,5 @@ #import "vector.typ" +#let cetz-core = plugin("../cetz-core/cetz_core.wasm") /// Compute an axis aligned bounding box (aabb) for a list of vectors. /// @@ -6,33 +7,10 @@ /// - init (aabb): Initial aabb /// -> aabb #let aabb(pts, init: none) = { - if type(pts) == array { - let bounds = if init == none and 0 < pts.len() { - let pt = pts.at(0) - (low: pt, high: pt) - } else { - init - } - for pt in pts { - assert(type(pt) == array and pt.len() == 3, message: repr(init) + repr(pts)) - let (x, y, z) = pt - - let (lo-x, lo-y, lo-z) = bounds.low - bounds.low = (calc.min(lo-x, x), calc.min(lo-y, y), calc.min(lo-z, z)) - - let (hi-x, hi-y, hi-z) = bounds.high - bounds.high = (calc.max(hi-x, x), calc.max(hi-y, y), calc.max(hi-z, z)) - } - return bounds - } else if type(pts) == dictionary { - if init == none { - return pts - } else { - return aabb((pts.low, pts.high,), init: init) - } - } - - panic("Expected array of vectors or bbox dictionary, got: " + repr(pts)) + let args = (pts: pts, init: init) + let encoded = cbor.encode(args) + let bounds = cbor(cetz-core.aabb_func(encoded)) + return bounds } /// Get the mid-point of an AABB as vector. diff --git a/src/process.typ b/src/process.typ index 47ffb416..f27b4da9 100644 --- a/src/process.typ +++ b/src/process.typ @@ -26,7 +26,7 @@ path-util.bounds(drawable.segments) } else if drawable.type == "content" { let (x, y, _, w, h,) = drawable.pos + (drawable.width, drawable.height) - ((x + w / 2, y - h / 2, 0), (x - w / 2, y + h / 2, 0)) + ((x + w / 2, y - h / 2, 0.0), (x - w / 2, y + h / 2, 0.0)) }, init: bounds ) @@ -81,7 +81,8 @@ let r = element(ctx, el) if r != none { if r.bounds != none { - bounds = aabb.aabb(r.bounds, init: bounds) + let pts = (r.bounds.low, r.bounds.high,) + bounds = aabb.aabb(pts, init: bounds) } ctx = r.ctx drawables += r.drawables