use crate::math::{Point, Real};
use crate::query::{PointProjection, PointQuery, PointQueryWithLocation};
use crate::shape::{FeatureId, Segment, SegmentPointLocation};
impl PointQuery for Segment {
#[inline]
fn project_local_point(&self, pt: &Point<Real>, solid: bool) -> PointProjection {
self.project_local_point_and_get_location(pt, solid).0
}
#[inline]
fn project_local_point_and_get_feature(
&self,
pt: &Point<Real>,
) -> (PointProjection, FeatureId) {
let (proj, loc) = self.project_local_point_and_get_location(pt, false);
let feature = match loc {
SegmentPointLocation::OnVertex(i) => FeatureId::Vertex(i),
SegmentPointLocation::OnEdge(..) => {
#[cfg(feature = "dim2")]
{
let dir = self.scaled_direction();
let dpt = *pt - proj.point;
if dpt.perp(&dir) >= 0.0 {
FeatureId::Face(0)
} else {
FeatureId::Face(1)
}
}
#[cfg(feature = "dim3")]
{
FeatureId::Edge(0)
}
}
};
(proj, feature)
}
}
impl PointQueryWithLocation for Segment {
type Location = SegmentPointLocation;
#[inline]
fn project_local_point_and_get_location(
&self,
pt: &Point<Real>,
_: bool,
) -> (PointProjection, Self::Location) {
let ab = self.b - self.a;
let ap = pt - self.a;
let ab_ap = ab.dot(&ap);
let sqnab = ab.norm_squared();
let proj;
let location;
if ab_ap <= 0.0 {
location = SegmentPointLocation::OnVertex(0);
proj = self.a;
} else if ab_ap >= sqnab {
location = SegmentPointLocation::OnVertex(1);
proj = self.b;
} else {
assert!(sqnab != 0.0);
let u = ab_ap / sqnab;
let bcoords = [1.0 - u, u];
location = SegmentPointLocation::OnEdge(bcoords);
proj = self.a + ab * u;
}
let inside = relative_eq!(proj, *pt);
(PointProjection::new(inside, proj), location)
}
}