1use itertools::Itertools;
2use parry3d::{
3 math::{Isometry, Point, Vector},
4 partitioning::Qbvh,
5 query::{
6 PointProjection, PointQuery, PointQueryWithLocation, Ray, RayCast, RayIntersection,
7 details::{
8 NormalConstraints, RayCompositeShapeToiAndNormalBestFirstVisitor,
9 RayCompositeShapeToiBestFirstVisitor,
10 },
11 point::PointCompositeShapeProjWithLocationBestFirstVisitor,
12 },
13 shape::{FeatureId, Shape, Triangle, TypedSimdCompositeShape},
14};
15
16use crate::{Face, MeshGraph};
17
18impl PointQuery for MeshGraph {
19 #[inline]
20 fn project_local_point(&self, point: &Point<f32>, solid: bool) -> PointProjection {
21 self.project_local_point_and_get_location(point, solid).0
22 }
23
24 fn project_local_point_and_get_feature(
25 &self,
26 _point: &Point<f32>,
27 ) -> (PointProjection, FeatureId) {
28 unimplemented!("Not available")
29 }
30}
31
32impl PointQueryWithLocation for MeshGraph {
33 type Location = (Face, ());
34
35 #[inline]
36 fn project_local_point_and_get_location(
37 &self,
38 point: &Point<f32>,
39 solid: bool,
40 ) -> (PointProjection, Self::Location) {
41 self.project_local_point_and_get_location_with_max_dist(point, solid, f32::MAX)
42 .unwrap()
43 }
44
45 fn project_local_point_and_get_location_with_max_dist(
47 &self,
48 point: &Point<f32>,
49 solid: bool,
50 max_dist: f32,
51 ) -> Option<(PointProjection, Self::Location)> {
52 let mut visitor =
53 PointCompositeShapeProjWithLocationBestFirstVisitor::new(self, point, solid);
54
55 if let Some((_, (mut proj, (face, _)))) =
56 self.qbvh
57 .traverse_best_first_node(&mut visitor, 0, max_dist)
58 {
59 if let Some(vertex_normals) = &self.vertex_normals {
60 let pseudo_normal = vertex_normals[self.halfedges[face.halfedge].end_vertex];
61
62 let dpt = point - proj.point;
63 proj.is_inside = dpt.dot(&Vector::new(
64 pseudo_normal.x,
65 pseudo_normal.y,
66 pseudo_normal.z,
67 )) <= 0.0;
68 }
69
70 Some((proj, (face, ())))
71 } else {
72 None
73 }
74 }
75}
76
77impl RayCast for MeshGraph {
78 #[inline]
79 fn cast_local_ray(&self, ray: &Ray, max_time_of_impact: f32, solid: bool) -> Option<f32> {
80 let mut visitor =
81 RayCompositeShapeToiBestFirstVisitor::new(self, ray, max_time_of_impact, solid);
82
83 self.qbvh
84 .traverse_best_first(&mut visitor)
85 .map(|(_, (_, toi))| toi)
86 }
87
88 #[inline]
89 fn cast_local_ray_and_get_normal(
90 &self,
91 ray: &Ray,
92 max_time_of_impact: f32,
93 solid: bool,
94 ) -> Option<RayIntersection> {
95 let mut visitor = RayCompositeShapeToiAndNormalBestFirstVisitor::new(
96 self,
97 ray,
98 max_time_of_impact,
99 solid,
100 );
101
102 self.qbvh
103 .traverse_best_first(&mut visitor)
104 .map(|(_, (_, res))| res)
105 }
106}
107
108impl TypedSimdCompositeShape for MeshGraph {
109 type PartShape = Triangle;
110 type PartNormalConstraints = (); type PartId = Face;
112
113 #[inline(always)]
114 fn map_typed_part_at(
115 &self,
116 face: Face,
117 mut f: impl FnMut(
118 Option<&Isometry<f32>>,
119 &Self::PartShape,
120 Option<&Self::PartNormalConstraints>,
121 ),
122 ) {
123 let tri = self.triangle(face);
124 let pseudo_normals = None; f(None, &tri, pseudo_normals.as_ref())
126 }
127
128 #[inline(always)]
129 fn map_untyped_part_at(
130 &self,
131 face: Face,
132 mut f: impl FnMut(Option<&Isometry<f32>>, &dyn Shape, Option<&dyn NormalConstraints>),
133 ) {
134 let tri = self.triangle(face);
135 let pseudo_normals = Some(()); f(
137 None,
138 &tri,
139 pseudo_normals.as_ref().map(|n| n as &dyn NormalConstraints),
140 )
141 }
142
143 fn typed_qbvh(&self) -> &Qbvh<Face> {
144 &self.qbvh
145 }
146}
147
148impl MeshGraph {
149 pub fn triangle(&self, face: Face) -> Triangle {
150 let pos = face
151 .vertices(self)
152 .into_iter()
153 .map(|v_id| self.positions[v_id])
154 .collect_vec();
155
156 Triangle::new(
157 Point::new(pos[0].x, pos[0].y, pos[0].z),
158 Point::new(pos[1].x, pos[1].y, pos[1].z),
159 Point::new(pos[2].x, pos[2].y, pos[2].z),
160 )
161 }
162
163 }