use ifc_lite_geometry::{intersection_solid, DegenerateReason, IntersectionSolid, Mesh};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct ClashIntersectionSolidJs {
positions: Vec<f64>,
indices: Vec<u32>,
volume_m3: f64,
is_solid: bool,
reason: &'static str,
thickness_m: f64,
required_m: f64,
}
#[wasm_bindgen]
impl ClashIntersectionSolidJs {
#[wasm_bindgen(getter, js_name = isSolid)]
pub fn is_solid(&self) -> bool {
self.is_solid
}
#[wasm_bindgen(getter, js_name = degenerateReason)]
pub fn degenerate_reason(&self) -> String {
self.reason.to_string()
}
#[wasm_bindgen(getter, js_name = volumeM3)]
pub fn volume_m3(&self) -> f64 {
self.volume_m3
}
#[wasm_bindgen(getter)]
pub fn positions(&self) -> Vec<f64> {
self.positions.clone()
}
#[wasm_bindgen(getter)]
pub fn indices(&self) -> Vec<u32> {
self.indices.clone()
}
#[wasm_bindgen(getter, js_name = triangleCount)]
pub fn triangle_count(&self) -> u32 {
(self.indices.len() / 3) as u32
}
#[wasm_bindgen(getter, js_name = thicknessM)]
pub fn thickness_m(&self) -> f64 {
self.thickness_m
}
#[wasm_bindgen(getter, js_name = requiredM)]
pub fn required_m(&self) -> f64 {
self.required_m
}
}
fn mesh_from(positions: &[f32], indices: &[u32]) -> Option<Mesh> {
if !positions.len().is_multiple_of(3) || !indices.len().is_multiple_of(3) {
return None;
}
if positions.iter().any(|p| !p.is_finite()) {
return None;
}
let vertex_count = (positions.len() / 3) as u32;
if indices.iter().any(|&i| i >= vertex_count) {
return None;
}
let mut m = Mesh::new();
m.positions.extend_from_slice(positions);
m.indices.extend_from_slice(indices);
Some(m)
}
fn malformed_operand_result() -> ClashIntersectionSolidJs {
ClashIntersectionSolidJs {
positions: Vec::new(),
indices: Vec::new(),
volume_m3: 0.0,
is_solid: false,
reason: "malformed-operand",
thickness_m: 0.0,
required_m: 0.0,
}
}
#[wasm_bindgen(js_name = clashIntersectionSolid)]
pub fn clash_intersection_solid(
positions_a: &[f32],
indices_a: &[u32],
positions_b: &[f32],
indices_b: &[u32],
) -> ClashIntersectionSolidJs {
let (Some(a), Some(b)) = (mesh_from(positions_a, indices_a), mesh_from(positions_b, indices_b)) else {
return malformed_operand_result();
};
match intersection_solid(&a, &b) {
IntersectionSolid::Solid {
positions,
indices,
volume_m3,
} => ClashIntersectionSolidJs {
positions,
indices,
volume_m3,
is_solid: true,
reason: "",
thickness_m: 0.0,
required_m: 0.0,
},
IntersectionSolid::Degenerate(reason) => {
let (name, thickness_m, required_m) = match reason {
DegenerateReason::EmptyOperand => ("empty-operand", 0.0, 0.0),
DegenerateReason::NoOverlap => ("no-overlap", 0.0, 0.0),
DegenerateReason::BudgetExhausted => ("budget-exhausted", 0.0, 0.0),
DegenerateReason::BelowKernelResolution {
thickness_m,
required_m,
} => ("below-kernel-resolution", thickness_m, required_m),
};
ClashIntersectionSolidJs {
positions: Vec::new(),
indices: Vec::new(),
volume_m3: 0.0,
is_solid: false,
reason: name,
thickness_m,
required_m,
}
}
}
}
#[cfg(test)]
#[path = "clash_solid_tests.rs"]
mod tests;