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, SubShapeId, TriMesh, TrianglePointLocation,
8 TypedCompositeShape,
9};
10
11use crate::shape::{Compound, Polyline};
12
13fn triangle_point_location_feature(location: TrianglePointLocation) -> FeatureId {
15 match location {
16 TrianglePointLocation::OnVertex(i) => FeatureId::Vertex(i),
17 #[cfg(feature = "dim3")]
18 TrianglePointLocation::OnEdge(i, _) => FeatureId::Edge(i),
19 #[cfg(feature = "dim2")]
20 TrianglePointLocation::OnEdge(i, _) => FeatureId::Face(i),
21 TrianglePointLocation::OnFace(i, _) => FeatureId::Face(i),
22 TrianglePointLocation::OnSolid => FeatureId::Face(0),
23 }
24}
25
26impl<S: TypedCompositeShape> CompositeShapeRef<'_, S> {
27 #[inline]
32 pub fn project_local_point_and_get_location(
33 &self,
34 point: Vector,
35 max_dist: Real,
36 solid: bool,
37 ) -> Option<(
38 SubShapeId,
39 PointProjection,
40 <S::PartShape as PointQueryWithLocation>::Location,
41 )>
42 where
43 S::PartShape: PointQueryWithLocation,
44 {
45 self.0
46 .bvh()
47 .find_best(
48 max_dist,
49 |node: &BvhNode, _best_so_far| node.aabb().distance_to_local_point(point, true),
50 |primitive, _best_so_far| {
51 let proj = self.0.map_typed_part_at(primitive, |pose, shape, _| {
52 if let Some(pose) = pose {
53 shape.project_point_and_get_location(pose, point, solid)
54 } else {
55 shape.project_local_point_and_get_location(point, solid)
56 }
57 })?;
58 let cost = (proj.0.point - point).length();
59 Some((cost, proj))
60 },
61 )
62 .map(|(best_id, (_, (proj, location)))| (best_id, proj, location))
63 }
64
65 pub fn project_local_point(
71 &self,
72 point: Vector,
73 max_dist: Real,
74 solid: bool,
75 ) -> Option<(SubShapeId, PointProjection)> {
76 let (best_id, (_, proj)) = self.0.bvh().find_best(
77 max_dist,
78 |node: &BvhNode, _best_so_far| node.aabb().distance_to_local_point(point, true),
79 |primitive, _best_so_far| {
80 let proj = self.0.map_typed_part_at(primitive, |pose, shape, _| {
81 if let Some(pose) = pose {
82 shape.project_point(pose, point, solid)
83 } else {
84 shape.project_local_point(point, solid)
85 }
86 })?;
87 let dist = (proj.point - point).length();
88 Some((dist, proj))
89 },
90 )?;
91 Some((best_id, proj))
92 }
93
94 #[inline]
99 pub fn project_local_point_and_get_feature(
100 &self,
101 point: Vector,
102 max_dist: Real,
103 ) -> Option<(SubShapeId, PointProjection, FeatureId)> {
104 let (best_id, (_, (proj, feature_id))) = self.0.bvh().find_best(
105 max_dist,
106 |node: &BvhNode, _best_so_far| node.aabb().distance_to_local_point(point, true),
107 |primitive, _best_so_far| {
108 let proj = self.0.map_typed_part_at(primitive, |pose, shape, _| {
109 if let Some(pose) = pose {
110 shape.project_point_and_get_feature(pose, point)
111 } else {
112 shape.project_local_point_and_get_feature(point)
113 }
114 })?;
115 let cost = (proj.0.point - point).length();
116 Some((cost, proj))
117 },
118 )?;
119 Some((best_id, proj, feature_id))
120 }
121
122 #[inline]
126 pub fn contains_local_point(&self, point: Vector) -> Option<SubShapeId> {
127 self.0
128 .bvh()
129 .leaves(|node: &BvhNode| node.aabb().contains_local_point(point))
130 .find(|leaf_id| {
131 self.0
132 .map_typed_part_at(*leaf_id, |pose, shape, _| {
133 if let Some(pose) = pose {
134 shape.contains_point(pose, point)
135 } else {
136 shape.contains_local_point(point)
137 }
138 })
139 .unwrap_or(false)
140 })
141 }
142}
143
144impl PointQuery for Polyline {
145 #[inline]
146 fn project_local_point(&self, point: Vector, solid: bool) -> PointProjection {
147 self.project_local_point_and_get_location(point, solid).0
148 }
149
150 #[inline]
151 #[allow(unused_mut)] fn project_local_point_and_get_feature(&self, point: Vector) -> (PointProjection, FeatureId) {
153 let Some((segment_id, mut proj, feature)) =
157 CompositeShapeRef(self).project_local_point_and_get_feature(point, Real::MAX)
158 else {
159 return (PointProjection::new(false, point), FeatureId::Unknown);
160 };
161 proj.subshape = segment_id;
162
163 #[cfg(feature = "dim2")]
165 if let Some(constraints) = self.segment_normal_constraints(segment_id) {
166 let pseudo_normal = match feature {
167 FeatureId::Vertex(i) => constraints.edges[i as usize],
168 _ => constraints.face,
169 };
170 proj.is_inside = (point - proj.point).dot(pseudo_normal) <= 0.0;
171 }
172
173 (proj, feature)
175 }
176
177 #[inline]
180 fn contains_local_point(&self, point: Vector) -> bool {
181 #[cfg(feature = "dim2")]
183 if self.flags().contains(crate::shape::PolylineFlags::ORIENTED) {
184 return self
185 .project_local_point_and_get_location(point, true)
186 .0
187 .is_inside;
188 }
189
190 CompositeShapeRef(self)
191 .contains_local_point(point)
192 .is_some()
193 }
194}
195
196impl PointQuery for TriMesh {
197 #[inline]
198 fn project_local_point(&self, point: Vector, solid: bool) -> PointProjection {
199 self.project_local_point_with_max_dist(point, solid, Real::MAX)
200 .unwrap_or(PointProjection::new(false, point))
203 }
204
205 #[inline]
206 fn project_local_point_and_get_feature(&self, point: Vector) -> (PointProjection, FeatureId) {
207 #[cfg(feature = "dim3")]
208 if self.pseudo_normals().is_some() {
209 let (proj, (_, location)) = self.project_local_point_and_get_location(point, false);
212 return (proj, triangle_point_location_feature(location));
213 }
214
215 let solid = cfg!(feature = "dim2");
216 let Some((triangle_id, proj, location)) =
219 CompositeShapeRef(self).project_local_point_and_get_location(point, Real::MAX, solid)
220 else {
221 return (PointProjection::new(false, point), FeatureId::Unknown);
222 };
223 (
225 proj.with_subshape(triangle_id),
226 triangle_point_location_feature(location),
227 )
228 }
229
230 #[inline]
233 fn contains_local_point(&self, point: Vector) -> bool {
234 #[cfg(feature = "dim3")]
235 if self.pseudo_normals.is_some() {
236 return self
238 .project_local_point_and_get_location(point, true)
239 .0
240 .is_inside;
241 }
242
243 CompositeShapeRef(self)
244 .contains_local_point(point)
245 .is_some()
246 }
247
248 fn project_local_point_with_max_dist(
250 &self,
251 pt: Vector,
252 solid: bool,
253 max_dist: Real,
254 ) -> Option<PointProjection> {
255 self.project_local_point_and_get_location_with_max_dist(pt, solid, max_dist)
256 .map(|proj| proj.0)
257 }
258}
259
260impl PointQuery for Compound {
261 #[inline]
262 fn project_local_point(&self, point: Vector, solid: bool) -> PointProjection {
263 CompositeShapeRef(self)
264 .project_local_point(point, Real::MAX, solid)
265 .map(|(part_id, proj)| proj.with_subshape(part_id))
266 .unwrap_or(PointProjection::new(false, point))
269 }
270
271 #[inline]
272 fn project_local_point_and_get_feature(&self, point: Vector) -> (PointProjection, FeatureId) {
273 CompositeShapeRef(self)
275 .project_local_point_and_get_feature(point, Real::MAX)
276 .map(|(part_id, proj, feature)| (proj.with_subshape(part_id), feature))
277 .unwrap_or((PointProjection::new(false, point), FeatureId::Unknown))
280 }
281
282 #[inline]
283 fn contains_local_point(&self, point: Vector) -> bool {
284 CompositeShapeRef(self)
285 .contains_local_point(point)
286 .is_some()
287 }
288}
289
290impl PointQueryWithLocation for Polyline {
291 type Location = (u32, SegmentPointLocation);
292
293 #[inline]
294 fn project_local_point_and_get_location(
295 &self,
296 point: Vector,
297 solid: bool,
298 ) -> (PointProjection, Self::Location) {
299 self.project_local_point_and_get_location_with_max_dist(point, solid, Real::MAX)
300 .unwrap_or((
303 PointProjection::new(false, point),
304 (0, SegmentPointLocation::OnVertex(0)),
305 ))
306 }
307
308 fn project_local_point_and_get_location_with_max_dist(
310 &self,
311 point: Vector,
312 solid: bool,
313 max_dist: Real,
314 ) -> Option<(PointProjection, Self::Location)> {
315 #[allow(unused_mut)] if let Some((seg_id, mut proj, loc)) =
317 CompositeShapeRef(self).project_local_point_and_get_location(point, max_dist, solid)
318 {
319 proj.subshape = seg_id;
320
321 #[cfg(feature = "dim2")]
323 if let Some(constraints) = self.segment_normal_constraints(seg_id) {
324 let pseudo_normal = match loc {
325 SegmentPointLocation::OnVertex(i) => constraints.edges[i as usize],
326 SegmentPointLocation::OnEdge(_) => constraints.face,
327 };
328 proj.is_inside = (point - proj.point).dot(pseudo_normal) <= 0.0;
329
330 if proj.is_inside && solid {
331 proj.point = point;
332 }
333 }
334
335 Some((proj, (seg_id, loc)))
336 } else {
337 None
338 }
339 }
340}
341
342impl PointQueryWithLocation for TriMesh {
343 type Location = (u32, TrianglePointLocation);
344
345 #[inline]
346 #[allow(unused_mut)] fn project_local_point_and_get_location(
348 &self,
349 point: Vector,
350 solid: bool,
351 ) -> (PointProjection, Self::Location) {
352 self.project_local_point_and_get_location_with_max_dist(point, solid, Real::MAX)
353 .unwrap_or((
356 PointProjection::new(false, point),
357 (0, TrianglePointLocation::OnVertex(0)),
358 ))
359 }
360
361 fn project_local_point_and_get_location_with_max_dist(
363 &self,
364 point: Vector,
365 solid: bool,
366 max_dist: Real,
367 ) -> Option<(PointProjection, Self::Location)> {
368 #[allow(unused_mut)] if let Some((part_id, mut proj, location)) =
370 CompositeShapeRef(self).project_local_point_and_get_location(point, max_dist, solid)
371 {
372 proj.subshape = part_id;
373
374 #[cfg(feature = "dim3")]
375 if let Some(pseudo_normals) = self.pseudo_normals_if_oriented() {
376 let pseudo_normal = match location {
377 TrianglePointLocation::OnFace(..) | TrianglePointLocation::OnSolid => {
378 Some(self.triangle(part_id).scaled_normal())
379 }
380 TrianglePointLocation::OnEdge(i, _) => pseudo_normals
381 .edges_pseudo_normal
382 .get(part_id as usize)
383 .map(|pn| pn[i as usize]),
384 TrianglePointLocation::OnVertex(i) => {
385 let idx = self.indices()[part_id as usize];
386 pseudo_normals
387 .vertices_pseudo_normal
388 .get(idx[i as usize] as usize)
389 .copied()
390 }
391 };
392
393 if let Some(pseudo_normal) = pseudo_normal {
394 let dpt = point - proj.point;
395 proj.is_inside = dpt.dot(pseudo_normal) <= 0.0;
396 }
397 }
398
399 Some((proj, (part_id, location)))
400 } else {
401 None
402 }
403 }
404}