planar_geo/polysegment.rs
1/*!
2Defines the [`Polysegment`] type, a foundational [`Composite`] used throughout
3this crate.
4
5Segment polysegments serve as the basis for higher-level geometric types such as
6[`Contour`](crate::contour::Contour) and [`Shape`](crate::shape::Shape). They
7represent connected sequences of segments and provide the core functionality
8required by composite geometries.
9
10Most users should interact with this module through the [`Polysegment`] type
11itself; see its documentation for details on construction, invariants, and
12usage.
13*/
14
15use std::collections::VecDeque;
16
17use crate::primitive::Primitive;
18use crate::segment::{ArcSegment, LineSegment, Segment, SegmentRef};
19use crate::{DEFAULT_EPSILON, DEFAULT_MAX_ULPS};
20use crate::{Transformation, composite::*};
21use approx::ulps_eq;
22use bounding_box::{BoundingBox, ToBoundingBox};
23use rayon::prelude::*;
24
25#[cfg(feature = "serde")]
26use serde::{Deserialize, Serialize};
27
28/**
29A sequence of [`Segment`]s where each segment is "connected" to its successor
30(end / stop point of a segment is approximately equal to the start point of the
31successor).
32
33*/
34#[doc = ""]
35#[cfg_attr(
36 feature = "doc-images",
37 doc = "![Polysegment example][example_polysegment]"
38)]
39#[cfg_attr(
40 feature = "doc-images",
41 embed_doc_image::embed_doc_image("example_polysegment", "docs/img/example_polysegment.svg")
42)]
43#[cfg_attr(
44 not(feature = "doc-images"),
45 doc = "**Doc images not enabled**. Compile docs with `cargo doc --features 'doc-images'` and Rust version >= 1.54."
46)]
47/**
48
49The approximate equality of start and end point is defined by
50[`DEFAULT_EPSILON`] and [`DEFAULT_MAX_ULPS`]:
51
52```ignore
53approx::ulps_eq!(polysegment[i].stop(), polysegment[i+1].start(), epsilon = DEFAULT_EPSILON, max_ulps = DEFAULT_MAX_ULPS)
54```
55
56A polysegment can intersect itself (i.e. at least two of its segments
57intersect each other). This can be tested using its [`Composite`] trait
58implementation:
59
60```
61use planar_geo::prelude::*;
62
63let polysegment = Polysegment::from_points(&[[0.0, 0.0], [2.0, 2.0], [0.0, 2.0], [2.0, 0.0]]);
64assert_eq!(polysegment.intersections_polysegment(&polysegment, 0.0, 0).count(), 1);
65```
66
67# Constructing a polysegment
68
69A polysegment is essentially a [newtype](https://doc.rust-lang.org/rust-by-example/generics/new_types.html)
70wrapper around a [`VecDeque<Segment>`] and therefore exposes many of the same
71methods. For example, a polysegment can be built via
72[`push_front`](Polysegment::push_front) and
73[`push_back`](Polysegment::push_back) just like a [`VecDeque`]. The
74[`extend_front`](Polysegment::extend_front) and
75[`extend_back`](Polysegment::extend_back) methods can be used to implicitly
76add [`LineSegment`]s.
77
78There are multiple constructors available:
79- [`new`](Polysegment::new): A new, empty polysegment with no segment.
80- [`with_capacity`](Polysegment::with_capacity): A new, empty polysegment
81with a defined space for segments preallocated with no segment.
82- [`from_points`](Polysegment::from_points): A polysegment consisting of
83[`LineSegment`]s which connect the given points.
84- [`from_iter`](Polysegment::from_iter): A polysegment consisting of the
85segments given by an iterator. In case two subsequent segments are not
86connected, a "filler" [`LineSegment`] is introduced between them.
87
88# Modifying a polysegment
89
90To uphold the "connection" property, it is not possible to manipulate arbitrary
91segments, which is why methods like [`VecDeque::get_mut`] are missing.
92It is however possible to manipulate the whole polysegment via the
93[`Transformation`] implementation. Additionally, individual segments can be
94removed from the ends of the polysegment with [`pop_front`](Polysegment::pop_front)
95and [`pop_back`](Polysegment::pop_back).
96
97# Access of individual segments
98
99Accessing individual segments is possible via indexing (which panics when
100out-of-bounds) and the [`get`](Polysegment::get) method (which returns `None`
101when out-of-bounds). As in the underlying deque, the segments are not
102necessarily contiguous in memory and can generally only be accessed via two
103slices with the [`as_slices`](Polysegment::as_slices) method. [`Polysegment`]
104does however expose the [`make_contiguous`](Polysegment::make_contiguous) to
105store the segments contiguous in memory.
106
107# Serialization and deserialization
108
109When the `serde` feature is enabled, a polysegment can be serialized and
110deserialized using the [`serde`] crate. It uses the same serialized
111representation as a [`VecDeque`].
112 */
113#[derive(Debug, Clone, PartialEq)]
114#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
115pub struct Polysegment(VecDeque<Segment>);
116
117impl Polysegment {
118 /**
119 Creates an empty [`Polysegment`].
120
121 # Examples
122
123 ```
124 use planar_geo::prelude::Polysegment;
125
126 let polysegment = Polysegment::new();
127 ```
128 */
129 pub fn new() -> Self {
130 return Self(VecDeque::new());
131 }
132
133 /**
134 Creates an empty [`Polysegment`] with space for at least `capacity`
135 [`Segment`].
136
137 # Examples
138
139 ```
140 use planar_geo::prelude::Polysegment;
141
142 let polysegment = Polysegment::with_capacity(10);
143 ```
144 */
145 pub fn with_capacity(capacity: usize) -> Self {
146 return Self(VecDeque::with_capacity(capacity));
147 }
148
149 /**
150 Creates an [`Polysegment`] of [`LineSegment`]s from the given points.
151 If two consecutive points are equal (within the tolerances defined by
152 [`DEFAULT_EPSILON`] and [`DEFAULT_MAX_ULPS`]), they are treated as a single
153 vertex.
154
155 # Examples
156
157 ```
158 use planar_geo::prelude::Polysegment;
159
160 // Three points -> Two line segments
161 let polysegment = Polysegment::from_points(&[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]);
162 assert_eq!(polysegment.len(), 2);
163
164 // Two consecutive points are equal
165 let polysegment = Polysegment::from_points(&[[0.0, 0.0], [0.0, 0.0], [0.0, 1.0]]);
166 assert_eq!(polysegment.len(), 1);
167 ```
168 */
169 pub fn from_points(points: &[[f64; 2]]) -> Self {
170 let mut segments: VecDeque<Segment> = VecDeque::with_capacity(points.len());
171 for window in points.windows(2) {
172 let start = window[0];
173 let stop = window[1];
174 match segments.back() {
175 Some(last_segment) => {
176 if last_segment.stop() == stop {
177 continue;
178 }
179 }
180 None => (),
181 }
182
183 if let Ok(ls) = LineSegment::new(start, stop, DEFAULT_EPSILON, DEFAULT_MAX_ULPS) {
184 segments.push_back(ls.into());
185 }
186 }
187 return Self(segments);
188 }
189
190 /**
191 "Closes" the [`Polysegment`] by connecting the start point of the first /
192 "front" segment with the stop point of the last / "back" segment with a
193 [`LineSegment`] (if the two points aren't already equal).
194
195 # Examples
196
197 ```
198 use planar_geo::prelude::Polysegment;
199
200 // Three points -> Two line segments
201 let mut polysegment = Polysegment::from_points(&[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]);
202 assert_eq!(polysegment.len(), 2);
203
204 // Close the polysegment
205 polysegment.close();
206 assert_eq!(polysegment.len(), 3);
207 ```
208 */
209 pub fn close(&mut self) -> () {
210 if let Some(start) = self.0.front() {
211 if let Some(stop) = self.0.back() {
212 if let Ok(ls) = LineSegment::new(
213 stop.stop(),
214 start.start(),
215 DEFAULT_EPSILON,
216 DEFAULT_MAX_ULPS,
217 ) {
218 self.0.push_back(ls.into())
219 }
220 }
221 }
222 }
223
224 /**
225 Returns a reference to the underlying [`VecDeque`].
226
227 This allows using all methods of [`VecDeque`] which use a shared reference,
228 e.g. its iterators, accessor methods etc.
229 */
230 pub fn vec_deque(&self) -> &VecDeque<Segment> {
231 return &self.0;
232 }
233
234 /**
235 Calls the [`VecDeque::as_slices`] method of the underlying deque. See its
236 docstring for more.
237 */
238 pub fn as_slices(&self) -> (&[Segment], &[Segment]) {
239 return self.0.as_slices();
240 }
241
242 /**
243 Calls the [`VecDeque::make_contiguous`] method of the underlying deque. See
244 its docstring for more.
245 */
246 pub fn make_contiguous(&mut self) -> &[Segment] {
247 return self.0.make_contiguous();
248 }
249
250 /**
251 Appends a [`Segment`] to the back of the [`Polysegment`].
252
253 If the polysegment already has a "back" segment ([`Polysegment::back`] returns
254 [`Some`]), the start point of `segment` is compared to the stop point of
255 the back segment. If they aren't equal within the tolerances defined by
256 [`DEFAULT_EPSILON`] and [`DEFAULT_MAX_ULPS`], a filler line segment is
257 inserted between the two.
258
259 # Examples
260
261 ```
262 use planar_geo::prelude::*;
263
264 let mut polysegment = Polysegment::new();
265 assert_eq!(polysegment.len(), 0);
266
267 polysegment.push_back(LineSegment::new([0.0, 0.0], [1.0, 0.0], DEFAULT_EPSILON, DEFAULT_MAX_ULPS).unwrap().into());
268 assert_eq!(polysegment.len(), 1);
269
270 // The start point of this segment matches the stop point of the already
271 // inserted segment
272 polysegment.push_back(LineSegment::new([1.0, 0.0], [1.0, 1.0], DEFAULT_EPSILON, DEFAULT_MAX_ULPS).unwrap().into());
273 assert_eq!(polysegment.len(), 2);
274
275 // Start and stop point don't match -> A filler segment is introduced
276 polysegment.push_back(LineSegment::new([2.0, 1.0], [2.0, 0.0], DEFAULT_EPSILON, DEFAULT_MAX_ULPS).unwrap().into());
277 assert_eq!(polysegment.len(), 4);
278 ```
279 */
280 pub fn push_back(&mut self, segment: Segment) {
281 let opt_stop = self.back().map(Segment::stop);
282 if let Some(stop) = opt_stop {
283 if let Ok(ls) =
284 LineSegment::new(stop, segment.start(), DEFAULT_EPSILON, DEFAULT_MAX_ULPS)
285 {
286 self.0.push_back(ls.into());
287 }
288 }
289 self.0.push_back(segment);
290 }
291
292 /**
293 Prepends a [`Segment`] to the front of the [`Polysegment`].
294
295 If the polysegment already has a "front" segment ([`Polysegment::front`] returns
296 [`Some`]), the stop point of `segment` is compared to the start point of
297 the front segment. If they aren't equal within the tolerances defined by
298 [`DEFAULT_EPSILON`] and [`DEFAULT_MAX_ULPS`], a filler line segment is
299 inserted between the two.
300
301 # Examples
302
303 ```
304 use planar_geo::prelude::*;
305
306 let mut polysegment = Polysegment::new();
307 assert_eq!(polysegment.len(), 0);
308
309 polysegment.push_front(LineSegment::new([0.0, 0.0], [1.0, 0.0], DEFAULT_EPSILON, DEFAULT_MAX_ULPS).unwrap().into());
310 assert_eq!(polysegment.len(), 1);
311
312 // The stop point of this segment matches the start point of the already
313 // inserted segment
314 polysegment.push_front(LineSegment::new([0.0, 1.0], [0.0, 0.0], DEFAULT_EPSILON, DEFAULT_MAX_ULPS).unwrap().into());
315 assert_eq!(polysegment.len(), 2);
316
317 // Start and stop point don't match -> A filler segment is introduced
318 polysegment.push_front(LineSegment::new([2.0, 1.0], [2.0, 0.0], DEFAULT_EPSILON, DEFAULT_MAX_ULPS).unwrap().into());
319 assert_eq!(polysegment.len(), 4);
320 ```
321 */
322 pub fn push_front(&mut self, segment: Segment) {
323 let opt_start = self.front().map(Segment::start);
324 if let Some(start) = opt_start {
325 if let Ok(ls) =
326 LineSegment::new(segment.stop(), start, DEFAULT_EPSILON, DEFAULT_MAX_ULPS)
327 {
328 self.0.push_front(ls.into());
329 }
330 }
331 self.0.push_front(segment);
332 }
333
334 /**
335 Removes the last / back element from the polysegment and returns it, or [`None`]
336 if it is empty.
337
338 # Examples
339
340 ```
341 use planar_geo::prelude::*;
342
343 let mut polysegment = Polysegment::new();
344
345 let ls: Segment = LineSegment::new([0.0, 0.0], [1.0, 0.0], DEFAULT_EPSILON, DEFAULT_MAX_ULPS).unwrap().into();
346
347 polysegment.push_back(ls.clone());
348 assert_eq!(polysegment.len(), 1);
349
350 assert_eq!(polysegment.pop_back(), Some(ls));
351 assert_eq!(polysegment.pop_back(), None);
352 */
353 pub fn pop_back(&mut self) -> Option<Segment> {
354 return self.0.pop_back();
355 }
356
357 /**
358 Removes the first / front element from the polysegment and returns it, or [`None`]
359 if it is empty.
360
361 # Examples
362
363 ```
364 use planar_geo::prelude::*;
365
366 let mut polysegment = Polysegment::new();
367
368 let ls: Segment = LineSegment::new([0.0, 0.0], [1.0, 0.0], DEFAULT_EPSILON, DEFAULT_MAX_ULPS).unwrap().into();
369
370 polysegment.push_front(ls.clone());
371 assert_eq!(polysegment.len(), 1);
372
373 assert_eq!(polysegment.pop_front(), Some(ls));
374 assert_eq!(polysegment.pop_front(), None);
375 */
376 pub fn pop_front(&mut self) -> Option<Segment> {
377 return self.0.pop_front();
378 }
379
380 /**
381 Provides a reference to the back element, or [`None`] if the polysegment is empty.
382
383 # Examples
384
385 ```
386 use planar_geo::prelude::*;
387
388 let polysegment = Polysegment::from_points(&[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]);
389 let ls: Segment = LineSegment::new([1.0, 0.0], [0.0, 1.0], DEFAULT_EPSILON, DEFAULT_MAX_ULPS).unwrap().into();
390
391 assert_eq!(polysegment.back(), Some(&ls));
392 ```
393 */
394 pub fn back(&self) -> Option<&Segment> {
395 return self.0.back();
396 }
397
398 /**
399 Provides a reference to the front element, or [`None`] if the polysegment is empty.
400
401 # Examples
402
403 ```
404 use planar_geo::prelude::*;
405
406 let polysegment = Polysegment::from_points(&[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]);
407 let ls: Segment = LineSegment::new([0.0, 0.0], [1.0, 0.0], DEFAULT_EPSILON, DEFAULT_MAX_ULPS).unwrap().into();
408
409 assert_eq!(polysegment.front(), Some(&ls));
410 */
411 pub fn front(&self) -> Option<&Segment> {
412 return self.0.front();
413 }
414
415 /**
416 Adds a [`LineSegment`] to the front of `self` which stops at `point` and
417 starts at the current stop point of `self` - i.e., the `stop` point of the
418 [`Segment`] returned from [`Polysegment::back`]. If `self` is empty or
419 `point` is equal to `stop`, this is a no-op.
420
421 # Examples
422
423 ```
424 use planar_geo::prelude::*;
425
426 let mut polysegment = Polysegment::new();
427 assert_eq!(polysegment.len(), 0);
428
429 // No-op, since the polysegment is empty
430 polysegment.extend_back([0.0, 0.0]);
431 assert_eq!(polysegment.len(), 0);
432
433 // Now add a line
434 polysegment.push_back(LineSegment::new([1.0, 0.0], [1.0, 1.0], DEFAULT_EPSILON, DEFAULT_MAX_ULPS).unwrap().into());
435 assert_eq!(polysegment.len(), 1);
436
437 // Now adding the point works
438 polysegment.extend_back([0.0, 0.0]);
439 assert_eq!(polysegment.len(), 2);
440 ```
441 */
442 pub fn extend_back(&mut self, point: [f64; 2]) {
443 let endpoint = self.back().map(|s| s.stop());
444 if let Some(endpoint) = endpoint {
445 if let Ok(ls) = LineSegment::new(endpoint, point, DEFAULT_EPSILON, DEFAULT_MAX_ULPS) {
446 self.push_back(ls.into());
447 }
448 }
449 }
450
451 /**
452 Adds a [`LineSegment`] to the front of `self` which starts at `point` and
453 stops at the current start point of `self` - i.e., the `start` point of the
454 [`Segment`] returned from [`Polysegment::front`]. If `self` is empty or
455 `point` is equal to `start`, this is a no-op.
456
457 # Examples
458
459 ```
460 use planar_geo::prelude::*;
461
462 let mut polysegment = Polysegment::new();
463 assert_eq!(polysegment.len(), 0);
464
465 // No-op, since the polysegment is empty
466 polysegment.extend_front([0.0, 0.0]);
467 assert_eq!(polysegment.len(), 0);
468
469 // Now add a line
470 polysegment.push_front(LineSegment::new([1.0, 0.0], [1.0, 1.0], DEFAULT_EPSILON, DEFAULT_MAX_ULPS).unwrap().into());
471 assert_eq!(polysegment.len(), 1);
472
473 // Now adding the point works
474 polysegment.extend_front([0.0, 0.0]);
475 assert_eq!(polysegment.len(), 2);
476 ```
477 */
478 pub fn extend_front(&mut self, point: [f64; 2]) {
479 let endpoint = self.front().map(|s| s.start());
480 if let Some(endpoint) = endpoint {
481 if let Ok(ls) = LineSegment::new(point, endpoint, DEFAULT_EPSILON, DEFAULT_MAX_ULPS) {
482 self.push_front(ls.into());
483 }
484 }
485 }
486
487 /**
488 Returns whether the polysegment / the underlying [`VecDeque`] is empty or not.
489
490 # Examples
491
492 ```
493 use planar_geo::prelude::*;
494
495 let mut polysegment = Polysegment::new();
496 assert!(polysegment.is_empty());
497
498 polysegment.push_back(LineSegment::new([0.0, 0.0], [1.0, 0.0], 0.0, 0).unwrap().into());
499 assert!(!polysegment.is_empty());
500 ```
501 */
502 pub fn is_empty(&self) -> bool {
503 return self.0.is_empty();
504 }
505
506 /**
507 Provides a reference to the [`Segment`] at the given index.
508
509 The segment at index 0 is the front of the polysegment.
510
511 # Examples
512
513 ```
514 use planar_geo::prelude::*;
515
516 let polysegment = Polysegment::from_points(&[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]);
517 assert!(polysegment.get(0).is_some());
518 assert!(polysegment.get(3).is_none());
519 ```
520 */
521 pub fn get(&self, index: usize) -> Option<&Segment> {
522 return self.0.get(index);
523 }
524
525 /**
526 Returns the number of [`Segment`]s in `self`.
527
528 # Examples
529
530 ```
531 use planar_geo::prelude::*;
532
533 let polysegment = Polysegment::from_points(&[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]);
534 assert_eq!(polysegment.len(), 2);
535 ```
536 */
537 pub fn len(&self) -> usize {
538 return self.0.len();
539 }
540
541 /**
542 Returns a front-to-back iterator over all [`Segment`]s of `self`.
543
544 # Examples
545
546 ```
547 use planar_geo::prelude::*;
548
549 let polysegment = Polysegment::from_points(&[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]);
550 let mut iter = polysegment.segments();
551 assert!(iter.next().is_some());
552 assert!(iter.next().is_some());
553 assert!(iter.next().is_none());
554 ```
555 */
556 pub fn segments(&self) -> std::collections::vec_deque::Iter<'_, Segment> {
557 return self.0.iter();
558 }
559
560 /**
561 Returns a parallel front-to-back iterator over all [`Segment`]s of `self`.
562
563 # Examples
564
565 ```
566 use rayon::prelude::*;
567 use planar_geo::prelude::*;
568
569 let polysegment = Polysegment::from_points(&[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]);
570 let vec: Vec<_> = polysegment.segments_par().collect();
571 assert_eq!(vec.len(), 2);
572 ```
573 */
574 pub fn segments_par(&self) -> rayon::collections::vec_deque::Iter<'_, Segment> {
575 return self.0.par_iter();
576 }
577
578 /**
579 Moves all [`Segment`]s of `other` into `self`, leaving `other` empty.
580
581 If the first point of `other` is not equal to the last point of `self`, a
582 filler line segment is introduced first
583
584 # Panics
585
586 Panics if the new number of elements in `self` overflows an [`usize`].
587
588 # Examples
589
590 ```
591 use planar_geo::prelude::*;
592
593 let mut polysegment1 = Polysegment::from_points(&[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]);
594 let mut polysegment2 = Polysegment::from_points(&[[1.0, 1.0], [0.0, 1.0]]);
595 let mut polysegment3 = Polysegment::from_points(&[[0.0, 2.0], [0.0, 3.0]]);
596
597 assert_eq!(polysegment1.len(), 2);
598 assert_eq!(polysegment2.len(), 1);
599 assert_eq!(polysegment3.len(), 1);
600
601 polysegment1.append(&mut polysegment2);
602 assert_eq!(polysegment1.len(), 3);
603
604 polysegment1.append(&mut polysegment3);
605 assert_eq!(polysegment1.len(), 5);
606 ```
607 */
608 pub fn append(&mut self, other: &mut Polysegment) -> () {
609 if let Some(s) = other.0.front() {
610 let start = s.start();
611 if let Some(s) = self.0.back() {
612 let stop = s.stop();
613 if let Ok(ls) = LineSegment::new(stop, start, DEFAULT_EPSILON, DEFAULT_MAX_ULPS) {
614 self.0.push_back(ls.into())
615 }
616 }
617 }
618
619 // Append the segments
620 self.0.append(&mut other.0);
621 }
622
623 /**
624 Returns an iterator over all points of the polysegment.
625
626 # Examples
627
628 ```
629 use planar_geo::prelude::*;
630
631 let polysegment = Polysegment::from_points(&[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]);
632
633 let mut iter = polysegment.points();
634 assert_eq!(iter.next(), Some([0.0, 0.0]));
635 assert_eq!(iter.next(), Some([1.0, 0.0]));
636 assert_eq!(iter.next(), Some([1.0, 1.0]));
637 assert_eq!(iter.next(), None);
638 ```
639 */
640 pub fn points(&self) -> PointIterator<'_> {
641 return PointIterator::new(self, false, Polygonizer::default());
642 }
643
644 /**
645 Returns the points of a polygon polysegment which approximates `self`. The
646 individual segments are "polygonized" via [`Segment::polygonize`] and
647 an [`SegmentPolygonizer`](crate::segment::SegmentPolygonizer) specified
648 within [`Polygonizer`]. See the docstring of the latter fore more.
649 */
650 pub fn polygonize(&self, polygonizer: Polygonizer) -> PointIterator<'_> {
651 return PointIterator::new(self, false, polygonizer);
652 }
653
654 /**
655 Returns the combined length of all segments of `self`.
656
657 ```
658 use planar_geo::prelude::*;
659 use std::f64::consts::{PI, TAU, SQRT_2};
660
661 // Square with a side length of 1
662 let points = &[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]];
663 let mut polysegment = Polysegment::from_points(points);
664 polysegment.close();
665 assert_eq!(polysegment.length(), 4.0);
666
667 // Circle with a radius of 1
668 let segment: Segment = ArcSegment::from_center_radius_start_offset_angle([0.0, 0.0], 1.0, 0.0, TAU, DEFAULT_EPSILON, DEFAULT_MAX_ULPS).unwrap().into();
669 let polysegment = Polysegment::from(segment);
670 assert_eq!(polysegment.length(), TAU);
671
672 // Half-circle segment
673 let segment: Segment = ArcSegment::from_center_radius_start_offset_angle([0.0, 0.0], 1.0, 0.0, PI, DEFAULT_EPSILON, DEFAULT_MAX_ULPS).unwrap().into();
674 let mut polysegment = Polysegment::from(segment);
675 polysegment.close();
676 assert_eq!(polysegment.length(), PI + 2.0);
677
678 // Triangle
679 let points = &[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]];
680 let mut polysegment = Polysegment::from_points(points);
681 polysegment.close();
682 assert_eq!(polysegment.length(), 2.0 + SQRT_2);
683 ```
684 */
685 pub fn length(&self) -> f64 {
686 return self.segments_par().map(|segment| segment.length()).sum();
687 }
688
689 /**
690 Cuts `self` into multiple polysegments by intersecting it with `other` and returns
691 those them.
692
693 The specified tolerances `epsilon` and `max_ulps` are used to determine
694 intersections, see documentation of
695 [`PrimitiveIntersections`](crate::primitive::PrimitiveIntersections).
696
697 # Examples
698
699 ```
700 use planar_geo::prelude::*;
701
702 let points = &[[0.0, 0.0], [2.0, 0.0], [2.0, 2.0], [0.0, 2.0]];
703 let line = Polysegment::from_points(points);
704 let cut = Polysegment::from_points(&[[-1.0, 1.0], [3.0, 1.0]]);
705
706 let separated_lines = line.intersection_cut(&cut, DEFAULT_EPSILON, DEFAULT_MAX_ULPS);
707
708 // This cut results in two separate polysegments
709 assert_eq!(separated_lines.len(), 2);
710 ```
711 */
712 pub fn intersection_cut(
713 &self,
714 other: &Polysegment,
715 epsilon: f64,
716 max_ulps: u32,
717 ) -> Vec<Polysegment> {
718 // Inner helper function
719 fn create_cut_segment(
720 segment: &Segment,
721 start: [f64; 2],
722 stop: [f64; 2],
723 center: [f64; 2],
724 epsilon: f64,
725 max_ulps: u32,
726 ) -> Option<Segment> {
727 // Check to avoid degenerated segments, when start equals stop
728 if !ulps_eq!(start, stop, epsilon = epsilon, max_ulps = max_ulps) {
729 match segment {
730 Segment::LineSegment(_) => {
731 if let Ok(ls) = LineSegment::new(start, stop, epsilon, max_ulps) {
732 return Some(ls.into());
733 } else {
734 return None;
735 }
736 }
737 Segment::ArcSegment(_) => {
738 let normalized_stop = [stop[0] - center[0], stop[1] - center[1]];
739 let normalized_start = [start[0] - center[0], start[1] - center[1]];
740 let stop_angle = normalized_stop[1].atan2(normalized_stop[0]);
741 let start_angle = normalized_start[1].atan2(normalized_start[0]);
742 let offset_angle = stop_angle - start_angle;
743
744 if let Ok(arc) = ArcSegment::from_start_center_angle(
745 start,
746 center,
747 offset_angle,
748 epsilon,
749 max_ulps,
750 ) {
751 return Some(arc.into());
752 } else {
753 return None;
754 }
755 }
756 };
757 } else {
758 return None;
759 }
760 }
761
762 let mut lines: Vec<Polysegment> = Vec::new();
763 let mut segments_of_current_line: Vec<Segment> = Vec::new();
764
765 // Temporary variables. The values themselves are dummy values to satisfy the
766 // borrow checker.
767 let mut center: [f64; 2] = [0.0, 0.0];
768
769 // Storage for intersections
770 let mut intersections: Vec<[f64; 2]> = Vec::new();
771
772 for segment_self in self.0.iter() {
773 // Preparations for the current segment
774 match segment_self {
775 Segment::LineSegment(_) => (),
776 Segment::ArcSegment(arc) => {
777 center = arc.center();
778 }
779 }
780
781 // Reset the intersections vector
782 intersections.clear();
783
784 for segment_other in other.0.iter() {
785 let intersections_segment_other =
786 segment_self.intersections_primitive(segment_other, epsilon, max_ulps);
787 intersections.extend(
788 intersections_segment_other
789 .into_iter()
790 .map::<[f64; 2], _>(From::from),
791 );
792 }
793
794 if intersections.is_empty() {
795 // No intersections could be found -> add segment_self to the list of segments
796 // forming the current line
797 segments_of_current_line.push(segment_self.clone())
798 } else {
799 // Sort the intersections depending on their distance from the start of
800 // segment_self (smalles first)
801 intersections.sort_unstable_by(|a, b| {
802 let start = segment_self.start();
803
804 // Calculate the distance of a from segment_self.start and compare it to the
805 // distance of b from segment_self. Since we are only
806 // interested in the order, it is not necessary to normalize the distance.
807 // Do the same for b.
808 let dist_a = (start[0] - a[0]).powi(2) + (start[1] - a[1]).powi(2);
809 let dist_b = (start[0] - b[0]).powi(2) + (start[1] - b[1]).powi(2);
810
811 // When non-comparable values are used (e.g. NaN), the user
812 // has provided nonsensical values for the segment points
813 // and the result of the intersection cut will be
814 // meaningless anyway -> We can return any ordering
815 dist_a
816 .partial_cmp(&dist_b)
817 .unwrap_or(std::cmp::Ordering::Equal)
818 });
819
820 for (idx, intersection) in intersections.iter().enumerate() {
821 if idx == 0 {
822 let start = segment_self.start();
823 let stop = *intersection;
824
825 if let Some(segment) = create_cut_segment(
826 &segment_self,
827 start,
828 stop,
829 center,
830 epsilon,
831 max_ulps,
832 ) {
833 if segments_of_current_line.is_empty() {
834 lines.push(Polysegment::from(segment));
835 } else {
836 let mut polysegment =
837 Polysegment::with_capacity(segments_of_current_line.len());
838 for s in segments_of_current_line.into_iter() {
839 polysegment.push_back(s);
840 }
841 polysegment.push_back(segment);
842 lines.push(polysegment);
843 }
844 } else {
845 if !segments_of_current_line.is_empty() {
846 let mut polysegment =
847 Polysegment::with_capacity(segments_of_current_line.len());
848 for s in segments_of_current_line.into_iter() {
849 polysegment.push_back(s);
850 }
851 lines.push(polysegment);
852 }
853 }
854
855 // Reinstatiate the vector for the next segment_self
856 segments_of_current_line = Vec::new();
857 } else {
858 // SAFETY: An out-of-bound index is catched by the if idx == 0 above
859 let start = unsafe { *intersections.get_unchecked(idx - 1) };
860 let stop = *intersection;
861
862 // Found some intersections => finish the current line and create a new one
863 if let Some(segment) = create_cut_segment(
864 &segment_self,
865 start,
866 stop,
867 center,
868 epsilon,
869 max_ulps,
870 ) {
871 lines.push(Polysegment::from(segment));
872 }
873 };
874 }
875
876 // Populate the "tail end" (the last cut)
877 if let Some(start) = intersections.last() {
878 if let Some(segment) = create_cut_segment(
879 &segment_self,
880 start.clone(),
881 segment_self.stop(),
882 center,
883 epsilon,
884 max_ulps,
885 ) {
886 segments_of_current_line.push(segment);
887 }
888 }
889 };
890 }
891
892 // Store the last segments as a line into the lines vector
893 let mut polysegment = Polysegment::with_capacity(segments_of_current_line.len());
894 for s in segments_of_current_line.into_iter() {
895 polysegment.push_back(s);
896 }
897 lines.push(polysegment);
898
899 return lines;
900 }
901
902 /**
903 Reverses all [`Segment`]s of the polysegment by switching their start and stop
904 points (see [`Segment::reverse`]). To ensure the "connected" property holds
905 true, the ordering of the segments itself is exchanged as well.
906
907 This method calls [`make_contiguous`](Polysegment::make_contiguous) so all
908 segments are in the first slice before reversing it.
909
910 # Examples
911
912 ```
913 use planar_geo::prelude::*;
914
915 let mut polysegment = Polysegment::from_points(&[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]);
916 assert_eq!(polysegment.front().unwrap().start(), [0.0, 0.0]);
917 assert_eq!(polysegment.back().unwrap().stop(), [0.0, 1.0]);
918
919 polysegment.reverse();
920 assert_eq!(polysegment.front().unwrap().start(), [0.0, 1.0]);
921 assert_eq!(polysegment.back().unwrap().stop(), [0.0, 0.0]);
922 ```
923 */
924 pub fn reverse(&mut self) -> () {
925 self.make_contiguous();
926
927 // After making the deque contiguous, the second slice is empty
928 let (slice, _) = self.0.as_mut_slices();
929 for segment in slice.iter_mut() {
930 segment.reverse();
931 }
932 slice.reverse();
933 }
934
935 /**
936 Creates a rotated pattern from `self`. For each one of the specified
937 `repetitions`, the individual segments of `self` are cloned, rotated around
938 `center` and then pushed to the back of `self`. The rotation angle is
939 `angle` times the index of the current repetition plus one.
940
941 # Examples
942
943 ```
944 use planar_geo::prelude::*;
945 use std::f64::consts::FRAC_PI_2;
946
947 let mut polysegment = Polysegment::from_points(&[[0.0, 0.0], [1.0, 0.0]]);
948
949 // 0 repetitions => The original polysegment is left unchanged
950 polysegment.rotational_pattern([1.0, 1.0], FRAC_PI_2, 0);
951 let points: Vec<[f64; 2]> = polysegment.points().collect();
952 assert_eq!(points.len(), 2);
953 approx::assert_abs_diff_eq!(points[0], [0.0, 0.0], epsilon = 1e-10);
954 approx::assert_abs_diff_eq!(points[1], [1.0, 0.0], epsilon = 1e-10);
955
956 // Two repetitions: The points are rotated by 90° and 180° respectively.
957 // The polysegment has 6 points after the extension
958 polysegment.rotational_pattern([1.0, 1.0], FRAC_PI_2, 2);
959 let points: Vec<[f64; 2]> = polysegment.points().collect();
960 assert_eq!(points.len(), 6);
961 approx::assert_abs_diff_eq!(points[0], [0.0, 0.0], epsilon = 1e-10);
962 approx::assert_abs_diff_eq!(points[1], [1.0, 0.0], epsilon = 1e-10);
963 approx::assert_abs_diff_eq!(points[2], [2.0, 0.0], epsilon = 1e-10);
964 approx::assert_abs_diff_eq!(points[3], [2.0, 1.0], epsilon = 1e-10);
965 approx::assert_abs_diff_eq!(points[4], [2.0, 2.0], epsilon = 1e-10);
966 approx::assert_abs_diff_eq!(points[5], [1.0, 2.0], epsilon = 1e-10);
967 ```
968 */
969 pub fn rotational_pattern(&mut self, center: [f64; 2], angle: f64, repetitions: usize) -> () {
970 let num_segments = self.num_segments();
971 for rep in 0..repetitions {
972 for seg_idx in 0..num_segments {
973 // Index is always in bounds, because its maximum value is
974 // the number of segments at the start of the outer loop
975 // and no segments are removed from self.
976 let mut segment = self[seg_idx].clone();
977 segment.rotate(center, angle * (rep as f64 + 1.0));
978 self.push_back(segment);
979 }
980 }
981 }
982
983 /**
984 Creates a translated pattern from `self`. For each one of the specified
985 `repetitions`, the individual segments of `self` are cloned, shifted and
986 then pushed to the back of `self`. The shift vector is `shift` times the
987 index of the current repetition plus one.
988
989 # Examples
990
991 ```
992 use planar_geo::prelude::*;
993 use std::f64::consts::FRAC_PI_2;
994
995 let mut polysegment = Polysegment::from_points(&[[0.0, 0.0], [1.0, 0.0]]);
996
997 // 0 repetitions => The original polysegment is left unchanged
998 polysegment.translational_pattern([1.0, 1.0], 0);
999 let points: Vec<[f64; 2]> = polysegment.points().collect();
1000 assert_eq!(points.len(), 2);
1001 approx::assert_abs_diff_eq!(points[0], [0.0, 0.0], epsilon = 1e-10);
1002 approx::assert_abs_diff_eq!(points[1], [1.0, 0.0], epsilon = 1e-10);
1003
1004 // Two repetitions: A "stair" polysegment is created
1005 polysegment.translational_pattern([1.0, 1.0], 2);
1006 let points: Vec<[f64; 2]> = polysegment.points().collect();
1007 assert_eq!(points.len(), 6);
1008 approx::assert_abs_diff_eq!(points[0], [0.0, 0.0], epsilon = 1e-10);
1009 approx::assert_abs_diff_eq!(points[1], [1.0, 0.0], epsilon = 1e-10);
1010 approx::assert_abs_diff_eq!(points[2], [1.0, 1.0], epsilon = 1e-10);
1011 approx::assert_abs_diff_eq!(points[3], [2.0, 1.0], epsilon = 1e-10);
1012 approx::assert_abs_diff_eq!(points[4], [2.0, 2.0], epsilon = 1e-10);
1013 approx::assert_abs_diff_eq!(points[5], [3.0, 2.0], epsilon = 1e-10);
1014 ```
1015 */
1016 pub fn translational_pattern(&mut self, shift: [f64; 2], repetitions: usize) -> () {
1017 let num_segments = self.num_segments();
1018 for rep in 0..repetitions {
1019 let f = rep as f64 + 1.0; // f for "factor"
1020 for seg_idx in 0..num_segments {
1021 // Index is always in bounds, because its maximum value is
1022 // the number of segments at the start of the outer loop
1023 // and no segments are removed from self.
1024 let mut segment = self[seg_idx].clone();
1025 segment.translate([shift[0] * f, shift[1] * f]);
1026 self.push_back(segment);
1027 }
1028 }
1029 }
1030}
1031
1032impl FromIterator<Segment> for Polysegment {
1033 fn from_iter<T: IntoIterator<Item = Segment>>(iter: T) -> Self {
1034 let mut polysegment = Polysegment::new();
1035 for segment in iter {
1036 polysegment.push_back(segment);
1037 }
1038 return polysegment;
1039 }
1040}
1041
1042impl crate::composite::private::Sealed for Polysegment {}
1043
1044impl Composite for Polysegment {
1045 fn segment(&self, key: SegmentKey) -> Option<&crate::segment::Segment> {
1046 return self.get(key.segment_idx);
1047 }
1048
1049 fn num_segments(&self) -> usize {
1050 return self.len();
1051 }
1052
1053 fn centroid(&self) -> [f64; 2] {
1054 return crate::CentroidData::from(self).into();
1055 }
1056
1057 fn iter<'a>(&'a self) -> impl Iterator<Item = (SegmentKey, &'a crate::segment::Segment)> {
1058 return self
1059 .segments()
1060 .enumerate()
1061 .map(|(i, s)| (SegmentKey::from_segment_idx(i), s));
1062 }
1063
1064 fn par_iter<'a>(
1065 &'a self,
1066 ) -> impl ParallelIterator<Item = (SegmentKey, &'a crate::segment::Segment)> {
1067 return self
1068 .segments_par()
1069 .enumerate()
1070 .map(|(i, s)| (SegmentKey::from_segment_idx(i), s));
1071 }
1072
1073 fn intersections_polysegment<'a>(
1074 &'a self,
1075 other: &'a Self,
1076 epsilon: f64,
1077 max_ulps: u32,
1078 ) -> impl Iterator<Item = Intersection> + 'a {
1079 let same_polysegment_len = if std::ptr::eq(self, other) {
1080 Some(self.num_segments())
1081 } else {
1082 None
1083 };
1084
1085 // Naive implementation: Loop over all segments of segments_self. Check each
1086 // segment of self for an intersection with all segments of
1087 // segments_other.
1088 return other
1089 .0
1090 .iter()
1091 .enumerate()
1092 .flat_map(move |(right_idx, right_seg)| {
1093 intersections_between_polysegment_and_segment_priv(
1094 self.0.iter(),
1095 right_idx,
1096 right_seg,
1097 same_polysegment_len,
1098 epsilon,
1099 max_ulps,
1100 )
1101 });
1102 }
1103
1104 fn intersections_polysegment_par<'a>(
1105 &'a self,
1106 other: &'a Self,
1107 epsilon: f64,
1108 max_ulps: u32,
1109 ) -> impl ParallelIterator<Item = Intersection> + 'a {
1110 let same_polysegment_len = if std::ptr::eq(self, other) {
1111 Some(self.num_segments())
1112 } else {
1113 None
1114 };
1115
1116 // Naive implementation: Loop over all segments of segments_self. Check each
1117 // segment of self for an intersection with all segments of
1118 // segments_other. The outer iteration is done in parallel,
1119 // therefore the intersections are out of order.
1120 return other
1121 .0
1122 .par_iter()
1123 .enumerate()
1124 .flat_map(move |(right_idx, right_seg)| {
1125 intersections_between_polysegment_and_segment_priv_par(
1126 self.0.par_iter(),
1127 right_idx,
1128 right_seg,
1129 same_polysegment_len,
1130 epsilon,
1131 max_ulps,
1132 )
1133 });
1134 }
1135
1136 fn intersections_shape<'a>(
1137 &'a self,
1138 shape: &'a crate::prelude::Shape,
1139 epsilon: f64,
1140 max_ulps: u32,
1141 ) -> impl Iterator<Item = Intersection> {
1142 shape
1143 .intersections_polysegment(self, epsilon, max_ulps)
1144 .map(Intersection::switch)
1145 }
1146
1147 fn intersections_shape_par<'a>(
1148 &'a self,
1149 shape: &'a crate::prelude::Shape,
1150 epsilon: f64,
1151 max_ulps: u32,
1152 ) -> impl ParallelIterator<Item = Intersection> {
1153 shape
1154 .intersections_polysegment_par(self, epsilon, max_ulps)
1155 .map(Intersection::switch)
1156 }
1157
1158 fn intersections_composite<'a, T: Composite>(
1159 &'a self,
1160 other: &'a T,
1161 epsilon: f64,
1162 max_ulps: u32,
1163 ) -> impl Iterator<Item = Intersection> + 'a
1164 where
1165 Self: Sized,
1166 {
1167 return other
1168 .intersections_polysegment(self, epsilon, max_ulps)
1169 .map(Intersection::switch);
1170 }
1171
1172 fn intersections_composite_par<'a, T: Composite>(
1173 &'a self,
1174 other: &'a T,
1175 epsilon: f64,
1176 max_ulps: u32,
1177 ) -> impl ParallelIterator<Item = Intersection> + 'a
1178 where
1179 Self: Sized,
1180 {
1181 return other
1182 .intersections_polysegment_par(self, epsilon, max_ulps)
1183 .map(Intersection::switch);
1184 }
1185
1186 fn covers_point(&self, point: [f64; 2], epsilon: f64, max_ulps: u32) -> bool {
1187 return self
1188 .intersections_primitive(&point, epsilon, max_ulps)
1189 .next()
1190 .is_some();
1191 }
1192
1193 fn covers_segment<'a, T: Into<SegmentRef<'a>>>(
1194 &self,
1195 segment: T,
1196 epsilon: f64,
1197 max_ulps: u32,
1198 ) -> bool {
1199 let segment: SegmentRef = segment.into();
1200 return covers_segment(self.0.iter(), segment, epsilon, max_ulps);
1201 }
1202
1203 fn contains_point(&self, _: [f64; 2], _: f64, _: u32) -> bool {
1204 // A polysegment has no surface area and therefore cannot contain a point.
1205 return false;
1206 }
1207
1208 fn contains_segment<'a, T: Into<SegmentRef<'a>>>(&self, _: T, _: f64, _: u32) -> bool {
1209 return false;
1210 }
1211
1212 fn covers_composite<'a, T: Composite>(
1213 &'a self,
1214 other: &'a T,
1215 epsilon: f64,
1216 max_ulps: u32,
1217 ) -> bool {
1218 return other.covers_polysegment(self, epsilon, max_ulps);
1219 }
1220
1221 fn contains_composite<'a, T: Composite>(
1222 &'a self,
1223 other: &'a T,
1224 epsilon: f64,
1225 max_ulps: u32,
1226 ) -> bool {
1227 return other.contains_polysegment(self, epsilon, max_ulps);
1228 }
1229
1230 fn overlaps_segment<'a, T: Into<SegmentRef<'a>>>(
1231 &self,
1232 _segment: T,
1233 _epsilon: f64,
1234 _max_ulps: u32,
1235 ) -> bool {
1236 return false;
1237 }
1238
1239 fn overlaps_contour(
1240 &self,
1241 _contour: &crate::prelude::Contour,
1242 _epsilon: f64,
1243 _max_ulps: u32,
1244 ) -> bool {
1245 return false;
1246 }
1247
1248 fn overlaps_shape(
1249 &self,
1250 _shape: &crate::prelude::Shape,
1251 _epsilon: f64,
1252 _max_ulps: u32,
1253 ) -> bool {
1254 return false;
1255 }
1256
1257 fn overlaps_composite<'a, T: Composite>(
1258 &'a self,
1259 _other: &'a T,
1260 _epsilon: f64,
1261 _max_ulps: u32,
1262 ) -> bool {
1263 return false;
1264 }
1265}
1266
1267impl std::ops::Index<usize> for Polysegment {
1268 type Output = Segment;
1269
1270 fn index(&self, index: usize) -> &Self::Output {
1271 &self.0[index]
1272 }
1273}
1274
1275impl From<Segment> for Polysegment {
1276 fn from(value: Segment) -> Self {
1277 let mut polysegment = Polysegment::new();
1278 polysegment.push_back(value);
1279 return polysegment;
1280 }
1281}
1282
1283impl From<LineSegment> for Polysegment {
1284 fn from(value: LineSegment) -> Self {
1285 return Polysegment::from(Segment::LineSegment(value));
1286 }
1287}
1288
1289impl From<ArcSegment> for Polysegment {
1290 fn from(value: ArcSegment) -> Self {
1291 return Polysegment::from(Segment::ArcSegment(value));
1292 }
1293}
1294
1295impl crate::Transformation for Polysegment {
1296 fn rotate(&mut self, center: [f64; 2], angle: f64) -> () {
1297 self.0.par_iter_mut().for_each(|segment| {
1298 segment.rotate(center, angle);
1299 })
1300 }
1301
1302 fn translate(&mut self, shift: [f64; 2]) -> () {
1303 self.0.par_iter_mut().for_each(|segment| {
1304 segment.translate(shift);
1305 })
1306 }
1307
1308 fn scale(&mut self, factor: f64) -> () {
1309 self.0.par_iter_mut().for_each(|segment| {
1310 segment.scale(factor);
1311 })
1312 }
1313
1314 fn line_reflection(&mut self, start: [f64; 2], stop: [f64; 2]) -> () {
1315 self.0.par_iter_mut().for_each(|segment| {
1316 segment.line_reflection(start, stop);
1317 })
1318 }
1319}
1320
1321impl ToBoundingBox for Polysegment {
1322 fn bounding_box(&self) -> BoundingBox {
1323 // Use the bounding box of the first segment as a starting point
1324 return self
1325 .segments_par()
1326 .skip(1)
1327 .map(|segment| BoundingBox::from(segment))
1328 .reduce(
1329 || {
1330 if let Some(s) = self.get(0) {
1331 BoundingBox::from(s)
1332 } else {
1333 BoundingBox::new(0.0, 0.0, 0.0, 0.0)
1334 }
1335 },
1336 |prev, curr| prev.union(&curr),
1337 );
1338 }
1339}
1340
1341impl IntoIterator for Polysegment {
1342 type Item = Segment;
1343 type IntoIter = std::collections::vec_deque::IntoIter<Segment>;
1344
1345 fn into_iter(self) -> Self::IntoIter {
1346 return self.0.into_iter();
1347 }
1348}
1349
1350impl From<Polysegment> for VecDeque<Segment> {
1351 fn from(line: Polysegment) -> Self {
1352 return line.0;
1353 }
1354}
1355
1356impl From<BoundingBox> for Polysegment {
1357 fn from(bounding_box: BoundingBox) -> Self {
1358 return Self::from(&bounding_box);
1359 }
1360}
1361
1362impl From<&BoundingBox> for Polysegment {
1363 fn from(bounding_box: &BoundingBox) -> Self {
1364 return Polysegment::from_points(&[
1365 [bounding_box.xmin(), bounding_box.ymin()],
1366 [bounding_box.xmax(), bounding_box.ymin()],
1367 [bounding_box.xmax(), bounding_box.ymax()],
1368 [bounding_box.xmin(), bounding_box.ymax()],
1369 [bounding_box.xmin(), bounding_box.ymin()],
1370 ]);
1371 }
1372}
1373
1374impl From<&Polysegment> for crate::CentroidData {
1375 fn from(value: &Polysegment) -> Self {
1376 return value
1377 .segments_par()
1378 .map(|segment| match segment {
1379 Segment::LineSegment(line) => Self::from(line),
1380 Segment::ArcSegment(arc) => Self::from(arc),
1381 })
1382 .reduce(
1383 || Self {
1384 area: 0.0,
1385 x: 0.0,
1386 y: 0.0,
1387 },
1388 |prev, curr| prev.union(&curr),
1389 );
1390 }
1391}
1392
1393/**
1394Calculates the area of a polygon defined by the points given by an iterator.
1395If the polygon orientation is mathematically positive (points are given
1396counterclockwise), then the result is also positive. Otherwise, it is negative.
1397
1398This implementation uses the "Shoelace formula", as e.g. described here:
1399<https://en.wikipedia.org/wiki/Shoelace_formula>.
1400
1401```
1402use planar_geo::polysegment::area_signed;
1403
1404let pt1 = [0.0, 0.0];
1405let pt2 = [1.0, 0.0];
1406let pt3 = [1.0, 1.0];
1407assert_eq!(
1408 area_signed([pt1, pt2, pt3].into_iter()),
1409 0.5
1410);
1411assert_eq!(
1412 area_signed([pt2, pt1, pt3].into_iter()),
1413 -0.5
1414);
1415```
1416 */
1417pub fn area_signed<'a, I>(mut points: I) -> f64
1418where
1419 I: Iterator<Item = [f64; 2]>,
1420{
1421 // Keep the first vertex
1422 let first_vertex = match points.next() {
1423 Some(vertex) => vertex,
1424 None => return 0.0,
1425 };
1426 let mut previous_vertex = first_vertex.clone();
1427
1428 let mut area = 0.0;
1429
1430 // The polysegmented element covers the end value x_n*y_0 - x_0*y_n, where n
1431 // equals the last iterator element
1432 for current_vertex in points.chain(std::iter::once(first_vertex)) {
1433 area += (previous_vertex[1] + current_vertex[1]) * (previous_vertex[0] - current_vertex[0]);
1434 previous_vertex = current_vertex.clone();
1435 }
1436
1437 return 0.5 * area; // Correction factor of 0.5 comes from the area formel of a triangle.
1438}
1439
1440pub(crate) fn covers_segment<'a, 'b, I>(
1441 iterator: I,
1442 segment: SegmentRef<'b>,
1443 epsilon: f64,
1444 max_ulps: u32,
1445) -> bool
1446where
1447 I: Iterator<Item = &'a Segment>,
1448{
1449 match segment {
1450 SegmentRef::LineSegment(line_segment) => {
1451 // Multiple subsequent line segments are combined into a single
1452 // one if they form a single line segment (i.e. their angles are
1453 // identical)
1454 let mut combined: Option<LineSegment> = None;
1455 for seg in iterator {
1456 if let Segment::LineSegment(sl) = seg {
1457 combined = match combined {
1458 Some(c) => {
1459 if ulps_eq!(
1460 c.angle(),
1461 sl.angle(),
1462 epsilon = epsilon,
1463 max_ulps = max_ulps
1464 ) {
1465 // Add the new line segment, if it has the same angle as
1466 // "combined"
1467 if let Ok(new_combined) =
1468 LineSegment::new(c.start(), sl.stop(), epsilon, max_ulps)
1469 {
1470 if new_combined.covers(line_segment, epsilon, max_ulps) {
1471 return true;
1472 }
1473 Some(new_combined)
1474 } else {
1475 None
1476 }
1477 } else {
1478 // Replace the old combined segment with the new line segment
1479 if sl.covers(line_segment, epsilon, max_ulps) {
1480 return true;
1481 }
1482 Some(sl.clone())
1483 }
1484 }
1485 None => {
1486 if sl.covers(line_segment, epsilon, max_ulps) {
1487 return true;
1488 }
1489 Some(sl.clone())
1490 }
1491 };
1492 } else {
1493 // Arc segment breaks the chain
1494 combined = None;
1495 }
1496 }
1497 return false;
1498 }
1499 SegmentRef::ArcSegment(arc_segment) => {
1500 // Multiple subsequent arc segments are combined into a single
1501 // one if they form a single arc segment
1502 // (i.e. their radius and center are identical).
1503 let mut combined: Option<ArcSegment> = None;
1504 for seg in iterator {
1505 if let Segment::ArcSegment(sa) = seg {
1506 combined = match combined {
1507 Some(c) => {
1508 if ulps_eq!(
1509 c.center(),
1510 sa.center(),
1511 epsilon = epsilon,
1512 max_ulps = max_ulps
1513 ) && ulps_eq!(
1514 c.radius(),
1515 sa.radius(),
1516 epsilon = epsilon,
1517 max_ulps = max_ulps
1518 ) {
1519 // Add the new line segment, if it has the same angle as
1520 // "combined"
1521 if let Ok(new_combined) =
1522 ArcSegment::from_center_radius_start_offset_angle(
1523 c.center(),
1524 c.radius(),
1525 c.start_angle(),
1526 c.offset_angle() + sa.offset_angle(),
1527 epsilon,
1528 max_ulps,
1529 )
1530 {
1531 if new_combined.covers(arc_segment, epsilon, max_ulps) {
1532 return true;
1533 }
1534 Some(new_combined)
1535 } else {
1536 None
1537 }
1538 } else {
1539 // Replace the old combined segment with the new line segment
1540 if sa.covers(arc_segment, epsilon, max_ulps) {
1541 return true;
1542 }
1543 Some(sa.clone())
1544 }
1545 }
1546 None => {
1547 if sa.covers(arc_segment, epsilon, max_ulps) {
1548 return true;
1549 }
1550 Some(sa.clone())
1551 }
1552 };
1553 } else {
1554 // Line segment breaks the chain
1555 combined = None;
1556 }
1557 }
1558 return false;
1559 }
1560 }
1561}
1562
1563fn intersections_between_polysegment_and_segment_priv<'a, L>(
1564 left: L,
1565 right_idx: usize,
1566 right_seg: &'a Segment,
1567 same_polysegment_len: Option<usize>,
1568 epsilon: f64,
1569 max_ulps: u32,
1570) -> impl Iterator<Item = Intersection> + 'a
1571where
1572 L: Iterator<Item = &'a Segment> + 'a,
1573{
1574 left.enumerate()
1575 .filter_map(move |(left_idx, left_seg)| {
1576 segment_intersections(
1577 left_seg,
1578 left_idx,
1579 right_seg,
1580 right_idx,
1581 same_polysegment_len,
1582 epsilon,
1583 max_ulps,
1584 )
1585 })
1586 .flatten()
1587}
1588
1589fn intersections_between_polysegment_and_segment_priv_par<'a, L>(
1590 left: L,
1591 right_idx: usize,
1592 right_seg: &'a Segment,
1593 same_polysegment_len: Option<usize>,
1594 epsilon: f64,
1595 max_ulps: u32,
1596) -> impl ParallelIterator<Item = Intersection> + 'a
1597where
1598 L: IndexedParallelIterator<Item = &'a Segment> + 'a,
1599{
1600 left.enumerate()
1601 .filter_map(move |(left_idx, left_seg)| {
1602 segment_intersections(
1603 left_seg,
1604 left_idx,
1605 right_seg,
1606 right_idx,
1607 same_polysegment_len,
1608 epsilon,
1609 max_ulps,
1610 )
1611 .map(|iter| iter.par_bridge())
1612 })
1613 .flatten()
1614}
1615
1616fn segment_intersections<'a>(
1617 left_seg: &'a Segment,
1618 left_idx: usize,
1619 right_seg: &'a Segment,
1620 right_idx: usize,
1621 same_polysegment_len: Option<usize>,
1622 epsilon: f64,
1623 max_ulps: u32,
1624) -> Option<impl Iterator<Item = Intersection> + 'a> {
1625 /*
1626 If the two segments are identical, they have no intersection by definition.
1627
1628 If slices_are_identical, an additional optimization is possible:
1629 It is sufficient to only check segments where left_idx < right_idx.
1630 This is possible because a naive loop implementation visits every segment pair twice:
1631 a) Left segment is in the outer loop, right segment is in the inner loop
1632 b) Left segment is in the outer loop, right segment is in the inner loop
1633 */
1634 if same_polysegment_len.is_some() {
1635 if left_idx == right_idx {
1636 return None;
1637 }
1638 if left_idx >= right_idx {
1639 return None;
1640 }
1641 } else {
1642 if left_seg == right_seg {
1643 return None;
1644 }
1645 }
1646
1647 let intersection_iter = left_seg
1648 .intersections_primitive(right_seg, epsilon, max_ulps)
1649 .into_iter()
1650 .filter_map(move |point| {
1651 let point: [f64; 2] = point.into();
1652
1653 /*
1654 If the two slices are identical, check whether left_seg is a
1655 successor / predecessor of right_seg. If yes, filter out the
1656 connection points.
1657 */
1658
1659 if let Some(len) = same_polysegment_len {
1660 /*
1661 This check evaluates whether the left segment is a predecessor
1662 of the right segment. If that is the case, filter out
1663 "intersections" which are actually just the connection point
1664 between the two segments.
1665 */
1666 if left_idx + 1 == right_idx || (left_idx == 0 && right_idx == len - 1) {
1667 if approx::ulps_eq!(
1668 point,
1669 left_seg.stop(),
1670 epsilon = epsilon,
1671 max_ulps = max_ulps
1672 ) {
1673 return None;
1674 }
1675 }
1676 }
1677
1678 Some(Intersection {
1679 point,
1680 left: SegmentKey::from_segment_idx(left_idx),
1681 right: SegmentKey::from_segment_idx(right_idx),
1682 })
1683 });
1684
1685 return Some(intersection_iter);
1686}