1use glam::Vec3;
2use itertools::Itertools;
3use parry3d::{
4 math::Pose,
5 partitioning::Bvh,
6 query::{
7 PointProjection, PointQuery, PointQueryWithLocation, Ray, RayCast, RayIntersection,
8 details::NormalConstraints,
9 },
10 shape::{CompositeShape, CompositeShapeRef, FeatureId, Shape, Triangle, TypedCompositeShape},
11};
12use tracing::instrument;
13
14use crate::{Face, MeshGraph, error_none, utils::unwrap_or_return};
15
16impl PointQuery for MeshGraph {
17 #[inline]
18 #[instrument(skip(self))]
19 fn project_local_point(&self, point: Vec3, solid: bool) -> PointProjection {
20 self.project_local_point_and_get_location(point, solid).0
21 }
22
23 fn project_local_point_and_get_feature(&self, point: Vec3) -> (PointProjection, FeatureId) {
24 let (proj, face) = self.project_local_point_and_get_location(point, false);
25 (proj, FeatureId::Face(face.index))
26 }
27}
28
29impl PointQueryWithLocation for MeshGraph {
30 type Location = Face;
31
32 #[inline]
33 #[instrument(skip(self))]
34 fn project_local_point_and_get_location(
35 &self,
36 point: Vec3,
37 solid: bool,
38 ) -> (PointProjection, Self::Location) {
39 self.project_local_point_and_get_location_with_max_dist(point, solid, f32::MAX)
40 .unwrap()
41 }
42
43 #[instrument(skip(self))]
45 fn project_local_point_and_get_location_with_max_dist(
46 &self,
47 point: Vec3,
48 solid: bool,
49 max_dist: f32,
50 ) -> Option<(PointProjection, Self::Location)> {
51 let (shape_id, (mut proj, _)) =
52 CompositeShapeRef(self).project_local_point_and_get_location(point, max_dist, solid)?;
53
54 let face_id = self
57 .index_to_face_id
58 .get(&shape_id)
59 .or_else(error_none!("Face not found"))?;
60 let face = self
61 .faces
62 .get(*face_id)
63 .or_else(error_none!("Face not found"))?;
64
65 if let Some(vertex_normals) = self.vertex_normals.as_ref() {
66 let he = self
67 .halfedges
68 .get(face.halfedge)
69 .or_else(error_none!("Halfedge not found"))?;
70 let pseudo_normal = vertex_normals
71 .get(he.end_vertex)
72 .or_else(error_none!("Vertex normal not found"))?;
73
74 let dpt = point - proj.point;
75 proj.is_inside =
76 dpt.dot(Vec3::new(pseudo_normal.x, pseudo_normal.y, pseudo_normal.z)) <= 0.0;
77 }
78
79 Some((proj, *face))
80 }
81}
82
83impl RayCast for MeshGraph {
84 #[inline]
85 #[instrument(skip(self))]
86 fn cast_local_ray(&self, ray: &Ray, max_time_of_impact: f32, solid: bool) -> Option<f32> {
87 CompositeShapeRef(self)
88 .cast_local_ray(ray, max_time_of_impact, solid)
89 .map(|hit| hit.1)
90 }
91
92 #[inline]
93 #[instrument(skip(self))]
94 fn cast_local_ray_and_get_normal(
95 &self,
96 ray: &Ray,
97 max_time_of_impact: f32,
98 solid: bool,
99 ) -> Option<RayIntersection> {
100 CompositeShapeRef(self)
101 .cast_local_ray_and_get_normal(ray, max_time_of_impact, solid)
102 .map(|(_, res)| res)
103 }
104}
105
106impl CompositeShape for MeshGraph {
107 #[instrument(skip(self, f))]
108 fn map_part_at(
109 &self,
110 shape_id: u32,
111 f: &mut dyn FnMut(Option<&Pose>, &dyn Shape, Option<&dyn NormalConstraints>),
112 ) {
113 let tri = self.triangle(shape_id);
114 let normal_constraints = Default::default(); f(None, &tri, normal_constraints)
116 }
117
118 fn bvh(&self) -> &Bvh {
119 &self.bvh
120 }
121}
122
123impl TypedCompositeShape for MeshGraph {
124 type PartShape = Triangle;
125 type PartNormalConstraints = ();
126
127 #[instrument(skip(self, f))]
128 fn map_typed_part_at<T>(
129 &self,
130 shape_id: u32,
131 mut f: impl FnMut(Option<&Pose>, &Self::PartShape, Option<&Self::PartNormalConstraints>) -> T,
132 ) -> Option<T> {
133 let tri = self.triangle(shape_id);
134 let pseudo_normals = None; Some(f(None, &tri, pseudo_normals.as_ref()))
136 }
137
138 #[instrument(skip(self, f))]
139 fn map_untyped_part_at<T>(
140 &self,
141 shape_id: u32,
142 mut f: impl FnMut(Option<&Pose>, &dyn Shape, Option<&dyn NormalConstraints>) -> T,
143 ) -> Option<T> {
144 let tri = self.triangle(shape_id);
145 let pseudo_normals = Default::default(); Some(f(None, &tri, pseudo_normals))
147 }
148}
149
150impl MeshGraph {
151 #[instrument(skip(self))]
152 pub fn triangle(&self, shape_id: u32) -> Triangle {
153 let face_id = unwrap_or_return!(
154 self.index_to_face_id.get(&shape_id),
155 "Index not found",
156 Triangle::default()
157 );
158
159 let face = unwrap_or_return!(
160 self.faces.get(*face_id),
161 "Face not found",
162 Triangle::default()
163 );
164
165 let pos = face
166 .vertices(self)
167 .filter_map(|v_id| {
168 self.positions
169 .get(v_id)
170 .or_else(error_none!("Position not found"))
171 })
172 .collect_vec();
173
174 if pos.len() < 3 {
175 return Triangle::default();
176 }
177
178 Triangle::new(
179 Vec3::new(pos[0].x, pos[0].y, pos[0].z),
180 Vec3::new(pos[1].x, pos[1].y, pos[1].z),
181 Vec3::new(pos[2].x, pos[2].y, pos[2].z),
182 )
183 }
184
185 }