iron_shapes/simple_rpolygon.rs
1// Copyright (c) 2018-2020 Thomas Kramer.
2// SPDX-FileCopyrightText: 2018-2022 Thomas Kramer
3//
4// SPDX-License-Identifier: AGPL-3.0-or-later
5
6//! This module contains data types and functions for basic rectilinear polygons without holes.
7
8use crate::CoordinateType;
9
10use crate::point::Point;
11use crate::rect::Rect;
12
13pub use crate::traits::{DoubledOrientedArea, MapPointwise, TryBoundingBox, WindingNumber};
14
15use crate::types::*;
16
17use crate::prelude::SimpleTransform;
18use crate::redge::{REdge, REdgeOrientation};
19use crate::simple_polygon::SimplePolygon;
20use crate::traits::TryCastCoord;
21use num_traits::NumCast;
22use std::cmp::{Ord, PartialEq};
23
24/// A `SimpleRPolygon` is a rectilinear polygon. It does not contain holes but can be self-intersecting.
25/// The vertices are stored in an implicit format (one coordinate of two neighbour vertices is always the same
26/// for rectilinear polygons). This reduces memory usage but has the drawback that edges must
27/// alternate between horizontal and vertical. Vertices between two edges of the same orientation will
28/// be dropped.
29///
30#[derive(Clone, Debug, Hash, PartialEq, Eq)]
31#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
32pub struct SimpleRPolygon<T> {
33 /// Vertices of the polygon.
34 /// Begin with a y-coordinate. First edge is horizontal.
35 half_points: Vec<T>,
36 normalized: bool,
37}
38
39/// Shorthand notation for creating a simple polygon.
40/// # Example
41/// ```
42/// # #[macro_use]
43/// # extern crate iron_shapes;
44/// # fn main() {
45/// use iron_shapes::prelude::*;
46/// let p = simple_rpolygon!((0, 0), (1, 0), (1, 1), (0, 1));
47/// assert_eq!(Some(p), SimpleRPolygon::try_new(&vec![(0, 0), (1, 0), (1, 1), (0, 1)]));
48/// # }
49/// ```
50#[macro_export]
51macro_rules! simple_rpolygon {
52 ($($x:expr),*) => {SimpleRPolygon::try_new((&vec![$($x),*])).unwrap()}
53}
54
55impl<T> SimpleRPolygon<T> {
56 /// Create empty polygon without any vertices.
57 pub fn empty() -> Self {
58 Self {
59 half_points: Vec::new(),
60 normalized: true,
61 }
62 }
63
64 /// Get the number of vertices.
65 #[deprecated(note = "use len() instead")]
66 pub fn num_points(&self) -> usize {
67 self.half_points.len()
68 }
69
70 /// Get the number of vertices.
71 pub fn len(&self) -> usize {
72 self.half_points.len()
73 }
74
75 /// Check if polygon has no vertices.
76 pub fn is_empty(&self) -> bool {
77 self.half_points.is_empty()
78 }
79
80 /// Reverse the order of the vertices in-place.
81 pub fn reverse(&mut self) {
82 self.half_points.reverse();
83 self.half_points.rotate_right(1);
84 }
85
86 /// Get index of previous half-point.
87 fn prev(&self, i: usize) -> usize {
88 match i {
89 0 => self.half_points.len() - 1,
90 x => x - 1,
91 }
92 }
93
94 /// Get index of next half-point.
95 fn next(&self, i: usize) -> usize {
96 match i {
97 _ if i == self.half_points.len() - 1 => 0,
98 x => x + 1,
99 }
100 }
101}
102
103impl<T> SimpleRPolygon<T> {
104 /// Reverse the order of vertices.
105 pub fn reversed(mut self) -> Self {
106 self.reverse();
107 self
108 }
109}
110
111impl<T: Copy> SimpleRPolygon<T> {
112 /// Get `i`-th point of the polygon.
113 fn get_point(&self, i: usize) -> Point<T> {
114 if i % 2 == 0 {
115 Point::new(self.half_points[self.prev(i)], self.half_points[i])
116 } else {
117 Point::new(self.half_points[i], self.half_points[self.prev(i)])
118 }
119 }
120
121 /// Iterate over the points.
122 pub fn points(
123 &self,
124 ) -> impl DoubleEndedIterator<Item = Point<T>> + ExactSizeIterator + Clone + '_ {
125 (0..self.len()).map(move |i| self.get_point(i))
126 }
127
128 /// Get all exterior edges of the polygon.
129 /// # Examples
130 ///
131 /// ```
132 /// use iron_shapes::simple_rpolygon::SimpleRPolygon;
133 /// use iron_shapes::redge::REdge;
134 /// let coords = vec![(0, 0), (1, 0), (1, 1), (0, 1)];
135 ///
136 /// let poly = SimpleRPolygon::try_new(&coords).unwrap();
137 /// let edges: Vec<_> = poly.edges().collect();
138 /// assert_eq!(edges, vec![
139 /// REdge::new((0, 0), (1, 0)),
140 /// REdge::new((1, 0), (1, 1)),
141 /// REdge::new((1, 1), (0, 1)),
142 /// REdge::new((0, 1), (0, 0)),
143 /// ]);
144 ///
145 /// ```
146 pub fn edges(&self) -> impl Iterator<Item = REdge<T>> + '_ {
147 (0..self.len()).map(move |i| {
148 let orientation = if i % 2 == 0 {
149 REdgeOrientation::Horizontal
150 } else {
151 REdgeOrientation::Vertical
152 };
153
154 REdge::new_raw(
155 self.half_points[self.prev(i)],
156 self.half_points[self.next(i)],
157 self.half_points[i],
158 orientation,
159 )
160 })
161 }
162
163 /// Get all exterior edges of the polygon.
164 /// In contrast to [`SimpleRPolygon::edges`] this method takes ownership of the polygon.
165 ///
166 /// # Examples
167 ///
168 /// ```
169 /// use iron_shapes::simple_rpolygon::SimpleRPolygon;
170 /// use iron_shapes::redge::REdge;
171 /// let coords = vec![(0, 0), (1, 0), (1, 1), (0, 1)];
172 ///
173 /// let poly = SimpleRPolygon::try_new(&coords).unwrap();
174 /// let edges: Vec<_> = poly.edges().collect();
175 /// assert_eq!(edges, vec![
176 /// REdge::new((0, 0), (1, 0)),
177 /// REdge::new((1, 0), (1, 1)),
178 /// REdge::new((1, 1), (0, 1)),
179 /// REdge::new((0, 1), (0, 0)),
180 /// ]);
181 ///
182 /// ```
183 pub fn into_edges(self) -> impl Iterator<Item = REdge<T>> {
184 (0..self.len()).map(move |i| {
185 let orientation = if i % 2 == 0 {
186 REdgeOrientation::Horizontal
187 } else {
188 REdgeOrientation::Vertical
189 };
190
191 REdge::new_raw(
192 self.half_points[self.prev(i)],
193 self.half_points[self.next(i)],
194 self.half_points[i],
195 orientation,
196 )
197 })
198 }
199}
200
201impl<T: Copy + PartialEq> SimpleRPolygon<T> {
202 /// Create new rectilinear polygon from points.
203 /// Returns `None` if the polygon defined by the points is not rectilinear.
204 /// ```
205 /// use iron_shapes::simple_rpolygon::SimpleRPolygon;
206 ///
207 /// let poly1 = SimpleRPolygon::try_new(&vec![(0, 0), (1, 0), (1, 1), (0, 1)]);
208 /// assert!(poly1.is_some());
209 ///
210 /// // A triangle cannot be rectilinear.
211 /// let poly1 = SimpleRPolygon::try_new(&vec![(0, 0), (1, 0), (1, 1)]);
212 /// assert!(poly1.is_none());
213 ///
214 /// ```
215 pub fn try_new<I, Points, P>(points: I) -> Option<Self>
216 where
217 I: IntoIterator<Item = P, IntoIter = Points>,
218 Points: DoubleEndedIterator<Item = P> + ExactSizeIterator + Clone,
219 P: Copy + Into<Point<T>>,
220 {
221 let points = points.into_iter();
222
223 if let Some(last) = points.clone().last() {
224 let mut half_points = Vec::new();
225
226 let mut last: Point<T> = last.into();
227 #[derive(PartialEq, Eq, Debug)]
228 enum Orientation {
229 None,
230 Vertical,
231 Horizontal,
232 }
233
234 let mut orientation = Orientation::None;
235
236 let num_points = points.len();
237 for p in points.cycle().take(num_points + 1) {
238 let p: Point<T> = p.into();
239 match (last.x == p.x, last.y == p.y) {
240 (true, true) => {
241 // Same point. Do nothing.
242 }
243 (false, false) => {
244 // Not rectilinear.
245 return None;
246 }
247 (false, true) => {
248 // Horizontal line. Store x.
249 if orientation != Orientation::Horizontal {
250 // Corner.
251 if orientation == Orientation::Vertical {
252 half_points.push(last.x);
253 }
254 orientation = Orientation::Horizontal;
255 }
256 }
257 (true, false) => {
258 // Vertical line.
259 if orientation != Orientation::Vertical {
260 // Corner.
261 if orientation == Orientation::Horizontal {
262 half_points.push(last.y);
263 }
264 orientation = Orientation::Vertical;
265 }
266 }
267 }
268 last = p;
269 }
270
271 debug_assert!(half_points.len() % 2 == 0);
272
273 if orientation == Orientation::Vertical {
274 // Last edge was horizontal.
275 // Make sure to *start* with a horizontal edge.
276
277 if !half_points.is_empty() {
278 half_points.rotate_left(1);
279 }
280 }
281 Some(Self {
282 half_points,
283 normalized: false,
284 })
285 } else {
286 // Empty polygon.
287 Some(Self::empty())
288 }
289 }
290}
291
292impl<T: CoordinateType> SimpleRPolygon<T> {
293 /// Apply the transformation to this rectilinear polygon.
294 pub fn transformed(&self, tf: &SimpleTransform<T>) -> Self {
295 let points = self.points().map(|p| tf.transform_point(p));
296 // Unwrap should be fine because the edges will remain axis-aligned under the Simple Transform.
297 Self::try_new(points).unwrap()
298 }
299
300 /// Convert to a `SimplePolygon`.
301 pub fn to_simple_polygon(&self) -> SimplePolygon<T> {
302 SimplePolygon::new(self.points().collect())
303 }
304
305 /// Get the convex hull of the polygon.
306 ///
307 /// Implements Andrew's Monotone Chain algorithm.
308 /// See: <http://geomalgorithms.com/a10-_hull-1.html>
309 pub fn convex_hull(&self) -> SimplePolygon<T>
310 where
311 T: Ord,
312 {
313 crate::algorithms::convex_hull::convex_hull(self.points().collect())
314 }
315
316 /// Get the vertex with lowest x-coordinate. Prefer lower y-coordinates to break ties.
317 ///
318 /// # Examples
319 ///
320 /// ```
321 /// use iron_shapes::simple_rpolygon::SimpleRPolygon;
322 /// use iron_shapes::point::Point;
323 /// let coords = vec![(0, 0), (1, 0), (1, 1), (0, 1)];
324 ///
325 /// let poly = SimpleRPolygon::try_new(&coords).unwrap();
326 ///
327 /// assert_eq!(poly.lower_left_vertex(), Point::new(0, 0));
328 ///
329 /// ```
330 pub fn lower_left_vertex(&self) -> Point<T> {
331 debug_assert!(!self.is_empty());
332
333 self.lower_left_vertex_with_index().1
334 }
335
336 /// Get the vertex with lowest x-coordinate and its index.
337 /// Prefer lower y-coordinates to break ties.
338 fn lower_left_vertex_with_index(&self) -> (usize, Point<T>) {
339 debug_assert!(!self.is_empty());
340
341 // Find minimum.
342 let min = self
343 .points()
344 .enumerate()
345 .min_by(|(_, p1), (_, p2)| p1.partial_cmp(p2).unwrap());
346 let (idx, point) = min.unwrap();
347
348 (idx, point)
349 }
350
351 /// Get the orientation of the polygon,
352 /// i.e. check if it is wound clock-wise or counter-clock-wise.
353 ///
354 /// # Examples
355 ///
356 /// ```
357 /// use iron_shapes::simple_rpolygon::SimpleRPolygon;
358 /// use iron_shapes::point::Point;
359 /// use iron_shapes::types::Orientation;
360 /// let coords = vec![(0, 0), (1, 0), (1, 1), (0, 1)];
361 ///
362 /// let poly = SimpleRPolygon::try_new(&coords).unwrap();
363 ///
364 /// assert_eq!(poly.orientation(), Orientation::CounterClockWise);
365 ///
366 /// ```
367 pub fn orientation(&self) -> Orientation {
368 // Find the orientation by the polygon area.
369
370 let area2 = self.area_doubled_oriented();
371
372 if area2 > T::zero() {
373 Orientation::CounterClockWise
374 } else if area2 < T::zero() {
375 Orientation::ClockWise
376 } else {
377 debug_assert!(area2 == T::zero());
378 Orientation::Straight
379 }
380 }
381
382 // TODO:
383 // /// Decompose into non-overlapping rectangles.
384 // pub fn decompose_rectangles(&self) -> Vec<Rect<T>> {
385 // // Get vertical edges and order them by their lower end.
386 // struct Vertical {
387 // x: T,
388 // y_low: T,
389 // y_high: T,
390 // }
391 // impl Ord for Vertical {
392 // fn cmp(&self, other: &Self) -> Ordering {
393 //
394 // }
395 // }
396 // let mut verticals = BinaryHeap::new();
397 // self.edges().filter(|e| e.is_vertical())
398 // .for_each(|e| {
399 // let (y_low, y_high) = if e.start < e.end {
400 // (e.start, e.end)
401 // } else {
402 // (e.end, e.start)
403 // };
404 // let v = Vertical {
405 // x: e.offset,
406 // y_low,
407 // y_high,
408 // };
409 // verticals.push(v);
410 // });
411 // unimplemented!()
412 // }
413}
414
415impl<T> WindingNumber<T> for SimpleRPolygon<T>
416where
417 T: CoordinateType,
418{
419 /// Calculate the winding number of the polygon around this point.
420 ///
421 /// TODO: Define how point on edges and vertices is handled.
422 ///
423 /// See: <http://geomalgorithms.com/a03-_inclusion.html>
424 fn winding_number(&self, point: Point<T>) -> isize {
425 let mut winding_number = 0isize;
426
427 // Edge Crossing Rules
428 //
429 // 1. an upward edge includes its starting endpoint, and excludes its final endpoint;
430 // 2. a downward edge excludes its starting endpoint, and includes its final endpoint;
431 // 3. horizontal edges are excluded
432 // 4. the edge-ray intersection point must be strictly right of the point P.
433
434 for e in self.edges() {
435 if e.start().y <= point.y {
436 // Crosses upward?
437 if e.end().y > point.y {
438 // Crosses really upward?
439 // Yes, crosses upward.
440 if e.side_of(point) == Side::Left {
441 winding_number += 1;
442 }
443 }
444 } else if e.end().y <= point.y {
445 // Crosses downward?
446 // Yes, crosses downward.
447 // `e.start.y > point.y` needs not to be checked anymore.
448 if e.side_of(point) == Side::Right {
449 winding_number -= 1;
450 }
451 }
452 }
453
454 winding_number
455 }
456}
457
458impl<T: CoordinateType> From<Rect<T>> for SimpleRPolygon<T> {
459 fn from(r: Rect<T>) -> Self {
460 Self {
461 half_points: vec![
462 r.lower_left.y,
463 r.upper_right.x,
464 r.upper_right.y,
465 r.lower_left.x,
466 ],
467 normalized: true,
468 }
469 }
470}
471
472#[test]
473fn test_from_rect() {
474 use super::rect::Rect;
475 let r = Rect::new((0, 1), (2, 3));
476 let p = SimpleRPolygon::from(r);
477 assert_eq!(
478 p.points().collect::<Vec<_>>(),
479 [
480 Point::new(0, 1),
481 Point::new(2, 1),
482 Point::new(2, 3),
483 Point::new(0, 3)
484 ]
485 );
486
487 assert_eq!(p, p.clone().normalized());
488}
489
490// /// Create a polygon from a type that is convertible into an iterator of values convertible to `Point`s.
491// impl<I, T, P> TryFrom<I> for SimpleRPolygon<T>
492// where T: CoordinateType,
493// I: IntoIterator<Item=P>,
494// Point<T>: From<P>
495// {
496// type Error = ();
497// /// Create a polygon from a type that is convertible into an iterator of values convertible to `Point`s.
498// /// Return `None` if the polygon is not rectilinear.
499// fn try_from(iter: I) -> Result<Self, ()> {
500// let points: Vec<Point<T>> = iter.into_iter().map(
501// |x| x.into()
502// ).collect();
503//
504// match SimpleRPolygon::try_new(points) {
505// None => Err(()),
506// Some(p) => Ok(p)
507// }
508// }
509// }
510
511//
512// /// Create a polygon from a `Vec` of values convertible to `Point`s.
513// impl<'a, T, P> From<&'a Vec<P>> for SimplePolygon<T>
514// where T: CoordinateType,
515// Point<T>: From<&'a P>
516// {
517// fn from(vec: &'a Vec<P>) -> Self {
518// let points: Vec<Point<T>> = vec.into_iter().map(
519// |x| x.into()
520// ).collect();
521//
522// SimplePolygon { points }
523// }
524// }
525//
526// /// Create a polygon from a `Vec` of values convertible to `Point`s.
527// impl<T, P> From<Vec<P>> for SimplePolygon<T>
528// where T: CoordinateType,
529// Point<T>: From<P>
530// {
531// fn from(vec: Vec<P>) -> Self {
532// let points: Vec<Point<T>> = vec.into_iter().map(
533// |x| x.into()
534// ).collect();
535//
536// SimplePolygon { points }
537// }
538// }
539
540impl<T> SimpleRPolygon<T>
541where
542 T: Copy + PartialOrd,
543{
544 /// Check if the polygon is an axis-aligned rectangle.
545 pub fn is_rect(&self) -> bool {
546 self.len() == 4
547 }
548}
549
550impl<T> TryBoundingBox<T> for SimpleRPolygon<T>
551where
552 T: Copy + PartialOrd,
553{
554 fn try_bounding_box(&self) -> Option<Rect<T>> {
555 if !self.is_empty() {
556 let mut xmax = self.half_points[1];
557 let mut ymax = self.half_points[0];
558 let mut xmin = xmax;
559 let mut ymin = ymax;
560 self.half_points.chunks(2).for_each(|c| {
561 let x = c[1];
562 let y = c[0];
563 if x > xmax {
564 xmax = x
565 };
566 if x < xmin {
567 xmin = x
568 };
569 if y > ymax {
570 ymax = y
571 };
572 if y < ymin {
573 ymin = y
574 };
575 });
576
577 Some(Rect::new((xmin, ymin), (xmax, ymax)))
578 } else {
579 None
580 }
581 }
582}
583
584impl<T: CoordinateType> DoubledOrientedArea<T> for SimpleRPolygon<T> {
585 /// Calculates the doubled oriented area.
586 ///
587 /// Using doubled area allows to compute in the integers because the area
588 /// of a polygon with integer coordinates is either integer or half-integer.
589 ///
590 /// The area will be positive if the vertices are listed counter-clockwise,
591 /// negative otherwise.
592 ///
593 /// Complexity: O(n)
594 ///
595 /// # Examples
596 ///
597 /// ```
598 /// use iron_shapes::traits::DoubledOrientedArea;
599 /// use iron_shapes::simple_rpolygon::SimpleRPolygon;
600 /// let coords = vec![(0, 0), (1, 0), (1, 1), (0, 1)];
601 ///
602 /// let poly = SimpleRPolygon::try_new(&coords).unwrap();
603 ///
604 /// assert_eq!(poly.area_doubled_oriented(), 2);
605 ///
606 /// ```
607 fn area_doubled_oriented(&self) -> T {
608 debug_assert!(self.half_points.len() % 2 == 0);
609 // Iterate over all horizontal edges. Compute the area
610 // as the sum of (oriented edge length) * (edge distance to origin).
611 let area: T = (0..self.len())
612 .step_by(2)
613 .map(move |i| {
614 let start = self.half_points[self.prev(i)];
615 let end = self.half_points[self.next(i)];
616 let offset = self.half_points[i];
617
618 (start - end) * offset
619 })
620 .fold(T::zero(), |acc, area| acc + area);
621
622 area + area
623 }
624}
625
626impl<T: PartialOrd> SimpleRPolygon<T> {
627 /// Rotate the vertices to get the lexicographically smallest polygon.
628 /// Does not change the orientation.
629 pub fn normalize(&mut self) {
630 let rotated = |rot: usize| {
631 self.half_points
632 .iter()
633 .cycle()
634 .skip(rot)
635 .take(self.half_points.len())
636 };
637 let best_rot =
638 (0..self.half_points.len() / 2)
639 .skip(1)
640 .map(|r| r * 2)
641 .fold(0, |best_rot, rot| {
642 if rotated(rot).lt(rotated(best_rot)) {
643 rot
644 } else {
645 best_rot
646 }
647 });
648 self.half_points.rotate_left(best_rot);
649 }
650
651 /// Rotate the vertices to get the lexicographically smallest polygon.
652 /// Does not change the orientation.
653 pub fn normalized(mut self) -> Self {
654 self.normalize();
655 self
656 }
657}
658
659impl<T: PartialEq> SimpleRPolygon<T> {
660 /// Equality test for simple polygons.
661 ///
662 /// Two polygons are equal iff a cyclic shift on their vertices can be applied
663 /// such that the both lists of vertices match exactly.
664 pub fn normalized_eq(&self, other: &Self) -> bool {
665 if self.len() != other.len() {
666 false
667 } else {
668 let n = self.half_points.len();
669 debug_assert!(n % 2 == 0);
670 (0..n / 2).any(|rot| {
671 let rotated = self.half_points.iter().cycle().skip(rot * 2).take(n);
672 rotated.eq(other.half_points.iter())
673 })
674 }
675 }
676}
677
678impl<T: CoordinateType + NumCast, Dst: CoordinateType + NumCast> TryCastCoord<T, Dst>
679 for SimpleRPolygon<T>
680{
681 type Output = SimpleRPolygon<Dst>;
682
683 fn try_cast(&self) -> Option<Self::Output> {
684 let new_half_points: Option<Vec<_>> =
685 self.half_points.iter().map(|&p| Dst::from(p)).collect();
686
687 new_half_points.map(|p| SimpleRPolygon {
688 half_points: p,
689 normalized: self.normalized,
690 })
691 }
692}
693
694#[test]
695fn test_create_rpolygon() {
696 let p = SimpleRPolygon::try_new(&vec![(0, 0), (1, 0), (1, 2), (0, 2)]).unwrap();
697 assert_eq!(p.half_points, vec![0, 1, 2, 0]);
698
699 let p = SimpleRPolygon::try_new(&vec![(1, 0), (1, 2), (0, 2), (0, 0)]).unwrap();
700 assert_eq!(p.half_points, vec![0, 1, 2, 0]);
701
702 // Zero-area polygon is converted to an empty polygon.
703 let p = SimpleRPolygon::try_new(&vec![(0, 1), (0, 0)]).unwrap();
704 assert_eq!(p.half_points, vec![]);
705
706 // Intermediate vertices on straight lines are removed.
707 let p = SimpleRPolygon::try_new(&vec![(0, 0), (1, 0), (1, 1), (1, 2), (0, 2), (0, 1)]).unwrap();
708
709 assert_eq!(p.half_points, vec![0, 1, 2, 0]);
710}
711
712/// Two simple polygons should be the same even if points are shifted cyclical.
713#[test]
714fn test_partial_eq() {
715 let p1 = simple_rpolygon!((0, 0), (0, 1), (1, 1), (1, 0));
716 let p2 = simple_rpolygon!((0, 0), (0, 1), (1, 1), (1, 0));
717 assert!(p1.normalized_eq(&p2));
718
719 let p2 = simple_rpolygon!((0, 1), (1, 1), (1, 0), (0, 0));
720 assert!(p1.normalized_eq(&p2));
721}
722
723/// Simple sanity check for computation of bounding box.
724#[test]
725fn test_bounding_box() {
726 let p = simple_rpolygon!((0, 1), (2, 1), (2, 3), (0, 3));
727 assert_eq!(p.try_bounding_box(), Some(Rect::new((0, 1), (2, 3))));
728}
729
730#[test]
731fn test_reversed() {
732 let p = simple_rpolygon!((0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (0, 2));
733 let p_rev_expected = simple_rpolygon!((0, 2), (2, 2), (2, 1), (1, 1), (1, 0), (0, 0));
734 let p_rev_actual = p.clone().reversed();
735 assert!(p_rev_actual.normalized_eq(&p_rev_expected));
736 assert_eq!(
737 p.clone().reversed().reversed().normalized().half_points,
738 p.normalized().half_points
739 )
740}