parry2d/shape/
polyline.rs

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