1#![allow(unused_parens)] use crate::math::{Real, Vector};
4use crate::partitioning::BvhNode;
5use crate::query::{PointProjection, PointQuery, PointQueryWithLocation};
6use crate::shape::{
7 CompositeShapeRef, FeatureId, SegmentPointLocation, TriMesh, TrianglePointLocation,
8 TypedCompositeShape,
9};
10
11use crate::shape::{Compound, Polyline};
12
13impl<S: TypedCompositeShape> CompositeShapeRef<'_, S> {
14 #[inline]
19 pub fn project_local_point_and_get_location(
20 &self,
21 point: Vector,
22 max_dist: Real,
23 solid: bool,
24 ) -> Option<(
25 u32,
26 (
27 PointProjection,
28 <S::PartShape as PointQueryWithLocation>::Location,
29 ),
30 )>
31 where
32 S::PartShape: PointQueryWithLocation,
33 {
34 self.0
35 .bvh()
36 .find_best(
37 max_dist,
38 |node: &BvhNode, _best_so_far| node.aabb().distance_to_local_point(point, true),
39 |primitive, _best_so_far| {
40 let proj = self.0.map_typed_part_at(primitive, |pose, shape, _| {
41 if let Some(pose) = pose {
42 shape.project_point_and_get_location(pose, point, solid)
43 } else {
44 shape.project_local_point_and_get_location(point, solid)
45 }
46 })?;
47 let cost = (proj.0.point - point).length();
48 Some((cost, proj))
49 },
50 )
51 .map(|(best_id, (_, (proj, location)))| (best_id, (proj, location)))
52 }
53
54 pub fn project_local_point(
60 &self,
61 point: Vector,
62 max_dist: Real,
63 solid: bool,
64 ) -> Option<(u32, PointProjection)> {
65 let (best_id, (_, proj)) = self.0.bvh().find_best(
66 max_dist,
67 |node: &BvhNode, _best_so_far| node.aabb().distance_to_local_point(point, true),
68 |primitive, _best_so_far| {
69 let proj = self.0.map_typed_part_at(primitive, |pose, shape, _| {
70 if let Some(pose) = pose {
71 shape.project_point(pose, point, solid)
72 } else {
73 shape.project_local_point(point, solid)
74 }
75 })?;
76 let dist = (proj.point - point).length();
77 Some((dist, proj))
78 },
79 )?;
80 Some((best_id, proj))
81 }
82
83 #[inline]
89 pub fn project_local_point_and_get_feature(
90 &self,
91 point: Vector,
92 max_dist: Real,
93 ) -> Option<(u32, (PointProjection, FeatureId))> {
94 let (best_id, (_, (proj, feature_id))) = self.0.bvh().find_best(
95 max_dist,
96 |node: &BvhNode, _best_so_far| node.aabb().distance_to_local_point(point, true),
97 |primitive, _best_so_far| {
98 let proj = self.0.map_typed_part_at(primitive, |pose, shape, _| {
99 if let Some(pose) = pose {
100 shape.project_point_and_get_feature(pose, point)
101 } else {
102 shape.project_local_point_and_get_feature(point)
103 }
104 })?;
105 let cost = (proj.0.point - point).length();
106 Some((cost, proj))
107 },
108 )?;
109 Some((best_id, (proj, feature_id)))
110 }
111
112 #[inline]
116 pub fn contains_local_point(&self, point: Vector) -> Option<u32> {
117 self.0
118 .bvh()
119 .leaves(|node: &BvhNode| node.aabb().contains_local_point(point))
120 .find(|leaf_id| {
121 self.0
122 .map_typed_part_at(*leaf_id, |pose, shape, _| {
123 if let Some(pose) = pose {
124 shape.contains_point(pose, point)
125 } else {
126 shape.contains_local_point(point)
127 }
128 })
129 .unwrap_or(false)
130 })
131 }
132}
133
134impl PointQuery for Polyline {
135 #[inline]
136 fn project_local_point(&self, point: Vector, solid: bool) -> PointProjection {
137 self.project_local_point_and_get_location(point, solid).0
138 }
139
140 #[inline]
141 #[allow(unused_mut)] fn project_local_point_and_get_feature(&self, point: Vector) -> (PointProjection, FeatureId) {
143 let Some((seg_id, (mut proj, feature))) =
147 CompositeShapeRef(self).project_local_point_and_get_feature(point, Real::MAX)
148 else {
149 return (PointProjection::new(false, point), FeatureId::Unknown);
150 };
151
152 #[cfg(feature = "dim2")]
154 if let Some(constraints) = self.segment_normal_constraints(seg_id) {
155 let pseudo_normal = match feature {
156 FeatureId::Vertex(i) => constraints.edges[i as usize],
157 _ => constraints.face,
158 };
159 proj.is_inside = (point - proj.point).dot(pseudo_normal) <= 0.0;
160 }
161
162 let polyline_feature = self.segment_feature_to_polyline_feature(seg_id, feature);
163 (proj, polyline_feature)
164 }
165
166 #[inline]
169 fn contains_local_point(&self, point: Vector) -> bool {
170 #[cfg(feature = "dim2")]
172 if self.flags().contains(crate::shape::PolylineFlags::ORIENTED) {
173 return self
174 .project_local_point_and_get_location(point, true)
175 .0
176 .is_inside;
177 }
178
179 CompositeShapeRef(self)
180 .contains_local_point(point)
181 .is_some()
182 }
183}
184
185impl PointQuery for TriMesh {
186 #[inline]
187 fn project_local_point(&self, point: Vector, solid: bool) -> PointProjection {
188 CompositeShapeRef(self)
189 .project_local_point(point, Real::MAX, solid)
190 .map(|(_, proj)| proj)
191 .unwrap_or(PointProjection::new(false, point))
194 }
195
196 #[inline]
197 fn project_local_point_and_get_feature(&self, point: Vector) -> (PointProjection, FeatureId) {
198 #[cfg(feature = "dim3")]
199 if self.pseudo_normals().is_some() {
200 let (proj, (id, _feature)) = self.project_local_point_and_get_location(point, false);
202 let feature_id = FeatureId::Face(id);
203 return (proj, feature_id);
204 }
205
206 let solid = cfg!(feature = "dim2");
207 let Some((tri_id, proj)) =
210 CompositeShapeRef(self).project_local_point(point, Real::MAX, solid)
211 else {
212 return (PointProjection::new(false, point), FeatureId::Unknown);
213 };
214 (proj, FeatureId::Face(tri_id))
215 }
216
217 #[inline]
220 fn contains_local_point(&self, point: Vector) -> bool {
221 #[cfg(feature = "dim3")]
222 if self.pseudo_normals.is_some() {
223 return self
225 .project_local_point_and_get_location(point, true)
226 .0
227 .is_inside;
228 }
229
230 CompositeShapeRef(self)
231 .contains_local_point(point)
232 .is_some()
233 }
234
235 fn project_local_point_with_max_dist(
237 &self,
238 pt: Vector,
239 solid: bool,
240 max_dist: Real,
241 ) -> Option<PointProjection> {
242 self.project_local_point_and_get_location_with_max_dist(pt, solid, max_dist)
243 .map(|proj| proj.0)
244 }
245}
246
247impl PointQuery for Compound {
248 #[inline]
249 fn project_local_point(&self, point: Vector, solid: bool) -> PointProjection {
250 CompositeShapeRef(self)
251 .project_local_point(point, Real::MAX, solid)
252 .map(|(_, proj)| proj)
253 .unwrap_or(PointProjection::new(false, point))
256 }
257
258 #[inline]
259 fn project_local_point_and_get_feature(&self, point: Vector) -> (PointProjection, FeatureId) {
260 (
261 CompositeShapeRef(self)
262 .project_local_point_and_get_feature(point, Real::MAX)
263 .map(|(_, (proj, _))| proj)
264 .unwrap_or(PointProjection::new(false, point)),
267 FeatureId::Unknown,
268 )
269 }
270
271 #[inline]
272 fn contains_local_point(&self, point: Vector) -> bool {
273 CompositeShapeRef(self)
274 .contains_local_point(point)
275 .is_some()
276 }
277}
278
279impl PointQueryWithLocation for Polyline {
280 type Location = (u32, SegmentPointLocation);
281
282 #[inline]
283 fn project_local_point_and_get_location(
284 &self,
285 point: Vector,
286 solid: bool,
287 ) -> (PointProjection, Self::Location) {
288 self.project_local_point_and_get_location_with_max_dist(point, solid, Real::MAX)
289 .unwrap_or((
292 PointProjection::new(false, point),
293 (0, SegmentPointLocation::OnVertex(0)),
294 ))
295 }
296
297 fn project_local_point_and_get_location_with_max_dist(
299 &self,
300 point: Vector,
301 solid: bool,
302 max_dist: Real,
303 ) -> Option<(PointProjection, Self::Location)> {
304 #[allow(unused_mut)] if let Some((seg_id, (mut proj, loc))) =
306 CompositeShapeRef(self).project_local_point_and_get_location(point, max_dist, solid)
307 {
308 #[cfg(feature = "dim2")]
310 if let Some(constraints) = self.segment_normal_constraints(seg_id) {
311 let pseudo_normal = match loc {
312 SegmentPointLocation::OnVertex(i) => constraints.edges[i as usize],
313 SegmentPointLocation::OnEdge(_) => constraints.face,
314 };
315 proj.is_inside = (point - proj.point).dot(pseudo_normal) <= 0.0;
316
317 if proj.is_inside && solid {
318 proj.point = point;
319 }
320 }
321
322 Some((proj, (seg_id, loc)))
323 } else {
324 None
325 }
326 }
327}
328
329impl PointQueryWithLocation for TriMesh {
330 type Location = (u32, TrianglePointLocation);
331
332 #[inline]
333 #[allow(unused_mut)] fn project_local_point_and_get_location(
335 &self,
336 point: Vector,
337 solid: bool,
338 ) -> (PointProjection, Self::Location) {
339 self.project_local_point_and_get_location_with_max_dist(point, solid, Real::MAX)
340 .unwrap_or((
343 PointProjection::new(false, point),
344 (0, TrianglePointLocation::OnVertex(0)),
345 ))
346 }
347
348 fn project_local_point_and_get_location_with_max_dist(
350 &self,
351 point: Vector,
352 solid: bool,
353 max_dist: Real,
354 ) -> Option<(PointProjection, Self::Location)> {
355 #[allow(unused_mut)] if let Some((part_id, (mut proj, location))) =
357 CompositeShapeRef(self).project_local_point_and_get_location(point, max_dist, solid)
358 {
359 #[cfg(feature = "dim3")]
360 if let Some(pseudo_normals) = self.pseudo_normals_if_oriented() {
361 let pseudo_normal = match location {
362 TrianglePointLocation::OnFace(..) | TrianglePointLocation::OnSolid => {
363 Some(self.triangle(part_id).scaled_normal())
364 }
365 TrianglePointLocation::OnEdge(i, _) => pseudo_normals
366 .edges_pseudo_normal
367 .get(part_id as usize)
368 .map(|pn| pn[i as usize]),
369 TrianglePointLocation::OnVertex(i) => {
370 let idx = self.indices()[part_id as usize];
371 pseudo_normals
372 .vertices_pseudo_normal
373 .get(idx[i as usize] as usize)
374 .copied()
375 }
376 };
377
378 if let Some(pseudo_normal) = pseudo_normal {
379 let dpt = point - proj.point;
380 proj.is_inside = dpt.dot(pseudo_normal) <= 0.0;
381 }
382 }
383
384 Some((proj, (part_id, location)))
385 } else {
386 None
387 }
388 }
389}