pub mod arrangement;
pub mod assemble;
pub mod cdt;
pub mod cells;
pub mod cells_extract;
pub mod exact;
mod graph_geom;
mod graph_self_cut;
mod graph_types;
pub mod intersection_graph;
pub mod pairing;
pub mod ray_shoot;
pub mod repair;
pub mod soup;
pub mod tri_tri;
use crate::cancel::CancelToken;
use crate::impl_mesh::ManifoldImpl;
use crate::linalg::Vec3;
use crate::types::{Error, OpType, WindingRule};
fn is_cancelled(token: Option<&CancelToken>) -> bool {
token.is_some_and(|t| t.is_cancelled())
}
fn cancelled_impl() -> ManifoldImpl {
let mut out = ManifoldImpl::new();
out.make_empty(Error::Cancelled);
out
}
fn needs_no_classification(
imp: &ManifoldImpl,
tris: &[[Vec3; 3]],
token: Option<&CancelToken>,
) -> bool {
!soup::has_self_intersections_with_token(imp, token) && repair::shells_well_nested(tris)
}
pub fn boolean(
a: &ManifoldImpl,
b: &ManifoldImpl,
op: OpType,
token: Option<&CancelToken>,
) -> ManifoldImpl {
boolean_with_progress(a, b, op, token, None)
}
pub fn boolean_with_progress(
a: &ManifoldImpl,
b: &ManifoldImpl,
op: OpType,
token: Option<&CancelToken>,
progress: Option<&crate::progress::ProgressReporter>,
) -> ManifoldImpl {
boolean_with_rule(a, b, op, WindingRule::Positive, token, progress)
}
pub fn boolean_with_rule(
a: &ManifoldImpl,
b: &ManifoldImpl,
op: OpType,
rule: WindingRule,
token: Option<&CancelToken>,
progress: Option<&crate::progress::ProgressReporter>,
) -> ManifoldImpl {
use crate::progress::{begin_phase, Phase};
if is_cancelled(token) {
return cancelled_impl();
}
if a.is_empty() {
return match op {
OpType::Add => b.clone(),
OpType::Intersect | OpType::Subtract => ManifoldImpl::new(),
};
}
if b.is_empty() {
return match op {
OpType::Add | OpType::Subtract => a.clone(),
OpType::Intersect => ManifoldImpl::new(),
};
}
let p_tris = soup::impl_to_tris(a);
let q_tris = soup::impl_to_tris(b);
let p_props = soup::impl_to_corner_props(a);
let q_props = soup::impl_to_corner_props(b);
if !a.bbox.does_overlap_box(&b.bbox) {
let a_clean = || needs_no_classification(a, &p_tris, token);
let b_clean = || needs_no_classification(b, &q_tris, token);
match op {
OpType::Add if a_clean() && b_clean() => {
let mut tris = p_tris.clone();
tris.extend(q_tris.iter().cloned());
let mut interner = intersection_graph::VertInterner::default();
let pieces: Vec<intersection_graph::Piece> = tris
.iter()
.enumerate()
.map(|(i, t)| intersection_graph::Piece {
mesh: if i < p_tris.len() { 0 } else { 1 },
tri: if i < p_tris.len() { i } else { i - p_tris.len() },
vi: [
interner.intern_f64(t[0]),
interner.intern_f64(t[1]),
interner.intern_f64(t[2]),
],
})
.collect();
let ctx = assemble::PropCtx {
num_prop: [a.num_prop, b.num_prop],
tris: [&p_tris, &q_tris],
props: [&p_props, &q_props],
};
let props = (ctx.out_num_prop() > 0).then_some(&ctx);
return assemble::assemble(
&pieces,
&interner.verts,
&interner.verts_f64,
|_| true,
props,
)
.into_impl();
}
OpType::Intersect => return ManifoldImpl::new(),
OpType::Subtract if a_clean() => return a.clone(),
OpType::Add | OpType::Subtract => {}
}
}
let Some(graph) =
intersection_graph::build_graph_with_progress(&p_tris, &q_tris, token, progress)
else {
return cancelled_impl();
};
let t_cells = crate::timing::start();
let Some(complex) = cells::build_cells_with_progress(&graph, token, progress) else {
return cancelled_impl();
};
crate::timing::print("robust: cell complex", t_cells);
begin_phase(progress, Phase::Winding, 0);
let t_winding = crate::timing::start();
let wind = cells::windings(&graph, &complex, [&p_tris, &q_tris]);
crate::timing::print("robust: winding propagation", t_winding);
if is_cancelled(token) {
return cancelled_impl();
}
begin_phase(progress, Phase::Assemble, 0);
let pieces = cells::extract(&graph, &complex, &wind, op, rule);
let ctx = assemble::PropCtx {
num_prop: [a.num_prop, b.num_prop],
tris: [&p_tris, &q_tris],
props: [&p_props, &q_props],
};
let props = (ctx.out_num_prop() > 0).then_some(&ctx);
let t_asm = crate::timing::start();
let out = assemble::assemble(&pieces, &graph.verts, &graph.verts_f64, |_| true, props);
crate::timing::print("robust: assemble+import", t_asm);
out.into_impl()
}
pub(crate) fn assemble_all(tris: &[[Vec3; 3]]) -> ManifoldImpl {
let mut interner = intersection_graph::VertInterner::default();
let pieces: Vec<intersection_graph::Piece> = tris
.iter()
.enumerate()
.map(|(i, t)| intersection_graph::Piece {
mesh: 0,
tri: i,
vi: [
interner.intern_f64(t[0]),
interner.intern_f64(t[1]),
interner.intern_f64(t[2]),
],
})
.collect();
assemble::assemble(&pieces, &interner.verts, &interner.verts_f64, |_| true, None).into_impl()
}
#[cfg(test)]
#[path = "engine_tests.rs"]
mod engine_tests;
#[cfg(test)]
#[path = "cross_validation_tests.rs"]
mod cross_validation_tests;
#[cfg(test)]
#[path = "nonmanifold_tests.rs"]
mod nonmanifold_tests;
#[cfg(test)]
#[path = "property_tests.rs"]
mod property_tests;
#[cfg(test)]
#[path = "thingi_tests.rs"]
mod thingi_tests;