parry2d/query/point/
point_segment.rs

1use crate::math::Vector;
2use crate::query::{PointProjection, PointQuery, PointQueryWithLocation};
3use crate::shape::{FeatureId, Segment, SegmentPointLocation};
4
5impl PointQuery for Segment {
6    #[inline]
7    fn project_local_point(&self, pt: Vector, solid: bool) -> PointProjection {
8        self.project_local_point_and_get_location(pt, solid).0
9    }
10
11    #[inline]
12    fn project_local_point_and_get_feature(&self, pt: Vector) -> (PointProjection, FeatureId) {
13        let (proj, loc) = self.project_local_point_and_get_location(pt, false);
14        let feature = match loc {
15            SegmentPointLocation::OnVertex(i) => FeatureId::Vertex(i),
16            SegmentPointLocation::OnEdge(..) => {
17                #[cfg(feature = "dim2")]
18                {
19                    let dir = self.scaled_direction();
20                    let dpt = pt - proj.point;
21                    if dpt.perp_dot(dir) >= 0.0 {
22                        FeatureId::Face(0)
23                    } else {
24                        FeatureId::Face(1)
25                    }
26                }
27
28                #[cfg(feature = "dim3")]
29                {
30                    FeatureId::Edge(0)
31                }
32            }
33        };
34
35        (proj, feature)
36    }
37
38    // NOTE: the default implementation of `.distance_to_point(...)` will return the error that was
39    // eaten by the `::approx_eq(...)` on `project_point(...)`.
40}
41
42impl PointQueryWithLocation for Segment {
43    type Location = SegmentPointLocation;
44
45    #[inline]
46    fn project_local_point_and_get_location(
47        &self,
48        pt: Vector,
49        _: bool,
50    ) -> (PointProjection, Self::Location) {
51        let ab = self.b - self.a;
52        let ap = pt - self.a;
53        let ab_ap = ab.dot(ap);
54        let sqnab = ab.length_squared();
55
56        let proj;
57        let location;
58
59        if ab_ap <= 0.0 {
60            // Voronoï region of vertex 'a'.
61            location = SegmentPointLocation::OnVertex(0);
62            proj = self.a;
63        } else if ab_ap >= sqnab {
64            // Voronoï region of vertex 'b'.
65            location = SegmentPointLocation::OnVertex(1);
66            proj = self.b;
67        } else {
68            assert!(sqnab != 0.0);
69
70            // Voronoï region of the segment interior.
71            let u = ab_ap / sqnab;
72            let bcoords = [1.0 - u, u];
73            location = SegmentPointLocation::OnEdge(bcoords);
74            proj = self.a + ab * u;
75        }
76
77        // TODO: is this acceptable?
78        let inside = relative_eq!(proj, pt);
79
80        (PointProjection::new(inside, proj), location)
81    }
82}