use super::inline_or_heap_list::InlineOrHeapList;
use super::{
Completeness, CurveCurveHit, CurveCurveHit2D, CurveCurveRegion, CurveSurfaceHit,
CurveSurfaceRegion,
};
use crate::{Curve, Point, Tolerance, Vector};
pub(crate) type CurveCurveHitList = InlineOrHeapList<CurveCurveHit, 4>;
pub(crate) type CurveCurveHitList2D = InlineOrHeapList<CurveCurveHit2D, 2>;
pub(crate) type CurveCurveOverlapList = Vec<CurveCurveOverlap>;
pub(crate) type CurveSurfaceHitList = InlineOrHeapList<CurveSurfaceHit, 4>;
pub(crate) type CurveSurfaceOverlapList = Vec<(f64, f64)>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IntersectionMethod {
Exact,
Numeric,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CurveCurveOverlap {
pub first_range: (f64, f64),
pub second_range: (f64, f64),
pub same_direction: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct IntersectionCurve {
pub curve: Curve,
pub tangent: bool,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SurfaceSurfaceRegion {
pub first_u_range: (f64, f64),
pub first_v_range: (f64, f64),
pub second_u_range: (f64, f64),
pub second_v_range: (f64, f64),
pub min_corner: Point,
pub max_corner: Point,
}
#[inline]
pub(crate) fn sort_by_known_parameter<T>(items: &mut [T], parameter: impl Fn(&T) -> Option<f64>) {
if items.iter().all(|item| parameter(item).is_some()) {
items.sort_by(|a, b| {
parameter(a)
.zip(parameter(b))
.map_or(std::cmp::Ordering::Equal, |(x, y)| x.total_cmp(&y))
});
}
}
#[inline]
fn sorted_by_first(mut hits: CurveCurveHitList) -> CurveCurveHitList {
sort_by_known_parameter(&mut hits, |hit| hit.first_parameter);
hits
}
pub(crate) fn curve_surface_multiplicity(
tangent: Vector,
normal: Vector,
point: Point,
tol: Tolerance,
) -> u32 {
let scale = tangent.magnitude() * normal.magnitude();
if scale == 0.0 || !scale.is_finite() {
return 1;
}
let extent = point.distance(Point::ORIGIN) + 1.0;
if (tangent.dot(normal) / scale).abs() <= tol.angular_for_extent(extent) {
2
} else {
1
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CurveCurveIntersection {
hits: CurveCurveHitList,
overlaps: CurveCurveOverlapList,
method: IntersectionMethod,
completeness: Completeness<CurveCurveRegion>,
}
impl CurveCurveIntersection {
pub(crate) fn exact(hits: CurveCurveHitList, overlaps: CurveCurveOverlapList) -> Self {
CurveCurveIntersection {
hits: sorted_by_first(hits),
overlaps,
method: IntersectionMethod::Exact,
completeness: Completeness::Complete,
}
}
pub(crate) fn numeric(
hits: Vec<CurveCurveHit>,
overlaps: CurveCurveOverlapList,
completeness: Completeness<CurveCurveRegion>,
) -> Self {
CurveCurveIntersection {
hits: sorted_by_first(hits.into()),
overlaps,
method: IntersectionMethod::Numeric,
completeness,
}
}
pub fn hits(&self) -> &[CurveCurveHit] {
&self.hits
}
pub fn overlaps(&self) -> &[CurveCurveOverlap] {
&self.overlaps
}
pub fn method(&self) -> IntersectionMethod {
self.method
}
pub fn completeness(&self) -> &Completeness<CurveCurveRegion> {
&self.completeness
}
pub fn is_complete(&self) -> bool {
self.completeness.is_complete()
}
pub fn is_empty(&self) -> bool {
self.hits.is_empty() && self.overlaps.is_empty()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CurveCurveIntersection2D {
hits: CurveCurveHitList2D,
overlaps: CurveCurveOverlapList,
completeness: Completeness<CurveCurveRegion>,
}
impl CurveCurveIntersection2D {
#[inline]
pub(crate) fn exact(
hits: impl IntoIterator<Item = CurveCurveHit2D>,
overlaps: CurveCurveOverlapList,
) -> Self {
let mut result = CurveCurveIntersection2D {
hits: CurveCurveHitList2D::new(),
overlaps,
completeness: Completeness::Complete,
};
for hit in hits {
result.hits.push(hit);
}
sort_by_known_parameter(&mut result.hits, |hit| hit.first_parameter);
result
}
pub fn hits(&self) -> &[CurveCurveHit2D] {
&self.hits
}
pub fn overlaps(&self) -> &[CurveCurveOverlap] {
&self.overlaps
}
pub fn method(&self) -> IntersectionMethod {
IntersectionMethod::Exact
}
pub fn completeness(&self) -> &Completeness<CurveCurveRegion> {
&self.completeness
}
pub fn is_complete(&self) -> bool {
self.completeness.is_complete()
}
pub fn is_empty(&self) -> bool {
self.hits.is_empty() && self.overlaps.is_empty()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CurveSurfaceIntersection {
hits: CurveSurfaceHitList,
overlaps: CurveSurfaceOverlapList,
method: IntersectionMethod,
completeness: Completeness<CurveSurfaceRegion>,
}
impl CurveSurfaceIntersection {
#[inline]
pub(crate) fn exact(
hits: impl IntoIterator<Item = CurveSurfaceHit>,
overlaps: CurveSurfaceOverlapList,
) -> Self {
let mut result = CurveSurfaceIntersection {
hits: CurveSurfaceHitList::new(),
overlaps,
method: IntersectionMethod::Exact,
completeness: Completeness::Complete,
};
for hit in hits {
result.hits.push(hit);
}
sort_by_known_parameter(&mut result.hits, |hit| hit.curve_parameter);
result
}
pub(crate) fn numeric(
hits: Vec<CurveSurfaceHit>,
overlaps: CurveSurfaceOverlapList,
completeness: Completeness<CurveSurfaceRegion>,
) -> Self {
CurveSurfaceIntersection {
hits: sorted_by_curve_parameter(hits.into()),
overlaps,
method: IntersectionMethod::Numeric,
completeness,
}
}
pub fn hits(&self) -> &[CurveSurfaceHit] {
&self.hits
}
pub fn overlaps(&self) -> &[(f64, f64)] {
&self.overlaps
}
pub fn method(&self) -> IntersectionMethod {
self.method
}
pub fn completeness(&self) -> &Completeness<CurveSurfaceRegion> {
&self.completeness
}
pub fn is_complete(&self) -> bool {
self.completeness.is_complete()
}
pub fn is_empty(&self) -> bool {
self.hits.is_empty() && self.overlaps.is_empty()
}
}
#[inline]
fn sorted_by_curve_parameter(mut hits: CurveSurfaceHitList) -> CurveSurfaceHitList {
sort_by_known_parameter(&mut hits, |hit| hit.curve_parameter);
hits
}
#[derive(Debug, Clone, PartialEq)]
pub struct SurfaceSurfaceIntersection {
curves: Vec<IntersectionCurve>,
contact_points: Vec<Point>,
coincident: bool,
method: IntersectionMethod,
completeness: Completeness<SurfaceSurfaceRegion>,
}
impl SurfaceSurfaceIntersection {
pub(crate) fn exact(
curves: Vec<IntersectionCurve>,
contact_points: Vec<Point>,
coincident: bool,
) -> Self {
SurfaceSurfaceIntersection {
curves,
contact_points,
coincident,
method: IntersectionMethod::Exact,
completeness: Completeness::Complete,
}
}
pub(crate) fn numeric(
curves: Vec<IntersectionCurve>,
contact_points: Vec<Point>,
completeness: Completeness<SurfaceSurfaceRegion>,
) -> Self {
SurfaceSurfaceIntersection {
curves,
contact_points,
coincident: false,
method: IntersectionMethod::Numeric,
completeness,
}
}
pub(crate) fn numeric_coincident(completeness: Completeness<SurfaceSurfaceRegion>) -> Self {
SurfaceSurfaceIntersection {
curves: Vec::new(),
contact_points: Vec::new(),
coincident: true,
method: IntersectionMethod::Numeric,
completeness,
}
}
pub fn curves(&self) -> &[IntersectionCurve] {
&self.curves
}
pub fn contact_points(&self) -> &[Point] {
&self.contact_points
}
pub fn is_coincident(&self) -> bool {
self.coincident
}
pub fn method(&self) -> IntersectionMethod {
self.method
}
pub fn completeness(&self) -> &Completeness<SurfaceSurfaceRegion> {
&self.completeness
}
pub fn is_complete(&self) -> bool {
self.completeness.is_complete()
}
pub fn is_empty(&self) -> bool {
self.curves.is_empty() && self.contact_points.is_empty() && !self.coincident
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Completeness, Point};
fn hit(first: f64, second: f64) -> CurveCurveHit {
CurveCurveHit {
first_parameter: Some(first),
second_parameter: Some(second),
point: Point::ORIGIN,
multiplicity: 1,
}
}
#[test]
fn test_exact_curve_curve_is_complete_and_sorted() {
let r = CurveCurveIntersection::exact(
[hit(2.0, 0.0), hit(1.0, 5.0)].into_iter().collect(),
CurveCurveOverlapList::new(),
);
assert_eq!(r.method(), IntersectionMethod::Exact);
assert!(r.is_complete());
assert_eq!(r.hits()[0].first_parameter, Some(1.0));
assert!(!r.is_empty());
}
#[test]
fn test_empty_incomplete_is_empty_but_not_complete() {
let r = CurveCurveIntersection::numeric(vec![], Vec::new(), Completeness::Sampled);
assert!(r.is_empty());
assert!(!r.is_complete());
assert_eq!(r.method(), IntersectionMethod::Numeric);
}
#[test]
fn test_coincident_surfaces_are_not_empty() {
let r = SurfaceSurfaceIntersection::exact(vec![], vec![], true);
assert!(r.is_coincident());
assert!(!r.is_empty());
}
#[test]
fn test_unbounded_overlap_is_not_empty() {
let r = CurveSurfaceIntersection::exact(
[],
[(f64::NEG_INFINITY, f64::INFINITY)].into_iter().collect(),
);
assert!(!r.is_empty());
assert_eq!(r.overlaps().len(), 1);
}
#[test]
fn test_multiplicity_is_two_when_tangent_is_perpendicular_to_normal() {
let tol = Tolerance::DEFAULT;
let p = Point::ORIGIN;
assert_eq!(curve_surface_multiplicity(Vector::X, Vector::Z, p, tol), 2);
assert_eq!(curve_surface_multiplicity(Vector::Z, Vector::Z, p, tol), 1);
assert_eq!(
curve_surface_multiplicity(Vector::ZERO, Vector::Z, p, tol),
1
);
}
#[test]
fn test_two_dimensional_result_is_exact_and_complete() {
let r = CurveCurveIntersection2D::exact([], CurveCurveOverlapList::new());
assert_eq!(r.method(), IntersectionMethod::Exact);
assert!(r.is_complete() && r.is_empty());
}
}