parry3d_f64/shape/
polyline.rs1use crate::bounding_volume::Aabb;
2use crate::math::{Isometry, Point, Real, Vector};
3use crate::partitioning::Qbvh;
4use crate::query::{PointProjection, PointQueryWithLocation};
5use crate::shape::composite_shape::SimdCompositeShape;
6use crate::shape::{FeatureId, Segment, SegmentPointLocation, Shape, TypedSimdCompositeShape};
7#[cfg(feature = "alloc")]
8use alloc::vec::Vec;
9
10use crate::query::details::NormalConstraints;
11
12#[derive(Clone, Debug)]
13#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
14#[cfg_attr(
15 feature = "rkyv",
16 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
17 archive(check_bytes)
18)]
19pub struct Polyline {
21 qbvh: Qbvh<u32>,
22 vertices: Vec<Point<Real>>,
23 indices: Vec<[u32; 2]>,
24}
25
26impl Polyline {
27 pub fn new(vertices: Vec<Point<Real>>, indices: Option<Vec<[u32; 2]>>) -> Self {
29 let indices =
30 indices.unwrap_or_else(|| (0..vertices.len() as u32 - 1).map(|i| [i, i + 1]).collect());
31 let data = indices.iter().enumerate().map(|(i, idx)| {
32 let aabb =
33 Segment::new(vertices[idx[0] as usize], vertices[idx[1] as usize]).local_aabb();
34 (i as u32, aabb)
35 });
36
37 let mut qbvh = Qbvh::new();
38 qbvh.clear_and_rebuild(data, 0.0);
41
42 Self {
43 qbvh,
44 vertices,
45 indices,
46 }
47 }
48
49 pub fn aabb(&self, pos: &Isometry<Real>) -> Aabb {
51 self.qbvh.root_aabb().transform_by(pos)
52 }
53
54 pub fn local_aabb(&self) -> &Aabb {
56 self.qbvh.root_aabb()
57 }
58
59 pub(crate) fn qbvh(&self) -> &Qbvh<u32> {
60 &self.qbvh
61 }
62
63 pub fn num_segments(&self) -> usize {
65 self.indices.len()
66 }
67
68 pub fn segments(&self) -> impl ExactSizeIterator<Item = Segment> + '_ {
70 self.indices.iter().map(move |ids| {
71 Segment::new(
72 self.vertices[ids[0] as usize],
73 self.vertices[ids[1] as usize],
74 )
75 })
76 }
77
78 pub fn segment(&self, i: u32) -> Segment {
80 let idx = self.indices[i as usize];
81 Segment::new(
82 self.vertices[idx[0] as usize],
83 self.vertices[idx[1] as usize],
84 )
85 }
86
87 pub fn segment_feature_to_polyline_feature(
89 &self,
90 segment: u32,
91 _feature: FeatureId,
92 ) -> FeatureId {
93 #[cfg(feature = "dim2")]
95 return FeatureId::Face(segment);
96 #[cfg(feature = "dim3")]
97 return FeatureId::Edge(segment);
98 }
99
100 pub fn vertices(&self) -> &[Point<Real>] {
102 &self.vertices[..]
103 }
104
105 pub fn indices(&self) -> &[[u32; 2]] {
107 &self.indices
108 }
109
110 pub fn flat_indices(&self) -> &[u32] {
112 unsafe {
113 let len = self.indices.len() * 2;
114 let data = self.indices.as_ptr() as *const u32;
115 core::slice::from_raw_parts(data, len)
116 }
117 }
118
119 pub fn scaled(mut self, scale: &Vector<Real>) -> Self {
121 self.vertices
122 .iter_mut()
123 .for_each(|pt| pt.coords.component_mul_assign(scale));
124 Self {
125 qbvh: self.qbvh.scaled(scale),
126 vertices: self.vertices,
127 indices: self.indices,
128 }
129 }
130
131 pub fn reverse(&mut self) {
134 for idx in &mut self.indices {
135 idx.swap(0, 1);
136 }
137
138 self.indices.reverse();
139
140 for (_, seg_id) in self.qbvh.iter_data_mut() {
143 *seg_id = self.indices.len() as u32 - *seg_id - 1;
144 }
145 }
146
147 pub fn extract_connected_components(&self) -> Vec<Polyline> {
169 let vertices = self.vertices();
170 let indices = self.indices();
171
172 if indices.is_empty() {
173 Vec::new()
175 } else {
176 let mut components = Vec::new();
177
178 let mut start_i = 0; let mut start_node = indices[0][0]; let mut component_vertices = Vec::new();
182 let mut component_indices: Vec<[u32; 2]> = Vec::new();
183
184 for (i, idx) in indices.iter().enumerate() {
186 component_vertices.push(vertices[idx[0] as usize]);
187
188 if idx[1] != start_node {
189 component_indices.push([(i - start_i) as u32, (i - start_i + 1) as u32]);
191 } else {
192 component_indices.push([(i - start_i) as u32, 0]);
194 components.push(Polyline::new(
195 core::mem::take(&mut component_vertices),
196 Some(core::mem::take(&mut component_indices)),
197 ));
198
199 if i + 1 < indices.len() {
200 start_node = indices[i + 1][0];
202 start_i = i + 1;
203 }
204 }
205 }
206
207 components
208 }
209 }
210
211 pub fn project_local_point_assuming_solid_interior_ccw(
225 &self,
226 point: Point<Real>,
227 #[cfg(feature = "dim3")] axis: u8,
228 ) -> (PointProjection, (u32, SegmentPointLocation)) {
229 let mut proj = self.project_local_point_and_get_location(&point, false);
230 let segment1 = self.segment((proj.1).0);
231
232 #[cfg(feature = "dim2")]
233 let normal1 = segment1.normal();
234 #[cfg(feature = "dim3")]
235 let normal1 = segment1.planar_normal(axis);
236
237 if let Some(normal1) = normal1 {
238 proj.0.is_inside = match proj.1 .1 {
239 SegmentPointLocation::OnVertex(i) => {
240 let dir2 = if i == 0 {
241 let adj_seg = if proj.1 .0 == 0 {
242 self.indices().len() as u32 - 1
243 } else {
244 proj.1 .0 - 1
245 };
246
247 assert_eq!(segment1.a, self.segment(adj_seg).b);
248 -self.segment(adj_seg).scaled_direction()
249 } else {
250 assert_eq!(i, 1);
251 let adj_seg = (proj.1 .0 + 1) % self.indices().len() as u32;
252 assert_eq!(segment1.b, self.segment(adj_seg).a);
253
254 self.segment(adj_seg).scaled_direction()
255 };
256
257 let dot = normal1.dot(&dir2);
258 let threshold = 1.0e-3 * dir2.norm();
264 if dot.abs() > threshold {
265 dot >= 0.0
268 } else {
269 (point - proj.0.point).dot(&normal1) <= 0.0
272 }
273 }
274 SegmentPointLocation::OnEdge(_) => (point - proj.0.point).dot(&normal1) <= 0.0,
275 };
276 }
277
278 proj
279 }
280}
281
282impl SimdCompositeShape for Polyline {
283 fn map_part_at(
284 &self,
285 i: u32,
286 f: &mut dyn FnMut(Option<&Isometry<Real>>, &dyn Shape, Option<&dyn NormalConstraints>),
287 ) {
288 let tri = self.segment(i);
289 f(None, &tri, None)
290 }
291
292 fn qbvh(&self) -> &Qbvh<u32> {
293 &self.qbvh
294 }
295}
296
297impl TypedSimdCompositeShape for Polyline {
298 type PartShape = Segment;
299 type PartNormalConstraints = ();
300 type PartId = u32;
301
302 #[inline(always)]
303 fn map_typed_part_at(
304 &self,
305 i: u32,
306 mut f: impl FnMut(
307 Option<&Isometry<Real>>,
308 &Self::PartShape,
309 Option<&Self::PartNormalConstraints>,
310 ),
311 ) {
312 let seg = self.segment(i);
313 f(None, &seg, None)
314 }
315
316 #[inline(always)]
317 fn map_untyped_part_at(
318 &self,
319 i: u32,
320 mut f: impl FnMut(Option<&Isometry<Real>>, &dyn Shape, Option<&dyn NormalConstraints>),
321 ) {
322 let seg = self.segment(i);
323 f(None, &seg, None)
324 }
325
326 fn typed_qbvh(&self) -> &Qbvh<u32> {
327 &self.qbvh
328 }
329}