use axiolid_mesh::{AttributeFate, DropReason, TriMesh};
#[derive(Debug, Clone, PartialEq, Default)]
#[non_exhaustive]
pub struct BooleanEvidence {
pub subject_triangles: usize,
pub tool_triangles: usize,
pub output_triangles: usize,
pub output_components: usize,
pub disjoint_tools: usize,
pub sub_operations: usize,
pub attribute_fates: Vec<(String, AttributeFate)>,
pub coincident_faces_encountered: bool,
pub analytic_path: bool,
pub relative_overlap: Option<f64>,
}
impl BooleanEvidence {
pub fn record(
subject_triangles: usize,
tool_triangles: usize,
output_triangles: usize,
output_components: usize,
) -> Self {
Self {
subject_triangles,
tool_triangles,
output_triangles,
output_components,
disjoint_tools: 0,
sub_operations: 1,
coincident_faces_encountered: false,
analytic_path: false,
relative_overlap: None,
attribute_fates: Vec::new(),
}
}
#[must_use]
pub fn with_attribute_fates(mut self, fates: Vec<(String, AttributeFate)>) -> Self {
self.attribute_fates = fates;
self
}
pub const fn with_relative_overlap(mut self, overlap: f64) -> Self {
self.relative_overlap = Some(overlap);
self
}
pub const fn with_analytic_path(mut self, analytic: bool) -> Self {
self.analytic_path = analytic;
self
}
pub const fn with_disjoint_tools(mut self, count: usize) -> Self {
self.disjoint_tools = count;
self
}
pub const fn with_sub_operations(mut self, count: usize) -> Self {
self.sub_operations = count;
self
}
pub const fn with_coincident_faces(mut self, encountered: bool) -> Self {
self.coincident_faces_encountered = encountered;
self
}
pub fn absorb(&mut self, other: Self) {
self.output_triangles = other.output_triangles;
self.output_components = other.output_components;
self.disjoint_tools += other.disjoint_tools;
self.sub_operations += other.sub_operations;
self.coincident_faces_encountered |= other.coincident_faces_encountered;
self.analytic_path |= other.analytic_path;
self.attribute_fates = merge_fates(&self.attribute_fates, other.attribute_fates);
self.relative_overlap = match (self.relative_overlap, other.relative_overlap) {
(Some(a), Some(b)) => Some(a.min(b)),
(Some(a), None) => Some(a),
(None, b) => b,
};
}
}
pub fn merge_fates(
earlier: &[(String, AttributeFate)],
later: Vec<(String, AttributeFate)>,
) -> Vec<(String, AttributeFate)> {
if earlier.is_empty() {
return later;
}
earlier
.iter()
.map(|(name, fate)| {
let next = later
.iter()
.find(|(n, _)| n == name)
.map(|(_, f)| f.clone())
.unwrap_or(AttributeFate::Dropped(DropReason::ProviderLimitation));
(name.clone(), fate.clone().then(next))
})
.collect()
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct BooleanOutcome {
pub mesh: TriMesh,
pub evidence: BooleanEvidence,
}
impl BooleanOutcome {
pub const fn new(mesh: TriMesh, evidence: BooleanEvidence) -> Self {
Self { mesh, evidence }
}
pub fn is_empty(&self) -> bool {
self.mesh.indices.is_empty()
}
}