use ifc_lite_geometry::zone_split::{split_mesh_by_zones, ZoneBox, ZoneShape};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct ZonePieceJs {
zone: i32,
positions: Vec<f64>,
indices: Vec<u32>,
volume: f64,
}
#[wasm_bindgen]
impl ZonePieceJs {
#[wasm_bindgen(getter, js_name = zoneIndex)]
pub fn zone_index(&self) -> i32 {
self.zone
}
#[wasm_bindgen(getter)]
pub fn positions(&self) -> js_sys::Float64Array {
js_sys::Float64Array::from(&self.positions[..])
}
#[wasm_bindgen(getter)]
pub fn indices(&self) -> js_sys::Uint32Array {
js_sys::Uint32Array::from(&self.indices[..])
}
#[wasm_bindgen(getter)]
pub fn volume(&self) -> f64 {
self.volume
}
}
#[wasm_bindgen]
pub struct ZoneSplitJs {
pieces: Vec<ZonePieceJs>,
whole_volume: f64,
sum_error_rel: f64,
remainder_failed: bool,
}
#[wasm_bindgen]
impl ZoneSplitJs {
#[wasm_bindgen(getter, js_name = pieceCount)]
pub fn piece_count(&self) -> usize {
self.pieces.len()
}
pub fn piece(&self, index: usize) -> Option<ZonePieceJs> {
self.pieces.get(index).map(|p| ZonePieceJs {
zone: p.zone,
positions: p.positions.clone(),
indices: p.indices.clone(),
volume: p.volume,
})
}
#[wasm_bindgen(getter, js_name = wholeVolume)]
pub fn whole_volume(&self) -> f64 {
self.whole_volume
}
#[wasm_bindgen(getter, js_name = sumErrorRel)]
pub fn sum_error_rel(&self) -> f64 {
self.sum_error_rel
}
#[wasm_bindgen(getter, js_name = remainderFailed)]
pub fn remainder_failed(&self) -> bool {
self.remainder_failed
}
}
#[wasm_bindgen(js_name = splitMeshByZones)]
pub fn split_mesh_by_zones_js(
positions: &[f64],
indices: &[u32],
zones: &[f64],
footprints: Option<Vec<f64>>,
footprint_counts: Option<Vec<u32>>,
) -> ZoneSplitJs {
let tris: Vec<[[f64; 3]; 3]> = indices
.chunks_exact(3)
.filter_map(|c| {
let vertex = |i: u32| -> Option<[f64; 3]> {
let b = (i as usize) * 3;
let p = [
*positions.get(b)?,
*positions.get(b + 1)?,
*positions.get(b + 2)?,
];
p.iter().all(|v| v.is_finite()).then_some(p)
};
Some([vertex(c[0])?, vertex(c[1])?, vertex(c[2])?])
})
.collect();
let footprints = footprints.unwrap_or_default();
let counts = footprint_counts.unwrap_or_default();
let mut cursor = 0usize;
let shapes: Vec<ZoneShape> = zones
.chunks_exact(7)
.enumerate()
.map(|(i, z)| {
let points = counts.get(i).copied().unwrap_or(0) as usize;
let span = points.checked_mul(2);
let start = cursor.checked_mul(2);
let slice = match (start, span) {
(Some(s), Some(n)) => s.checked_add(n).and_then(|e| footprints.get(s..e)),
_ => None,
};
cursor = cursor.saturating_add(points);
if let (true, Some(slice)) = (points >= 3, slice) {
ZoneShape::Prism {
footprint: slice.chunks_exact(2).map(|p| [p[0], p[1]]).collect(),
min_y: z[1] - z[4] / 2.0,
max_y: z[1] + z[4] / 2.0,
}
} else {
ZoneShape::Box(ZoneBox {
center: [z[0], z[1], z[2]],
size: [z[3], z[4], z[5]],
rotation_y: z[6],
})
}
})
.collect();
let split = split_mesh_by_zones(&tris, &shapes);
let sum_error_rel = split.sum_error_rel();
let remainder_failed = split.remainder_failed;
let pieces = split
.pieces
.into_iter()
.map(|p| {
let mut flat = Vec::with_capacity(p.tris.len() * 9);
let mut idx = Vec::with_capacity(p.tris.len() * 3);
for t in &p.tris {
let base = (flat.len() / 3) as u32;
for v in t {
flat.extend_from_slice(v);
}
idx.extend_from_slice(&[base, base + 1, base + 2]);
}
ZonePieceJs {
zone: p.zone.map_or(-1, |z| z as i32),
positions: flat,
indices: idx,
volume: p.volume,
}
})
.collect();
ZoneSplitJs {
pieces,
whole_volume: split.whole_volume,
sum_error_rel,
remainder_failed,
}
}