parry2d/shape/polyline.rs
1use crate::bounding_volume::Aabb;
2use crate::math::{Isometry, Point, Real, Vector};
3use crate::partitioning::{Bvh, BvhBuildStrategy};
4use crate::query::{PointProjection, PointQueryWithLocation};
5use crate::shape::composite_shape::CompositeShape;
6use crate::shape::{FeatureId, Segment, SegmentPointLocation, Shape, TypedCompositeShape};
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)]
19/// A polyline shape formed by connected line segments.
20///
21/// A polyline is a sequence of line segments (edges) connecting vertices. It can be open
22/// (not forming a closed loop) or closed (where the last vertex connects back to the first).
23/// Polylines are commonly used for paths, boundaries, and 2D/3D curves.
24///
25/// # Structure
26///
27/// A polyline consists of:
28/// - **Vertices**: Points in 2D or 3D space
29/// - **Indices**: Pairs of vertex indices defining each segment
30/// - **BVH**: Bounding Volume Hierarchy for fast spatial queries
31///
32/// # Properties
33///
34/// - **Composite shape**: Made up of multiple segments
35/// - **1-dimensional**: Has length but no volume
36/// - **Flexible topology**: Can be open or closed, branching or linear
37/// - **Accelerated queries**: Uses BVH for efficient collision detection
38///
39/// # Use Cases
40///
41/// Polylines are ideal for:
42/// - **Paths and roads**: Navigation paths, road networks
43/// - **Terrain boundaries**: Cliff edges, coastlines, level boundaries
44/// - **Outlines**: 2D shape outlines, contours
45/// - **Wire frames**: Simplified representations of complex shapes
46/// - **Motion paths**: Character movement paths, camera rails
47///
48/// # Example
49///
50/// ```rust
51/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
52/// use parry3d::shape::Polyline;
53/// use nalgebra::Point3;
54///
55/// // Create a simple L-shaped polyline
56/// let vertices = vec![
57/// Point3::origin(),
58/// Point3::new(1.0, 0.0, 0.0),
59/// Point3::new(1.0, 1.0, 0.0),
60/// ];
61///
62/// // Indices are automatically generated to connect consecutive vertices
63/// let polyline = Polyline::new(vertices, None);
64///
65/// // The polyline has 2 segments: (0,1) and (1,2)
66/// assert_eq!(polyline.num_segments(), 2);
67/// assert_eq!(polyline.vertices().len(), 3);
68/// # }
69/// ```
70///
71/// # Custom Connectivity
72///
73/// You can provide custom indices to create non-sequential connections:
74///
75/// ```rust
76/// # #[cfg(all(feature = "dim2", feature = "f32"))] {
77/// use parry2d::shape::Polyline;
78/// use nalgebra::Point2;
79///
80/// // Create a triangle polyline (closed loop)
81/// let vertices = vec![
82/// Point2::origin(),
83/// Point2::new(1.0, 0.0),
84/// Point2::new(0.5, 1.0),
85/// ];
86///
87/// // Manually specify edges to create a closed triangle
88/// let indices = vec![
89/// [0, 1], // Bottom edge
90/// [1, 2], // Right edge
91/// [2, 0], // Left edge (closes the loop)
92/// ];
93///
94/// let polyline = Polyline::new(vertices, Some(indices));
95/// assert_eq!(polyline.num_segments(), 3);
96/// # }
97/// ```
98pub struct Polyline {
99 bvh: Bvh,
100 vertices: Vec<Point<Real>>,
101 indices: Vec<[u32; 2]>,
102}
103
104impl Polyline {
105 /// Creates a new polyline from a vertex buffer and an optional index buffer.
106 ///
107 /// This is the main constructor for creating a polyline. If no indices are provided,
108 /// the vertices will be automatically connected in sequence (vertex 0 to 1, 1 to 2, etc.).
109 ///
110 /// # Arguments
111 ///
112 /// * `vertices` - A vector of points defining the polyline vertices
113 /// * `indices` - Optional vector of `[u32; 2]` pairs defining which vertices connect.
114 /// If `None`, vertices are connected sequentially.
115 ///
116 /// # Returns
117 ///
118 /// A new `Polyline` with an internal BVH for accelerated queries.
119 ///
120 /// # Example
121 ///
122 /// ```
123 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
124 /// use parry3d::shape::Polyline;
125 /// use nalgebra::Point3;
126 ///
127 /// // Create a zigzag path with automatic sequential connections
128 /// let vertices = vec![
129 /// Point3::origin(),
130 /// Point3::new(1.0, 1.0, 0.0),
131 /// Point3::new(2.0, 0.0, 0.0),
132 /// Point3::new(3.0, 1.0, 0.0),
133 /// ];
134 /// let polyline = Polyline::new(vertices, None);
135 /// assert_eq!(polyline.num_segments(), 3);
136 /// # }
137 /// ```
138 ///
139 /// # Custom Connectivity Example
140 ///
141 /// ```
142 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
143 /// use parry2d::shape::Polyline;
144 /// use nalgebra::Point2;
145 ///
146 /// // Create a square with custom indices
147 /// let vertices = vec![
148 /// Point2::origin(),
149 /// Point2::new(1.0, 0.0),
150 /// Point2::new(1.0, 1.0),
151 /// Point2::new(0.0, 1.0),
152 /// ];
153 ///
154 /// // Define edges to form a closed square
155 /// let indices = vec![
156 /// [0, 1], [1, 2], [2, 3], [3, 0]
157 /// ];
158 ///
159 /// let square = Polyline::new(vertices, Some(indices));
160 /// assert_eq!(square.num_segments(), 4);
161 ///
162 /// // Each segment connects the correct vertices
163 /// let first_segment = square.segment(0);
164 /// assert_eq!(first_segment.a, Point2::origin());
165 /// assert_eq!(first_segment.b, Point2::new(1.0, 0.0));
166 /// # }
167 /// ```
168 pub fn new(vertices: Vec<Point<Real>>, indices: Option<Vec<[u32; 2]>>) -> Self {
169 let indices =
170 indices.unwrap_or_else(|| (0..vertices.len() as u32 - 1).map(|i| [i, i + 1]).collect());
171 let leaves = indices.iter().enumerate().map(|(i, idx)| {
172 let aabb =
173 Segment::new(vertices[idx[0] as usize], vertices[idx[1] as usize]).local_aabb();
174 (i, aabb)
175 });
176
177 // NOTE: we apply no dilation factor because we won't
178 // update this tree dynamically.
179 let bvh = Bvh::from_iter(BvhBuildStrategy::Binned, leaves);
180
181 Self {
182 bvh,
183 vertices,
184 indices,
185 }
186 }
187
188 /// Computes the axis-aligned bounding box of this polyline in world space.
189 ///
190 /// The AABB is the smallest box aligned with the world axes that fully contains
191 /// the polyline after applying the given position/rotation transformation.
192 ///
193 /// # Arguments
194 ///
195 /// * `pos` - The position and orientation (isometry) of the polyline in world space
196 ///
197 /// # Returns
198 ///
199 /// An `Aabb` that bounds the transformed polyline
200 ///
201 /// # Example
202 ///
203 /// ```
204 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
205 /// use parry3d::shape::Polyline;
206 /// use nalgebra::{Point3, Isometry3, Translation3};
207 ///
208 /// // Create a polyline along the X axis
209 /// let vertices = vec![
210 /// Point3::origin(),
211 /// Point3::new(2.0, 0.0, 0.0),
212 /// ];
213 /// let polyline = Polyline::new(vertices, None);
214 ///
215 /// // Compute AABB at the origin
216 /// let identity = Isometry3::identity();
217 /// let aabb = polyline.aabb(&identity);
218 /// assert_eq!(aabb.mins.x, 0.0);
219 /// assert_eq!(aabb.maxs.x, 2.0);
220 ///
221 /// // Compute AABB after translating by (10, 5, 0)
222 /// let translated = Isometry3::from_parts(
223 /// Translation3::new(10.0, 5.0, 0.0),
224 /// nalgebra::UnitQuaternion::identity()
225 /// );
226 /// let aabb_translated = polyline.aabb(&translated);
227 /// assert_eq!(aabb_translated.mins.x, 10.0);
228 /// assert_eq!(aabb_translated.maxs.x, 12.0);
229 /// # }
230 /// ```
231 pub fn aabb(&self, pos: &Isometry<Real>) -> Aabb {
232 self.bvh.root_aabb().transform_by(pos)
233 }
234
235 /// Gets the local axis-aligned bounding box of this polyline.
236 ///
237 /// This returns the AABB in the polyline's local coordinate system (before any
238 /// transformation is applied). It's more efficient than `aabb()` when you don't
239 /// need to transform the polyline.
240 ///
241 /// # Returns
242 ///
243 /// An `Aabb` that bounds the polyline in local space
244 ///
245 /// # Example
246 ///
247 /// ```
248 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
249 /// use parry2d::shape::Polyline;
250 /// use nalgebra::Point2;
251 ///
252 /// // Create a rectangular polyline
253 /// let vertices = vec![
254 /// Point2::new(-1.0, -2.0),
255 /// Point2::new(3.0, -2.0),
256 /// Point2::new(3.0, 4.0),
257 /// Point2::new(-1.0, 4.0),
258 /// ];
259 /// let polyline = Polyline::new(vertices, None);
260 ///
261 /// // Get the local AABB
262 /// let aabb = polyline.local_aabb();
263 ///
264 /// // The AABB should contain all vertices
265 /// assert_eq!(aabb.mins.x, -1.0);
266 /// assert_eq!(aabb.mins.y, -2.0);
267 /// assert_eq!(aabb.maxs.x, 3.0);
268 /// assert_eq!(aabb.maxs.y, 4.0);
269 /// # }
270 /// ```
271 pub fn local_aabb(&self) -> Aabb {
272 self.bvh.root_aabb()
273 }
274
275 /// The BVH acceleration structure for this polyline.
276 pub fn bvh(&self) -> &Bvh {
277 &self.bvh
278 }
279
280 /// Returns the number of segments (edges) in this polyline.
281 ///
282 /// Each segment connects two vertices. For a polyline with `n` vertices and
283 /// sequential connectivity, there are `n-1` segments. For custom connectivity,
284 /// the number of segments equals the number of index pairs.
285 ///
286 /// # Returns
287 ///
288 /// The total number of line segments
289 ///
290 /// # Example
291 ///
292 /// ```
293 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
294 /// use parry3d::shape::Polyline;
295 /// use nalgebra::Point3;
296 ///
297 /// // Sequential polyline: 5 vertices -> 4 segments
298 /// let vertices = vec![
299 /// Point3::origin(),
300 /// Point3::new(1.0, 0.0, 0.0),
301 /// Point3::new(2.0, 0.0, 0.0),
302 /// Point3::new(3.0, 0.0, 0.0),
303 /// Point3::new(4.0, 0.0, 0.0),
304 /// ];
305 /// let polyline = Polyline::new(vertices.clone(), None);
306 /// assert_eq!(polyline.num_segments(), 4);
307 ///
308 /// // Custom connectivity: can have different number of segments
309 /// let indices = vec![[0, 4], [1, 3]]; // Only 2 segments
310 /// let custom = Polyline::new(vertices, Some(indices));
311 /// assert_eq!(custom.num_segments(), 2);
312 /// # }
313 /// ```
314 pub fn num_segments(&self) -> usize {
315 self.indices.len()
316 }
317
318 /// Returns an iterator over all segments in this polyline.
319 ///
320 /// Each segment is returned as a [`Segment`] object with two endpoints.
321 /// The iterator yields exactly `num_segments()` items.
322 ///
323 /// # Returns
324 ///
325 /// An exact-size iterator that yields `Segment` instances
326 ///
327 /// # Example
328 ///
329 /// ```
330 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
331 /// use parry2d::shape::Polyline;
332 /// use nalgebra::Point2;
333 ///
334 /// // Create a triangle
335 /// let vertices = vec![
336 /// Point2::origin(),
337 /// Point2::new(1.0, 0.0),
338 /// Point2::new(0.5, 1.0),
339 /// ];
340 /// let polyline = Polyline::new(vertices, None);
341 ///
342 /// // Iterate over all segments
343 /// let mut total_length = 0.0;
344 /// for segment in polyline.segments() {
345 /// total_length += segment.length();
346 /// }
347 ///
348 /// // Calculate expected perimeter (not closed, so 2 sides only)
349 /// assert!(total_length > 2.0);
350 ///
351 /// // Collect all segments into a vector
352 /// let segments: Vec<_> = polyline.segments().collect();
353 /// assert_eq!(segments.len(), 2);
354 /// # }
355 /// ```
356 pub fn segments(&self) -> impl ExactSizeIterator<Item = Segment> + '_ {
357 self.indices.iter().map(move |ids| {
358 Segment::new(
359 self.vertices[ids[0] as usize],
360 self.vertices[ids[1] as usize],
361 )
362 })
363 }
364
365 /// Returns the segment at the given index.
366 ///
367 /// This retrieves a specific segment by its index. Indices range from `0` to
368 /// `num_segments() - 1`. If you need to access multiple segments, consider
369 /// using the `segments()` iterator instead.
370 ///
371 /// # Arguments
372 ///
373 /// * `i` - The index of the segment to retrieve (0-based)
374 ///
375 /// # Returns
376 ///
377 /// A `Segment` representing the edge at index `i`
378 ///
379 /// # Panics
380 ///
381 /// Panics if `i >= num_segments()`
382 ///
383 /// # Example
384 ///
385 /// ```
386 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
387 /// use parry3d::shape::Polyline;
388 /// use nalgebra::Point3;
389 ///
390 /// let vertices = vec![
391 /// Point3::origin(),
392 /// Point3::new(1.0, 0.0, 0.0),
393 /// Point3::new(2.0, 1.0, 0.0),
394 /// ];
395 /// let polyline = Polyline::new(vertices, None);
396 ///
397 /// // Get the first segment (connects vertex 0 to vertex 1)
398 /// let seg0 = polyline.segment(0);
399 /// assert_eq!(seg0.a, Point3::origin());
400 /// assert_eq!(seg0.b, Point3::new(1.0, 0.0, 0.0));
401 /// assert_eq!(seg0.length(), 1.0);
402 ///
403 /// // Get the second segment (connects vertex 1 to vertex 2)
404 /// let seg1 = polyline.segment(1);
405 /// assert_eq!(seg1.a, Point3::new(1.0, 0.0, 0.0));
406 /// assert_eq!(seg1.b, Point3::new(2.0, 1.0, 0.0));
407 /// # }
408 /// ```
409 pub fn segment(&self, i: u32) -> Segment {
410 let idx = self.indices[i as usize];
411 Segment::new(
412 self.vertices[idx[0] as usize],
413 self.vertices[idx[1] as usize],
414 )
415 }
416
417 /// Transforms the feature-id of a segment to the feature-id of this polyline.
418 pub fn segment_feature_to_polyline_feature(
419 &self,
420 segment: u32,
421 _feature: FeatureId,
422 ) -> FeatureId {
423 // TODO: return a vertex feature when it makes sense.
424 #[cfg(feature = "dim2")]
425 return FeatureId::Face(segment);
426 #[cfg(feature = "dim3")]
427 return FeatureId::Edge(segment);
428 }
429
430 /// Returns a slice containing all vertices of this polyline.
431 ///
432 /// Vertices are the points that define the polyline. Segments connect
433 /// pairs of these vertices according to the index buffer.
434 ///
435 /// # Returns
436 ///
437 /// A slice of all vertex points
438 ///
439 /// # Example
440 ///
441 /// ```
442 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
443 /// use parry2d::shape::Polyline;
444 /// use nalgebra::Point2;
445 ///
446 /// let vertices = vec![
447 /// Point2::origin(),
448 /// Point2::new(1.0, 0.0),
449 /// Point2::new(1.0, 1.0),
450 /// ];
451 /// let polyline = Polyline::new(vertices.clone(), None);
452 ///
453 /// // Access all vertices
454 /// let verts = polyline.vertices();
455 /// assert_eq!(verts.len(), 3);
456 /// assert_eq!(verts[0], Point2::origin());
457 /// assert_eq!(verts[1], Point2::new(1.0, 0.0));
458 /// assert_eq!(verts[2], Point2::new(1.0, 1.0));
459 ///
460 /// // You can iterate over vertices
461 /// for (i, vertex) in polyline.vertices().iter().enumerate() {
462 /// println!("Vertex {}: {:?}", i, vertex);
463 /// }
464 /// # }
465 /// ```
466 pub fn vertices(&self) -> &[Point<Real>] {
467 &self.vertices[..]
468 }
469
470 /// Returns a slice containing all segment indices.
471 ///
472 /// Each index is a pair `[u32; 2]` representing a segment connecting two vertices.
473 /// The first element is the index of the segment's start vertex, and the second
474 /// is the index of the end vertex.
475 ///
476 /// # Returns
477 ///
478 /// A slice of all segment index pairs
479 ///
480 /// # Example
481 ///
482 /// ```
483 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
484 /// use parry3d::shape::Polyline;
485 /// use nalgebra::Point3;
486 ///
487 /// let vertices = vec![
488 /// Point3::origin(),
489 /// Point3::new(1.0, 0.0, 0.0),
490 /// Point3::new(2.0, 0.0, 0.0),
491 /// ];
492 ///
493 /// // With automatic indices
494 /// let polyline = Polyline::new(vertices.clone(), None);
495 /// let indices = polyline.indices();
496 /// assert_eq!(indices.len(), 2);
497 /// assert_eq!(indices[0], [0, 1]); // First segment: vertex 0 -> 1
498 /// assert_eq!(indices[1], [1, 2]); // Second segment: vertex 1 -> 2
499 ///
500 /// // With custom indices
501 /// let custom_indices = vec![[0, 2], [1, 0]];
502 /// let custom = Polyline::new(vertices, Some(custom_indices));
503 /// assert_eq!(custom.indices()[0], [0, 2]);
504 /// assert_eq!(custom.indices()[1], [1, 0]);
505 /// # }
506 /// ```
507 pub fn indices(&self) -> &[[u32; 2]] {
508 &self.indices
509 }
510
511 /// A flat view of the index buffer of this mesh.
512 pub fn flat_indices(&self) -> &[u32] {
513 unsafe {
514 let len = self.indices.len() * 2;
515 let data = self.indices.as_ptr() as *const u32;
516 core::slice::from_raw_parts(data, len)
517 }
518 }
519
520 /// Computes a scaled version of this polyline.
521 ///
522 /// This consumes the polyline and returns a new one with all vertices scaled
523 /// component-wise by the given scale vector. The connectivity (indices) remains
524 /// unchanged, but the BVH is rebuilt to reflect the new geometry.
525 ///
526 /// # Arguments
527 ///
528 /// * `scale` - The scaling factors for each axis
529 ///
530 /// # Returns
531 ///
532 /// A new polyline with scaled vertices
533 ///
534 /// # Example
535 ///
536 /// ```
537 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
538 /// use parry2d::shape::Polyline;
539 /// use nalgebra::{Point2, Vector2};
540 ///
541 /// let vertices = vec![
542 /// Point2::new(1.0, 2.0),
543 /// Point2::new(3.0, 4.0),
544 /// ];
545 /// let polyline = Polyline::new(vertices, None);
546 ///
547 /// // Scale by 2x in X and 3x in Y
548 /// let scaled = polyline.scaled(&Vector2::new(2.0, 3.0));
549 ///
550 /// // Check scaled vertices
551 /// assert_eq!(scaled.vertices()[0], Point2::new(2.0, 6.0));
552 /// assert_eq!(scaled.vertices()[1], Point2::new(6.0, 12.0));
553 /// # }
554 /// ```
555 ///
556 /// # Note
557 ///
558 /// This method consumes `self`. If you need to keep the original polyline,
559 /// clone it first:
560 ///
561 /// ```
562 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
563 /// use parry3d::shape::Polyline;
564 /// use nalgebra::{Point3, Vector3};
565 ///
566 /// let vertices = vec![Point3::origin(), Point3::new(1.0, 0.0, 0.0)];
567 /// let original = Polyline::new(vertices, None);
568 ///
569 /// // Clone before scaling if you need to keep the original
570 /// let scaled = original.clone().scaled(&Vector3::new(2.0, 2.0, 2.0));
571 ///
572 /// // Both polylines still exist
573 /// assert_eq!(original.vertices()[1].x, 1.0);
574 /// assert_eq!(scaled.vertices()[1].x, 2.0);
575 /// # }
576 /// ```
577 pub fn scaled(mut self, scale: &Vector<Real>) -> Self {
578 self.vertices
579 .iter_mut()
580 .for_each(|pt| pt.coords.component_mul_assign(scale));
581 let mut bvh = self.bvh.clone();
582 bvh.scale(scale);
583 Self {
584 bvh,
585 vertices: self.vertices,
586 indices: self.indices,
587 }
588 }
589
590 /// Reverses the orientation of this polyline.
591 ///
592 /// This operation:
593 /// 1. Swaps the start and end vertex of each segment (reversing edge direction)
594 /// 2. Reverses the order of segments in the index buffer
595 /// 3. Rebuilds the BVH to maintain correct acceleration structure
596 ///
597 /// After reversing, traversing the polyline segments in order will visit
598 /// the same geometry but in the opposite direction.
599 ///
600 /// # Example
601 ///
602 /// ```
603 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
604 /// use parry2d::shape::Polyline;
605 /// use nalgebra::Point2;
606 ///
607 /// let vertices = vec![
608 /// Point2::origin(),
609 /// Point2::new(1.0, 0.0),
610 /// Point2::new(2.0, 0.0),
611 /// ];
612 /// let mut polyline = Polyline::new(vertices, None);
613 ///
614 /// // Original: segment 0 goes from vertex 0 to 1, segment 1 from 1 to 2
615 /// assert_eq!(polyline.indices()[0], [0, 1]);
616 /// assert_eq!(polyline.indices()[1], [1, 2]);
617 ///
618 /// // Reverse the polyline
619 /// polyline.reverse();
620 ///
621 /// // After reversing: order is flipped and directions are swapped
622 /// // The last segment becomes first, with swapped endpoints
623 /// assert_eq!(polyline.indices()[0], [2, 1]);
624 /// assert_eq!(polyline.indices()[1], [1, 0]);
625 /// # }
626 /// ```
627 ///
628 /// # Use Cases
629 ///
630 /// This is useful for:
631 /// - Correcting winding order for 2D shapes
632 /// - Reversing path direction for navigation
633 /// - Ensuring consistent edge orientation in connected components
634 pub fn reverse(&mut self) {
635 for idx in &mut self.indices {
636 idx.swap(0, 1);
637 }
638
639 self.indices.reverse();
640
641 // Rebuild the bvh since the segment indices no longer map to the correct element.
642 // TODO PERF: should the Bvh have a function for efficient leaf index remapping?
643 // Probably not worth it unless this function starts showing up as a
644 // bottleneck for someone.
645 let leaves = self.segments().map(|seg| seg.local_aabb()).enumerate();
646 let bvh = Bvh::from_iter(BvhBuildStrategy::Binned, leaves);
647 self.bvh = bvh;
648 }
649
650 /// Extracts the connected components of this polyline, consuming `self`.
651 ///
652 /// This method is currently quite restrictive on the kind of allowed input. The polyline
653 /// represented by `self` must already have an index buffer sorted such that:
654 /// - Each connected component appears in the index buffer one after the other, i.e., a
655 /// connected component of this polyline must be a contiguous range of this polyline’s
656 /// index buffer.
657 /// - Each connected component is closed, i.e., each range of this polyline index buffer
658 /// `self.indices[i_start..=i_end]` forming a complete connected component, we must have
659 /// `self.indices[i_start][0] == self.indices[i_end][1]`.
660 /// - The indices for each component must already be in order, i.e., if the segments
661 /// `self.indices[i]` and `self.indices[i + 1]` are part of the same connected component then
662 /// we must have `self.indices[i][1] == self.indices[i + 1][0]`.
663 ///
664 /// # Output
665 /// Returns the set of polylines. If the inputs fulfill the constraints mentioned above, each
666 /// polyline will be a closed loop with consistent edge orientations, i.e., for all indices `i`,
667 /// we have `polyline.indices[i][1] == polyline.indices[i + 1][0]`.
668 ///
669 /// The orientation of each closed loop (clockwise or counterclockwise) are identical to their
670 /// original orientation in `self`.
671 pub fn extract_connected_components(&self) -> Vec<Polyline> {
672 let vertices = self.vertices();
673 let indices = self.indices();
674
675 if indices.is_empty() {
676 // Polyline is empty, return empty Vec
677 Vec::new()
678 } else {
679 let mut components = Vec::new();
680
681 let mut start_i = 0; // Start position of component
682 let mut start_node = indices[0][0]; // Start vertex index of component
683
684 let mut component_vertices = Vec::new();
685 let mut component_indices: Vec<[u32; 2]> = Vec::new();
686
687 // Iterate over indices, building polylines as we go
688 for (i, idx) in indices.iter().enumerate() {
689 component_vertices.push(vertices[idx[0] as usize]);
690
691 if idx[1] != start_node {
692 // Keep scanning and adding data
693 component_indices.push([(i - start_i) as u32, (i - start_i + 1) as u32]);
694 } else {
695 // Start node reached: build polyline and start next component
696 component_indices.push([(i - start_i) as u32, 0]);
697 components.push(Polyline::new(
698 core::mem::take(&mut component_vertices),
699 Some(core::mem::take(&mut component_indices)),
700 ));
701
702 if i + 1 < indices.len() {
703 // More components to find
704 start_node = indices[i + 1][0];
705 start_i = i + 1;
706 }
707 }
708 }
709
710 components
711 }
712 }
713
714 /// Perform a point projection assuming a solid interior based on a counter-clock-wise orientation.
715 ///
716 /// This is similar to `self.project_local_point_and_get_location` except that the resulting
717 /// `PointProjection::is_inside` will be set to true if the point is inside of the area delimited
718 /// by this polyline, assuming that:
719 /// - This polyline isn’t self-crossing.
720 /// - This polyline is closed with `self.indices[i][1] == self.indices[(i + 1) % num_indices][0]` where
721 /// `num_indices == self.indices.len()`.
722 /// - This polyline is oriented counter-clockwise.
723 /// - In 3D, the polyline is assumed to be fully coplanar, on a plane with normal given by
724 /// `axis`.
725 ///
726 /// These properties are not checked.
727 pub fn project_local_point_assuming_solid_interior_ccw(
728 &self,
729 point: Point<Real>,
730 #[cfg(feature = "dim3")] axis: u8,
731 ) -> (PointProjection, (u32, SegmentPointLocation)) {
732 let mut proj = self.project_local_point_and_get_location(&point, false);
733 let segment1 = self.segment((proj.1).0);
734
735 #[cfg(feature = "dim2")]
736 let normal1 = segment1.normal();
737 #[cfg(feature = "dim3")]
738 let normal1 = segment1.planar_normal(axis);
739
740 if let Some(normal1) = normal1 {
741 proj.0.is_inside = match proj.1 .1 {
742 SegmentPointLocation::OnVertex(i) => {
743 let dir2 = if i == 0 {
744 let adj_seg = if proj.1 .0 == 0 {
745 self.indices().len() as u32 - 1
746 } else {
747 proj.1 .0 - 1
748 };
749
750 assert_eq!(segment1.a, self.segment(adj_seg).b);
751 -self.segment(adj_seg).scaled_direction()
752 } else {
753 assert_eq!(i, 1);
754 let adj_seg = (proj.1 .0 + 1) % self.indices().len() as u32;
755 assert_eq!(segment1.b, self.segment(adj_seg).a);
756
757 self.segment(adj_seg).scaled_direction()
758 };
759
760 let dot = normal1.dot(&dir2);
761 // TODO: is this threshold too big? This corresponds to an angle equal to
762 // abs(acos(1.0e-3)) = (90 - 0.057) degrees.
763 // We did encounter some cases where this was needed, but perhaps the
764 // actual problem was an issue with the SegmentPointLocation (which should
765 // perhaps have been Edge instead of Vertex)?
766 let threshold = 1.0e-3 * dir2.norm();
767 if dot.abs() > threshold {
768 // If the vertex is a reentrant vertex, then the point is
769 // inside. Otherwise, it is outside.
770 dot >= 0.0
771 } else {
772 // If the two edges are collinear, we can’t classify the vertex.
773 // So check against the edge’s normal instead.
774 (point - proj.0.point).dot(&normal1) <= 0.0
775 }
776 }
777 SegmentPointLocation::OnEdge(_) => (point - proj.0.point).dot(&normal1) <= 0.0,
778 };
779 }
780
781 proj
782 }
783}
784
785impl CompositeShape for Polyline {
786 fn map_part_at(
787 &self,
788 i: u32,
789 f: &mut dyn FnMut(Option<&Isometry<Real>>, &dyn Shape, Option<&dyn NormalConstraints>),
790 ) {
791 let tri = self.segment(i);
792 f(None, &tri, None)
793 }
794
795 fn bvh(&self) -> &Bvh {
796 &self.bvh
797 }
798}
799
800impl TypedCompositeShape for Polyline {
801 type PartShape = Segment;
802 type PartNormalConstraints = ();
803
804 #[inline(always)]
805 fn map_typed_part_at<T>(
806 &self,
807 i: u32,
808 mut f: impl FnMut(
809 Option<&Isometry<Real>>,
810 &Self::PartShape,
811 Option<&Self::PartNormalConstraints>,
812 ) -> T,
813 ) -> Option<T> {
814 let seg = self.segment(i);
815 Some(f(None, &seg, None))
816 }
817
818 #[inline(always)]
819 fn map_untyped_part_at<T>(
820 &self,
821 i: u32,
822 mut f: impl FnMut(Option<&Isometry<Real>>, &dyn Shape, Option<&dyn NormalConstraints>) -> T,
823 ) -> Option<T> {
824 let seg = self.segment(i);
825 Some(f(None, &seg, None))
826 }
827}