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}