use axiolid_contracts::Sign;
use axiolid_core::Point3;
use crate::orient3d;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SegmentTriangleRelation {
Disjoint,
Proper,
Touching,
Coplanar,
DegenerateSegment,
DegenerateTriangle,
}
#[must_use]
pub fn segment_triangle_relation(
start: Point3,
end: Point3,
triangle: [Point3; 3],
) -> SegmentTriangleRelation {
if start == end {
return SegmentTriangleRelation::DegenerateSegment;
}
let [a, b, c] = triangle;
if (b - a).cross(c - a).length_squared() == 0.0 {
return SegmentTriangleRelation::DegenerateTriangle;
}
let start_side = sign(orient3d(a, b, c, start));
let end_side = sign(orient3d(a, b, c, end));
if start_side == Sign::Zero && end_side == Sign::Zero {
return SegmentTriangleRelation::Coplanar;
}
if start_side != Sign::Zero && start_side == end_side {
return SegmentTriangleRelation::Disjoint;
}
let edge_signs = [
sign(orient3d(start, end, a, b)),
sign(orient3d(start, end, b, c)),
sign(orient3d(start, end, c, a)),
];
let all_nonnegative = edge_signs.iter().all(|&value| value != Sign::Negative);
let all_nonpositive = edge_signs.iter().all(|&value| value != Sign::Positive);
if !all_nonnegative && !all_nonpositive {
return SegmentTriangleRelation::Disjoint;
}
if start_side == Sign::Zero || end_side == Sign::Zero || edge_signs.contains(&Sign::Zero) {
SegmentTriangleRelation::Touching
} else {
SegmentTriangleRelation::Proper
}
}
fn sign(value: axiolid_contracts::Certified) -> Sign {
value
.sign()
.expect("orient3d is a total certified predicate")
}