use super::mesh_outline::MeshOutlineJs;
use ifc_lite_geometry::{
boolean_2d, resolve_2d, sanitize_contours, BooleanOp2D, ContourSet, Ring2D,
};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct Contours2D {
set: ContourSet,
}
#[wasm_bindgen]
impl Contours2D {
#[wasm_bindgen(constructor)]
pub fn new(coords: &[f64], ring_lengths: &[u32]) -> Result<Contours2D, JsValue> {
if coords.len() % 2 != 0 {
return Err(js_sys::Error::new("Contours2D: coords length must be even").into());
}
let expected = ring_lengths
.iter()
.try_fold(0usize, |acc, n| acc.checked_add(*n as usize))
.and_then(|v| v.checked_mul(2))
.ok_or_else(|| js_sys::Error::new("Contours2D: ringLengths overflow"))?;
if expected != coords.len() {
return Err(js_sys::Error::new(&format!(
"Contours2D: ringLengths sum to {} vertices but coords hold {}",
expected / 2,
coords.len() / 2
))
.into());
}
let mut rings: Vec<Ring2D> = Vec::with_capacity(ring_lengths.len());
let mut at = 0usize;
for n in ring_lengths {
let n = *n as usize;
let ring = coords[at * 2..(at + n) * 2]
.chunks_exact(2)
.map(|p| [p[0], p[1]])
.collect();
rings.push(ring);
at += n;
}
Ok(Contours2D {
set: ContourSet {
rings: sanitize_contours(&rings),
shape_offsets: Vec::new(),
},
})
}
#[wasm_bindgen(js_name = fromMeshOutline)]
pub fn from_mesh_outline(outline: &MeshOutlineJs) -> Contours2D {
Contours2D {
set: ContourSet {
rings: sanitize_contours(&outline.rings_f64()),
shape_offsets: Vec::new(),
},
}
}
#[wasm_bindgen(getter, js_name = ringCount)]
pub fn ring_count(&self) -> usize {
self.set.rings.len()
}
#[wasm_bindgen(getter, js_name = shapeCount)]
pub fn shape_count(&self) -> usize {
self.set.shape_count()
}
#[wasm_bindgen(getter, js_name = isEmpty)]
pub fn is_empty(&self) -> bool {
self.set.is_empty()
}
#[wasm_bindgen(js_name = shapeOffsets)]
pub fn shape_offsets(&self) -> js_sys::Uint32Array {
let v: Vec<u32> = self
.set
.shape_offsets
.iter()
.map(|o| *o as u32)
.collect();
js_sys::Uint32Array::from(&v[..])
}
pub fn ring(&self, index: usize) -> Option<js_sys::Float64Array> {
let ring = self.set.rings.get(index)?;
let mut flat = Vec::with_capacity(ring.len() * 2);
for p in ring {
flat.push(p[0]);
flat.push(p[1]);
}
Some(js_sys::Float64Array::from(&flat[..]))
}
pub fn coords(&self) -> js_sys::Float64Array {
let total: usize = self.set.rings.iter().map(|r| r.len() * 2).sum();
let mut flat = Vec::with_capacity(total);
for ring in &self.set.rings {
for p in ring {
flat.push(p[0]);
flat.push(p[1]);
}
}
js_sys::Float64Array::from(&flat[..])
}
#[wasm_bindgen(js_name = ringLengths)]
pub fn ring_lengths(&self) -> js_sys::Uint32Array {
let v: Vec<u32> = self.set.rings.iter().map(|r| r.len() as u32).collect();
js_sys::Uint32Array::from(&v[..])
}
pub fn bounds(&self) -> Option<js_sys::Float64Array> {
self.set
.bounds()
.map(|b| js_sys::Float64Array::from(&b[..]))
}
}
#[wasm_bindgen(js_name = union2d)]
pub fn union_2d(a: &Contours2D, b: &Contours2D) -> Contours2D {
Contours2D {
set: boolean_2d(&a.set.rings, &b.set.rings, BooleanOp2D::Union),
}
}
#[wasm_bindgen(js_name = difference2d)]
pub fn difference_2d(a: &Contours2D, b: &Contours2D) -> Contours2D {
Contours2D {
set: boolean_2d(&a.set.rings, &b.set.rings, BooleanOp2D::Difference),
}
}
#[wasm_bindgen(js_name = intersection2d)]
pub fn intersection_2d(a: &Contours2D, b: &Contours2D) -> Contours2D {
Contours2D {
set: boolean_2d(&a.set.rings, &b.set.rings, BooleanOp2D::Intersection),
}
}
#[wasm_bindgen(js_name = resolve2d)]
pub fn resolve_2d_js(a: &Contours2D) -> Contours2D {
Contours2D {
set: resolve_2d(&a.set.rings),
}
}