use super::{record_csg_op, ClippingProcessor};
use crate::diagnostics::{BoolFailureReason, BoolOp};
use crate::error::Result;
use crate::mesh::Mesh;
enum UnionOutcome {
Kernel(Mesh),
PassThrough(Mesh),
}
impl ClippingProcessor {
pub fn union_mesh(&self, mesh_a: &Mesh, mesh_b: &Mesh) -> Result<Mesh> {
match self.union_pair(mesh_a, mesh_b)? {
UnionOutcome::PassThrough(mesh) => Ok(mesh),
UnionOutcome::Kernel(mesh) => Ok(self.audit_and_gate_union(mesh, || {
let mut merged = mesh_a.clone();
merged.merge(mesh_b);
merged
})),
}
}
fn union_pair(&self, mesh_a: &Mesh, mesh_b: &Mesh) -> Result<UnionOutcome> {
record_csg_op(1, mesh_a.triangle_count(), mesh_b.triangle_count());
if mesh_a.is_empty() {
return Ok(UnionOutcome::PassThrough(mesh_b.clone()));
}
if mesh_b.is_empty() {
return Ok(UnionOutcome::PassThrough(mesh_a.clone()));
}
let raw_u = crate::kernel::mesh_bridge::union(mesh_a, mesh_b);
let result = Self::consolidate_coplanar(raw_u);
if result.is_empty() || !self.validate_mesh(&result) {
self.record_failure(BoolOp::Union, BoolFailureReason::KernelOutputInvalid);
let mut merged = mesh_a.clone();
merged.merge(mesh_b);
return Ok(UnionOutcome::PassThrough(merged));
}
Ok(UnionOutcome::Kernel(result))
}
pub fn union_meshes(&self, meshes: &[Mesh]) -> Result<Mesh> {
if meshes.is_empty() {
return Ok(Mesh::new());
}
if meshes.len() == 1 {
return Ok(meshes[0].clone());
}
let mut result = Mesh::new();
let mut found_first = false;
let mut returned_by_kernel = false;
for mesh in meshes {
if mesh.is_empty() {
continue;
}
if !found_first {
result = mesh.clone();
found_first = true;
continue;
}
match self.union_pair(&result, mesh)? {
UnionOutcome::Kernel(m) => {
result = m;
returned_by_kernel = true;
}
UnionOutcome::PassThrough(m) => {
result = m;
returned_by_kernel = false;
}
}
}
if returned_by_kernel {
result = self.audit_and_gate_union(result, || {
let mut merged = Mesh::new();
for mesh in meshes.iter().filter(|m| !m.is_empty()) {
merged.merge(mesh);
}
merged
});
}
Ok(result)
}
fn audit_and_gate_union(&self, mesh: Mesh, fallback: impl FnOnce() -> Mesh) -> Mesh {
if !self.validate_mesh(&mesh) {
return mesh;
}
if self.accept_gates_reject(BoolOp::Union, &mesh) {
return fallback();
}
self.record_topology_tear(BoolOp::Union, &mesh);
mesh
}
}