parry2d/query/point/
point_query.rs

1use crate::math::{Isometry, Point, Real};
2use crate::shape::FeatureId;
3use na;
4
5#[cfg(feature = "rkyv")]
6use rkyv::{bytecheck, CheckBytes};
7
8/// Description of the projection of a point on a shape.
9#[derive(Copy, Clone, Debug, Default)]
10#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
11#[cfg_attr(
12    feature = "rkyv",
13    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, CheckBytes),
14    archive(as = "Self")
15)]
16pub struct PointProjection {
17    /// Whether or not the point to project was inside of the shape.
18    pub is_inside: bool,
19    /// The projection result.
20    pub point: Point<Real>,
21}
22
23impl PointProjection {
24    /// Initializes a new `PointProjection`.
25    pub fn new(is_inside: bool, point: Point<Real>) -> Self {
26        PointProjection { is_inside, point }
27    }
28
29    /// Transforms `self.point` by `pos`.
30    pub fn transform_by(&self, pos: &Isometry<Real>) -> Self {
31        PointProjection {
32            is_inside: self.is_inside,
33            point: pos * self.point,
34        }
35    }
36
37    /// Returns `true` if `Self::is_inside` is `true` or if the distance between the projected point and `point` is smaller than `min_dist`.
38    pub fn is_inside_eps(&self, original_point: &Point<Real>, min_dist: Real) -> bool {
39        self.is_inside || na::distance_squared(original_point, &self.point) < min_dist * min_dist
40    }
41}
42
43/// Trait of objects that can be tested for point inclusion and projection.
44pub trait PointQuery {
45    /// Projects a point on `self`, unless the projection lies further than the given max distance.
46    ///
47    /// The point is assumed to be expressed in the local-space of `self`.
48    fn project_local_point_with_max_dist(
49        &self,
50        pt: &Point<Real>,
51        solid: bool,
52        max_dist: Real,
53    ) -> Option<PointProjection> {
54        let proj = self.project_local_point(pt, solid);
55        if na::distance(&proj.point, pt) > max_dist {
56            None
57        } else {
58            Some(proj)
59        }
60    }
61
62    /// Projects a point on `self` transformed by `m`, unless the projection lies further than the given max distance.
63    fn project_point_with_max_dist(
64        &self,
65        m: &Isometry<Real>,
66        pt: &Point<Real>,
67        solid: bool,
68        max_dist: Real,
69    ) -> Option<PointProjection> {
70        self.project_local_point_with_max_dist(&m.inverse_transform_point(pt), solid, max_dist)
71            .map(|proj| proj.transform_by(m))
72    }
73
74    /// Projects a point on `self`.
75    ///
76    /// The point is assumed to be expressed in the local-space of `self`.
77    fn project_local_point(&self, pt: &Point<Real>, solid: bool) -> PointProjection;
78
79    /// Projects a point on the boundary of `self` and returns the id of the
80    /// feature the point was projected on.
81    fn project_local_point_and_get_feature(&self, pt: &Point<Real>)
82        -> (PointProjection, FeatureId);
83
84    /// Computes the minimal distance between a point and `self`.
85    fn distance_to_local_point(&self, pt: &Point<Real>, solid: bool) -> Real {
86        let proj = self.project_local_point(pt, solid);
87        let dist = na::distance(pt, &proj.point);
88
89        if solid || !proj.is_inside {
90            dist
91        } else {
92            -dist
93        }
94    }
95
96    /// Tests if the given point is inside of `self`.
97    fn contains_local_point(&self, pt: &Point<Real>) -> bool {
98        self.project_local_point(pt, true).is_inside
99    }
100
101    /// Projects a point on `self` transformed by `m`.
102    fn project_point(&self, m: &Isometry<Real>, pt: &Point<Real>, solid: bool) -> PointProjection {
103        self.project_local_point(&m.inverse_transform_point(pt), solid)
104            .transform_by(m)
105    }
106
107    /// Computes the minimal distance between a point and `self` transformed by `m`.
108    #[inline]
109    fn distance_to_point(&self, m: &Isometry<Real>, pt: &Point<Real>, solid: bool) -> Real {
110        self.distance_to_local_point(&m.inverse_transform_point(pt), solid)
111    }
112
113    /// Projects a point on the boundary of `self` transformed by `m` and returns the id of the
114    /// feature the point was projected on.
115    fn project_point_and_get_feature(
116        &self,
117        m: &Isometry<Real>,
118        pt: &Point<Real>,
119    ) -> (PointProjection, FeatureId) {
120        let res = self.project_local_point_and_get_feature(&m.inverse_transform_point(pt));
121        (res.0.transform_by(m), res.1)
122    }
123
124    /// Tests if the given point is inside of `self` transformed by `m`.
125    #[inline]
126    fn contains_point(&self, m: &Isometry<Real>, pt: &Point<Real>) -> bool {
127        self.contains_local_point(&m.inverse_transform_point(pt))
128    }
129}
130
131/// Returns shape-specific info in addition to generic projection information
132///
133/// One requirement for the `PointQuery` trait is to be usable as a trait
134/// object. Unfortunately this precludes us from adding an associated type to it
135/// that might allow us to return shape-specific information in addition to the
136/// general information provided in `PointProjection`. This is where
137/// `PointQueryWithLocation` comes in. It forgoes the ability to be used as a trait
138/// object in exchange for being able to provide shape-specific projection
139/// information.
140///
141/// Any shapes that implement `PointQuery` but are able to provide extra
142/// information, can implement `PointQueryWithLocation` in addition and have their
143/// `PointQuery::project_point` implementation just call out to
144/// `PointQueryWithLocation::project_point_and_get_location`.
145pub trait PointQueryWithLocation {
146    /// Additional shape-specific projection information
147    ///
148    /// In addition to the generic projection information returned in
149    /// `PointProjection`, implementations might provide shape-specific
150    /// projection info. The type of this shape-specific information is defined
151    /// by this associated type.
152    type Location;
153
154    /// Projects a point on `self`.
155    fn project_local_point_and_get_location(
156        &self,
157        pt: &Point<Real>,
158        solid: bool,
159    ) -> (PointProjection, Self::Location);
160
161    /// Projects a point on `self` transformed by `m`.
162    fn project_point_and_get_location(
163        &self,
164        m: &Isometry<Real>,
165        pt: &Point<Real>,
166        solid: bool,
167    ) -> (PointProjection, Self::Location) {
168        let res = self.project_local_point_and_get_location(&m.inverse_transform_point(pt), solid);
169        (res.0.transform_by(m), res.1)
170    }
171
172    /// Projects a point on `self`, with a maximum projection distance.
173    fn project_local_point_and_get_location_with_max_dist(
174        &self,
175        pt: &Point<Real>,
176        solid: bool,
177        max_dist: Real,
178    ) -> Option<(PointProjection, Self::Location)> {
179        let (proj, location) = self.project_local_point_and_get_location(pt, solid);
180        if na::distance(&proj.point, pt) > max_dist {
181            None
182        } else {
183            Some((proj, location))
184        }
185    }
186
187    /// Projects a point on `self` transformed by `m`, with a maximum projection distance.
188    fn project_point_and_get_location_with_max_dist(
189        &self,
190        m: &Isometry<Real>,
191        pt: &Point<Real>,
192        solid: bool,
193        max_dist: Real,
194    ) -> Option<(PointProjection, Self::Location)> {
195        self.project_local_point_and_get_location_with_max_dist(
196            &m.inverse_transform_point(pt),
197            solid,
198            max_dist,
199        )
200        .map(|res| (res.0.transform_by(m), res.1))
201    }
202}