1#![allow(unused_parens)] use crate::bounding_volume::SimdAabb;
4use crate::math::{Point, Real, SimdReal, SIMD_WIDTH};
5use crate::partitioning::{SimdBestFirstVisitStatus, SimdBestFirstVisitor};
6use crate::query::visitors::CompositePointContainmentTest;
7use crate::query::{PointProjection, PointQuery, PointQueryWithLocation};
8use crate::shape::{
9 FeatureId, SegmentPointLocation, TriMesh, TrianglePointLocation, TypedSimdCompositeShape,
10};
11use na;
12use simba::simd::{SimdBool as _, SimdPartialOrd, SimdValue};
13
14use crate::shape::{Compound, Polyline};
15
16impl PointQuery for Polyline {
17 #[inline]
18 fn project_local_point(&self, point: &Point<Real>, solid: bool) -> PointProjection {
19 self.project_local_point_and_get_location(point, solid).0
20 }
21
22 #[inline]
23 fn project_local_point_and_get_feature(
24 &self,
25 point: &Point<Real>,
26 ) -> (PointProjection, FeatureId) {
27 let mut visitor =
28 PointCompositeShapeProjWithFeatureBestFirstVisitor::new(self, point, false);
29 let (proj, (id, feature)) = self.qbvh().traverse_best_first(&mut visitor).unwrap().1;
30 let polyline_feature = self.segment_feature_to_polyline_feature(id, feature);
31
32 (proj, polyline_feature)
33 }
34
35 #[inline]
38 fn contains_local_point(&self, point: &Point<Real>) -> bool {
39 let mut visitor = CompositePointContainmentTest::new(self, point);
40 let _ = self.qbvh().traverse_depth_first(&mut visitor);
41 visitor.found
42 }
43}
44
45impl PointQuery for TriMesh {
46 #[inline]
47 fn project_local_point(&self, point: &Point<Real>, solid: bool) -> PointProjection {
48 self.project_local_point_and_get_location(point, solid).0
49 }
50
51 #[inline]
52 fn project_local_point_and_get_feature(
53 &self,
54 point: &Point<Real>,
55 ) -> (PointProjection, FeatureId) {
56 #[cfg(feature = "dim3")]
57 if self.pseudo_normals().is_some() {
58 let (proj, (id, _feature)) = self.project_local_point_and_get_location(point, false);
60 let feature_id = FeatureId::Face(id);
61 return (proj, feature_id);
62 }
63
64 let solid = cfg!(feature = "dim2");
65
66 let mut visitor =
67 PointCompositeShapeProjWithFeatureBestFirstVisitor::new(self, point, solid);
68 let (proj, (id, _feature)) = self.qbvh().traverse_best_first(&mut visitor).unwrap().1;
69 let feature_id = FeatureId::Face(id);
70 (proj, feature_id)
71 }
72
73 #[inline]
76 fn contains_local_point(&self, point: &Point<Real>) -> bool {
77 #[cfg(feature = "dim3")]
78 if self.pseudo_normals.is_some() {
79 return self
81 .project_local_point_and_get_location(point, true)
82 .0
83 .is_inside;
84 }
85
86 let mut visitor = CompositePointContainmentTest::new(self, point);
87 let _ = self.qbvh().traverse_depth_first(&mut visitor);
88 visitor.found
89 }
90
91 fn project_local_point_with_max_dist(
93 &self,
94 pt: &Point<Real>,
95 solid: bool,
96 max_dist: Real,
97 ) -> Option<PointProjection> {
98 self.project_local_point_and_get_location_with_max_dist(pt, solid, max_dist)
99 .map(|proj| proj.0)
100 }
101}
102
103impl PointQuery for Compound {
104 #[inline]
105 fn project_local_point(&self, point: &Point<Real>, solid: bool) -> PointProjection {
106 let mut visitor = PointCompositeShapeProjBestFirstVisitor::new(self, point, solid);
107 self.qbvh().traverse_best_first(&mut visitor).unwrap().1 .0
108 }
109
110 #[inline]
111 fn project_local_point_and_get_feature(
112 &self,
113 point: &Point<Real>,
114 ) -> (PointProjection, FeatureId) {
115 (self.project_local_point(point, false), FeatureId::Unknown)
116 }
117
118 #[inline]
119 fn contains_local_point(&self, point: &Point<Real>) -> bool {
120 let mut visitor = CompositePointContainmentTest::new(self, point);
121 let _ = self.qbvh().traverse_depth_first(&mut visitor);
122 visitor.found
123 }
124}
125
126impl PointQueryWithLocation for Polyline {
127 type Location = (u32, SegmentPointLocation);
128
129 #[inline]
130 fn project_local_point_and_get_location(
131 &self,
132 point: &Point<Real>,
133 solid: bool,
134 ) -> (PointProjection, Self::Location) {
135 let mut visitor =
136 PointCompositeShapeProjWithLocationBestFirstVisitor::new(self, point, solid);
137 self.qbvh().traverse_best_first(&mut visitor).unwrap().1
138 }
139}
140
141impl PointQueryWithLocation for TriMesh {
142 type Location = (u32, TrianglePointLocation);
143
144 #[inline]
145 #[allow(unused_mut)] fn project_local_point_and_get_location(
147 &self,
148 point: &Point<Real>,
149 solid: bool,
150 ) -> (PointProjection, Self::Location) {
151 self.project_local_point_and_get_location_with_max_dist(point, solid, Real::MAX)
152 .unwrap()
153 }
154
155 fn project_local_point_and_get_location_with_max_dist(
157 &self,
158 point: &Point<Real>,
159 solid: bool,
160 max_dist: Real,
161 ) -> Option<(PointProjection, Self::Location)> {
162 let mut visitor =
163 PointCompositeShapeProjWithLocationBestFirstVisitor::new(self, point, solid);
164
165 #[allow(unused_mut)] if let Some((_, (mut proj, (part_id, location)))) =
167 self.qbvh()
168 .traverse_best_first_node(&mut visitor, 0, max_dist)
169 {
170 #[cfg(feature = "dim3")]
171 if let Some(pseudo_normals) = self.pseudo_normals_if_oriented() {
172 let pseudo_normal = match location {
173 TrianglePointLocation::OnFace(..) | TrianglePointLocation::OnSolid => {
174 Some(self.triangle(part_id).scaled_normal())
175 }
176 TrianglePointLocation::OnEdge(i, _) => pseudo_normals
177 .edges_pseudo_normal
178 .get(part_id as usize)
179 .map(|pn| pn[i as usize]),
180 TrianglePointLocation::OnVertex(i) => {
181 let idx = self.indices()[part_id as usize];
182 pseudo_normals
183 .vertices_pseudo_normal
184 .get(idx[i as usize] as usize)
185 .copied()
186 }
187 };
188
189 if let Some(pseudo_normal) = pseudo_normal {
190 let dpt = point - proj.point;
191 proj.is_inside = dpt.dot(&pseudo_normal) <= 0.0;
192 }
193 }
194
195 Some((proj, (part_id, location)))
196 } else {
197 None
198 }
199 }
200}
201
202macro_rules! gen_visitor(
206 ($Visitor: ident, $project_local_point: ident, $project_point: ident $(, $Location: ty, $extra_info: ident)* $(| $args: ident)* $(where $PartShapeBound: ident)*) => {
207 pub struct $Visitor<'a, S> {
209 shape: &'a S,
210 point: &'a Point<Real>,
211 simd_point: Point<SimdReal>,
212 solid: bool,
213 }
214
215 impl<'a, S> $Visitor<'a, S> {
216 pub fn new(shape: &'a S, point: &'a Point<Real>, solid: bool) -> Self {
218 Self {
219 shape,
220 point,
221 simd_point: Point::splat(*point),
222 solid,
223 }
224 }
225 }
226
227 impl<'a, S> SimdBestFirstVisitor<S::PartId, SimdAabb> for $Visitor<'a, S>
228 where S: TypedSimdCompositeShape
229 $(, $Location: Copy)*
230 $(, S::PartShape: $PartShapeBound)* {
231 type Result = (PointProjection, (S::PartId $(, $Location)*));
232
233 #[inline]
234 fn visit(
235 &mut self,
236 best: Real,
237 aabb: &SimdAabb,
238 data: Option<[Option<&S::PartId>; SIMD_WIDTH]>,
239 ) -> SimdBestFirstVisitStatus<Self::Result> {
240 let dist = aabb.distance_to_local_point(&self.simd_point);
241 let mask = dist.simd_lt(SimdReal::splat(best));
242
243 if let Some(data) = data {
244 let mut weights = [0.0; SIMD_WIDTH];
245 let mut results = [None; SIMD_WIDTH];
246 let bitmask = mask.bitmask();
247
248 for ii in 0..SIMD_WIDTH {
249 if (bitmask & (1 << ii)) != 0 && data[ii].is_some() {
250 let mut is_inside = false;
251 let subshape_id = *data[ii].unwrap();
252 self.shape.map_typed_part_at(subshape_id, |part_pos, part_shape, _| {
253 let (proj $(, $extra_info)*) = if let Some(part_pos) = part_pos {
254 part_shape.$project_point(
255 part_pos,
256 self.point
257 $(, self.$args)*
258 )
259 } else {
260 part_shape.$project_local_point(
261 self.point
262 $(, self.$args)*
263 )
264 };
265
266 is_inside = proj.is_inside;
267 weights[ii] = na::distance(self.point, &proj.point);
268 results[ii] = Some((proj, (subshape_id $(, $extra_info)*)));
269 });
270
271 if self.solid && is_inside {
272 return SimdBestFirstVisitStatus::ExitEarly(results[ii]);
273 }
274 }
275 }
276
277 SimdBestFirstVisitStatus::MaybeContinue {
278 weights: SimdReal::from(weights),
279 mask,
280 results,
281 }
282 } else {
283 SimdBestFirstVisitStatus::MaybeContinue {
284 weights: dist,
285 mask,
286 results: [None; SIMD_WIDTH],
287 }
288 }
289 }
290 }
291 }
292);
293
294gen_visitor!(
295 PointCompositeShapeProjBestFirstVisitor,
296 project_local_point,
297 project_point | solid
298);
299gen_visitor!(
300 PointCompositeShapeProjWithLocationBestFirstVisitor,
301 project_local_point_and_get_location,
302 project_point_and_get_location,
303 <S::PartShape as PointQueryWithLocation>::Location,
304 extra_info | solid
305 where PointQueryWithLocation
306 where Copy
307);
308gen_visitor!(
309 PointCompositeShapeProjWithFeatureBestFirstVisitor,
310 project_local_point_and_get_feature,
311 project_point_and_get_feature,
312 FeatureId,
313 extra_info
314);