Skip to main content

mesh_graph/ops/
query.rs

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        unimplemented!("Not available")
25    }
26}
27
28impl PointQueryWithLocation for MeshGraph {
29    type Location = Face;
30
31    #[inline]
32    #[instrument(skip(self))]
33    fn project_local_point_and_get_location(
34        &self,
35        point: Vec3,
36        solid: bool,
37    ) -> (PointProjection, Self::Location) {
38        self.project_local_point_and_get_location_with_max_dist(point, solid, f32::MAX)
39            .unwrap()
40    }
41
42    /// Projects a point on `self`, with a maximum projection distance.
43    #[instrument(skip(self))]
44    fn project_local_point_and_get_location_with_max_dist(
45        &self,
46        point: Vec3,
47        solid: bool,
48        max_dist: f32,
49    ) -> Option<(PointProjection, Self::Location)> {
50        let (shape_id, (mut proj, _)) =
51            CompositeShapeRef(self).project_local_point_and_get_location(point, max_dist, solid)?;
52
53        // TODO : this could be more precise by interpolating the normal depending on the hit location
54
55        let face_id = self
56            .index_to_face_id
57            .get(&shape_id)
58            .or_else(error_none!("Face not found"))?;
59        let face = self
60            .faces
61            .get(*face_id)
62            .or_else(error_none!("Face not found"))?;
63
64        if let Some(vertex_normals) = self.vertex_normals.as_ref() {
65            let he = self
66                .halfedges
67                .get(face.halfedge)
68                .or_else(error_none!("Halfedge not found"))?;
69            let pseudo_normal = vertex_normals
70                .get(he.end_vertex)
71                .or_else(error_none!("Vertex normal not found"))?;
72
73            let dpt = point - proj.point;
74            proj.is_inside =
75                dpt.dot(Vec3::new(pseudo_normal.x, pseudo_normal.y, pseudo_normal.z)) <= 0.0;
76        }
77
78        Some((proj, *face))
79    }
80}
81
82impl RayCast for MeshGraph {
83    #[inline]
84    #[instrument(skip(self))]
85    fn cast_local_ray(&self, ray: &Ray, max_time_of_impact: f32, solid: bool) -> Option<f32> {
86        CompositeShapeRef(self)
87            .cast_local_ray(ray, max_time_of_impact, solid)
88            .map(|hit| hit.1)
89    }
90
91    #[inline]
92    #[instrument(skip(self))]
93    fn cast_local_ray_and_get_normal(
94        &self,
95        ray: &Ray,
96        max_time_of_impact: f32,
97        solid: bool,
98    ) -> Option<RayIntersection> {
99        CompositeShapeRef(self)
100            .cast_local_ray_and_get_normal(ray, max_time_of_impact, solid)
101            .map(|(_, res)| res)
102    }
103}
104
105impl CompositeShape for MeshGraph {
106    #[instrument(skip(self, f))]
107    fn map_part_at(
108        &self,
109        shape_id: u32,
110        f: &mut dyn FnMut(Option<&Pose>, &dyn Shape, Option<&dyn NormalConstraints>),
111    ) {
112        let tri = self.triangle(shape_id);
113        let normal_constraints = Default::default(); // self.triangle_normal_constraints(face_id);
114        f(None, &tri, normal_constraints)
115    }
116
117    fn bvh(&self) -> &Bvh {
118        &self.bvh
119    }
120}
121
122impl TypedCompositeShape for MeshGraph {
123    type PartShape = Triangle;
124    type PartNormalConstraints = ();
125
126    #[instrument(skip(self, f))]
127    fn map_typed_part_at<T>(
128        &self,
129        shape_id: u32,
130        mut f: impl FnMut(Option<&Pose>, &Self::PartShape, Option<&Self::PartNormalConstraints>) -> T,
131    ) -> Option<T> {
132        let tri = self.triangle(shape_id);
133        let pseudo_normals = None; // self.triangle_normal_constraints(face_id);
134        Some(f(None, &tri, pseudo_normals.as_ref()))
135    }
136
137    #[instrument(skip(self, f))]
138    fn map_untyped_part_at<T>(
139        &self,
140        shape_id: u32,
141        mut f: impl FnMut(Option<&Pose>, &dyn Shape, Option<&dyn NormalConstraints>) -> T,
142    ) -> Option<T> {
143        let tri = self.triangle(shape_id);
144        let pseudo_normals = Default::default(); // self.triangle_normal_constraints(face_id);
145        Some(f(None, &tri, pseudo_normals))
146    }
147}
148
149impl MeshGraph {
150    #[instrument(skip(self))]
151    pub fn triangle(&self, shape_id: u32) -> Triangle {
152        let face_id = unwrap_or_return!(
153            self.index_to_face_id.get(&shape_id),
154            "Index not found",
155            Triangle::default()
156        );
157
158        let face = unwrap_or_return!(
159            self.faces.get(*face_id),
160            "Face not found",
161            Triangle::default()
162        );
163
164        let pos = face
165            .vertices(self)
166            .filter_map(|v_id| {
167                self.positions
168                    .get(v_id)
169                    .or_else(error_none!("Position not found"))
170            })
171            .collect_vec();
172
173        if pos.len() < 3 {
174            return Triangle::default();
175        }
176
177        Triangle::new(
178            Vec3::new(pos[0].x, pos[0].y, pos[0].z),
179            Vec3::new(pos[1].x, pos[1].y, pos[1].z),
180            Vec3::new(pos[2].x, pos[2].y, pos[2].z),
181        )
182    }
183
184    // TODO : is this necessary?
185    // pub fn triangle_normal_constraints(&self, face_id: FaceId) -> Option<TrianglePseudoNormals> {
186    //     if let Some(vertex_normals) = &self.vertex_normals {
187    //         let triangle = self.triangle(face_id);
188    //         let pseudo_normals = self.pseudo_normals.as_ref()?;
189    //         let edges_pseudo_normals = pseudo_normals.edges_pseudo_normal[i as usize];
190
191    //         // TODO: could the pseudo-normal be pre-normalized instead of having to renormalize
192    //         //       every time we need them?
193    //         Some(TrianglePseudoNormals {
194    //             face: triangle.normal()?,
195    //             edges: [
196    //                 Unit::try_new(edges_pseudo_normals[0], 1.0e-6)?,
197    //                 Unit::try_new(edges_pseudo_normals[1], 1.0e-6)?,
198    //                 Unit::try_new(edges_pseudo_normals[2], 1.0e-6)?,
199    //             ],
200    //         })
201    //     } else {
202    //         None
203    //     }
204    // }
205}