mesh_graph/ops/
query.rs

1use itertools::Itertools;
2use parry3d::{
3    math::{Isometry, Point, Vector},
4    partitioning::Bvh,
5    query::{
6        PointProjection, PointQuery, PointQueryWithLocation, Ray, RayCast, RayIntersection,
7        details::NormalConstraints,
8    },
9    shape::{CompositeShape, CompositeShapeRef, FeatureId, Shape, Triangle, TypedCompositeShape},
10};
11use tracing::instrument;
12
13use crate::{Face, MeshGraph, error_none, utils::unwrap_or_return};
14
15impl PointQuery for MeshGraph {
16    #[inline]
17    fn project_local_point(&self, point: &Point<f32>, solid: bool) -> PointProjection {
18        self.project_local_point_and_get_location(point, solid).0
19    }
20
21    fn project_local_point_and_get_feature(
22        &self,
23        _point: &Point<f32>,
24    ) -> (PointProjection, FeatureId) {
25        unimplemented!("Not available")
26    }
27}
28
29impl PointQueryWithLocation for MeshGraph {
30    type Location = Face;
31
32    #[inline]
33    fn project_local_point_and_get_location(
34        &self,
35        point: &Point<f32>,
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    fn project_local_point_and_get_location_with_max_dist(
44        &self,
45        point: &Point<f32>,
46        solid: bool,
47        max_dist: f32,
48    ) -> Option<(PointProjection, Self::Location)> {
49        let (shape_id, (mut proj, _)) =
50            CompositeShapeRef(self).project_local_point_and_get_location(point, max_dist, solid)?;
51
52        // TODO : this could be more precise by interpolating the normal depending on the hit location
53
54        let face_id = self
55            .index_to_face_id
56            .get(shape_id as usize)
57            .or_else(error_none!("Face not found"))?;
58        let face = self
59            .faces
60            .get(*face_id)
61            .or_else(error_none!("Face not found"))?;
62
63        let vertex_normals = self.vertex_normals.as_ref()?;
64        let he = self
65            .halfedges
66            .get(face.halfedge)
67            .or_else(error_none!("Halfedge not found"))?;
68        let pseudo_normal = vertex_normals
69            .get(he.end_vertex)
70            .or_else(error_none!("Vertex normal not found"))?;
71
72        let dpt = point - proj.point;
73        proj.is_inside = dpt.dot(&Vector::new(
74            pseudo_normal.x,
75            pseudo_normal.y,
76            pseudo_normal.z,
77        )) <= 0.0;
78
79        Some((proj, *face))
80    }
81}
82
83impl RayCast for MeshGraph {
84    #[inline]
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    fn cast_local_ray_and_get_normal(
93        &self,
94        ray: &Ray,
95        max_time_of_impact: f32,
96        solid: bool,
97    ) -> Option<RayIntersection> {
98        CompositeShapeRef(self)
99            .cast_local_ray_and_get_normal(ray, max_time_of_impact, solid)
100            .map(|(_, res)| res)
101    }
102}
103
104impl CompositeShape for MeshGraph {
105    fn map_part_at(
106        &self,
107        shape_id: u32,
108        f: &mut dyn FnMut(Option<&Isometry<f32>>, &dyn Shape, Option<&dyn NormalConstraints>),
109    ) {
110        let tri = self.triangle(shape_id);
111        let normal_constraints = Default::default(); // self.triangle_normal_constraints(face_id);
112        f(None, &tri, normal_constraints)
113    }
114
115    fn bvh(&self) -> &Bvh {
116        &self.bvh
117    }
118}
119
120impl TypedCompositeShape for MeshGraph {
121    type PartShape = Triangle;
122    type PartNormalConstraints = ();
123
124    fn map_typed_part_at<T>(
125        &self,
126        shape_id: u32,
127        mut f: impl FnMut(
128            Option<&Isometry<f32>>,
129            &Self::PartShape,
130            Option<&Self::PartNormalConstraints>,
131        ) -> T,
132    ) -> Option<T> {
133        let tri = self.triangle(shape_id);
134        let pseudo_normals = None; // self.triangle_normal_constraints(face_id);
135        Some(f(None, &tri, pseudo_normals.as_ref()))
136    }
137
138    fn map_untyped_part_at<T>(
139        &self,
140        shape_id: u32,
141        mut f: impl FnMut(Option<&Isometry<f32>>, &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 as usize),
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            Point::new(pos[0].x, pos[0].y, pos[0].z),
179            Point::new(pos[1].x, pos[1].y, pos[1].z),
180            Point::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}