bevy_math/primitives/
dim2.rs

1use core::f32::consts::{FRAC_1_SQRT_2, FRAC_PI_2, FRAC_PI_3, PI};
2use derive_more::derive::From;
3#[cfg(feature = "alloc")]
4use thiserror::Error;
5
6use super::{Measured2d, Primitive2d, WindingOrder};
7use crate::{
8    ops::{self, FloatPow},
9    primitives::Inset,
10    Dir2, InvalidDirectionError, Isometry2d, Ray2d, Rot2, Vec2,
11};
12
13#[cfg(feature = "alloc")]
14use super::polygon::is_polygon_simple;
15
16#[cfg(feature = "bevy_reflect")]
17use bevy_reflect::{std_traits::ReflectDefault, Reflect};
18#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
19use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
20
21#[cfg(feature = "alloc")]
22use alloc::vec::Vec;
23
24/// A circle primitive, representing the set of points some distance from the origin
25#[derive(Clone, Copy, Debug, PartialEq)]
26#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
27#[cfg_attr(
28    feature = "bevy_reflect",
29    derive(Reflect),
30    reflect(Debug, PartialEq, Default, Clone)
31)]
32#[cfg_attr(
33    all(feature = "serialize", feature = "bevy_reflect"),
34    reflect(Serialize, Deserialize)
35)]
36pub struct Circle {
37    /// The radius of the circle
38    pub radius: f32,
39}
40
41impl Primitive2d for Circle {}
42
43impl Default for Circle {
44    /// Returns the default [`Circle`] with a radius of `0.5`.
45    fn default() -> Self {
46        Self { radius: 0.5 }
47    }
48}
49
50impl Circle {
51    /// Create a new [`Circle`] from a `radius`
52    #[inline]
53    pub const fn new(radius: f32) -> Self {
54        Self { radius }
55    }
56
57    /// Get the diameter of the circle
58    #[inline]
59    pub const fn diameter(&self) -> f32 {
60        2.0 * self.radius
61    }
62
63    /// Finds the point on the circle that is closest to the given `point`.
64    ///
65    /// If the point is outside the circle, the returned point will be on the perimeter of the circle.
66    /// Otherwise, it will be inside the circle and returned as is.
67    #[inline]
68    pub fn closest_point(&self, point: Vec2) -> Vec2 {
69        let distance_squared = point.length_squared();
70
71        if distance_squared <= self.radius.squared() {
72            // The point is inside the circle.
73            point
74        } else {
75            // The point is outside the circle.
76            // Find the closest point on the perimeter of the circle.
77            let dir_to_point = point / ops::sqrt(distance_squared);
78            self.radius * dir_to_point
79        }
80    }
81}
82
83impl Measured2d for Circle {
84    /// Get the area of the circle
85    #[inline]
86    fn area(&self) -> f32 {
87        PI * self.radius.squared()
88    }
89
90    /// Get the perimeter or circumference of the circle
91    #[inline]
92    #[doc(alias = "circumference")]
93    fn perimeter(&self) -> f32 {
94        2.0 * PI * self.radius
95    }
96}
97
98/// A primitive representing an arc between two points on a circle.
99///
100/// An arc has no area.
101/// If you want to include the portion of a circle's area swept out by the arc,
102/// use the pie-shaped [`CircularSector`].
103/// If you want to include only the space inside the convex hull of the arc,
104/// use the bowl-shaped [`CircularSegment`].
105///
106/// The arc is drawn starting from [`Vec2::Y`], extending by `half_angle` radians on
107/// either side. The center of the circle is the origin [`Vec2::ZERO`]. Note that this
108/// means that the origin may not be within the `Arc2d`'s convex hull.
109///
110/// **Warning:** Arcs with negative angle or radius, or with angle greater than an entire circle, are not officially supported.
111/// It is recommended to normalize arcs to have an angle in [0, 2π].
112#[derive(Clone, Copy, Debug, PartialEq)]
113#[doc(alias("CircularArc", "CircleArc"))]
114#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
115#[cfg_attr(
116    feature = "bevy_reflect",
117    derive(Reflect),
118    reflect(Debug, PartialEq, Default, Clone)
119)]
120#[cfg_attr(
121    all(feature = "serialize", feature = "bevy_reflect"),
122    reflect(Serialize, Deserialize)
123)]
124pub struct Arc2d {
125    /// The radius of the circle
126    pub radius: f32,
127    /// Half the angle defining the arc
128    pub half_angle: f32,
129}
130
131impl Primitive2d for Arc2d {}
132
133impl Default for Arc2d {
134    /// Returns the default [`Arc2d`] with radius `0.5`, covering one third of a circle
135    fn default() -> Self {
136        Self {
137            radius: 0.5,
138            half_angle: 2.0 * FRAC_PI_3,
139        }
140    }
141}
142
143impl Arc2d {
144    /// Create a new [`Arc2d`] from a `radius` and a `half_angle`
145    #[inline]
146    pub const fn new(radius: f32, half_angle: f32) -> Self {
147        Self { radius, half_angle }
148    }
149
150    /// Create a new [`Arc2d`] from a `radius` and an `angle` in radians
151    #[inline]
152    pub const fn from_radians(radius: f32, angle: f32) -> Self {
153        Self {
154            radius,
155            half_angle: angle / 2.0,
156        }
157    }
158
159    /// Create a new [`Arc2d`] from a `radius` and an `angle` in degrees.
160    #[inline]
161    pub const fn from_degrees(radius: f32, angle: f32) -> Self {
162        Self {
163            radius,
164            half_angle: angle.to_radians() / 2.0,
165        }
166    }
167
168    /// Create a new [`Arc2d`] from a `radius` and a `fraction` of a single turn.
169    ///
170    /// For instance, `0.5` turns is a semicircle.
171    #[inline]
172    pub const fn from_turns(radius: f32, fraction: f32) -> Self {
173        Self {
174            radius,
175            half_angle: fraction * PI,
176        }
177    }
178
179    /// Get the angle of the arc
180    #[inline]
181    pub const fn angle(&self) -> f32 {
182        self.half_angle * 2.0
183    }
184
185    /// Get the length of the arc
186    #[inline]
187    pub const fn length(&self) -> f32 {
188        self.angle() * self.radius
189    }
190
191    /// Get the right-hand end point of the arc
192    #[inline]
193    pub fn right_endpoint(&self) -> Vec2 {
194        self.radius * Vec2::from_angle(FRAC_PI_2 - self.half_angle)
195    }
196
197    /// Get the left-hand end point of the arc
198    #[inline]
199    pub fn left_endpoint(&self) -> Vec2 {
200        self.radius * Vec2::from_angle(FRAC_PI_2 + self.half_angle)
201    }
202
203    /// Get the endpoints of the arc
204    #[inline]
205    pub fn endpoints(&self) -> [Vec2; 2] {
206        [self.left_endpoint(), self.right_endpoint()]
207    }
208
209    /// Get the midpoint of the arc
210    #[inline]
211    pub fn midpoint(&self) -> Vec2 {
212        self.radius * Vec2::Y
213    }
214
215    /// Get half the distance between the endpoints (half the length of the chord)
216    #[inline]
217    pub fn half_chord_length(&self) -> f32 {
218        self.radius * ops::sin(self.half_angle)
219    }
220
221    /// Get the distance between the endpoints (the length of the chord)
222    #[inline]
223    pub fn chord_length(&self) -> f32 {
224        2.0 * self.half_chord_length()
225    }
226
227    /// Get the midpoint of the two endpoints (the midpoint of the chord)
228    #[inline]
229    pub fn chord_midpoint(&self) -> Vec2 {
230        self.apothem() * Vec2::Y
231    }
232
233    /// Get the length of the apothem of this arc, that is,
234    /// the distance from the center of the circle to the midpoint of the chord, in the direction of the midpoint of the arc.
235    /// Equivalently, the [`radius`](Self::radius) minus the [`sagitta`](Self::sagitta).
236    ///
237    /// Note that for a [`major`](Self::is_major) arc, the apothem will be negative.
238    #[inline]
239    // Naming note: Various sources are inconsistent as to whether the apothem is the segment between the center and the
240    // midpoint of a chord, or the length of that segment. Given this confusion, we've opted for the definition
241    // used by Wolfram MathWorld, which is the distance rather than the segment.
242    pub fn apothem(&self) -> f32 {
243        let sign = if self.is_minor() { 1.0 } else { -1.0 };
244        sign * ops::sqrt(self.radius.squared() - self.half_chord_length().squared())
245    }
246
247    /// Get the length of the sagitta of this arc, that is,
248    /// the length of the line between the midpoints of the arc and its chord.
249    /// Equivalently, the height of the triangle whose base is the chord and whose apex is the midpoint of the arc.
250    ///
251    /// The sagitta is also the sum of the [`radius`](Self::radius) and the [`apothem`](Self::apothem).
252    pub fn sagitta(&self) -> f32 {
253        self.radius - self.apothem()
254    }
255
256    /// Produces true if the arc is at most half a circle.
257    ///
258    /// **Note:** This is not the negation of [`is_major`](Self::is_major): an exact semicircle is both major and minor.
259    #[inline]
260    pub const fn is_minor(&self) -> bool {
261        self.half_angle <= FRAC_PI_2
262    }
263
264    /// Produces true if the arc is at least half a circle.
265    ///
266    /// **Note:** This is not the negation of [`is_minor`](Self::is_minor): an exact semicircle is both major and minor.
267    #[inline]
268    pub const fn is_major(&self) -> bool {
269        self.half_angle >= FRAC_PI_2
270    }
271}
272
273/// A primitive representing a circular sector: a pie slice of a circle.
274///
275/// The segment is positioned so that it always includes [`Vec2::Y`] and is vertically symmetrical.
276/// To orient the sector differently, apply a rotation.
277/// The sector is drawn with the center of its circle at the origin [`Vec2::ZERO`].
278///
279/// **Warning:** Circular sectors with negative angle or radius, or with angle greater than an entire circle, are not officially supported.
280/// We recommend normalizing circular sectors to have an angle in [0, 2π].
281#[derive(Clone, Copy, Debug, PartialEq, From)]
282#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
283#[cfg_attr(
284    feature = "bevy_reflect",
285    derive(Reflect),
286    reflect(Debug, PartialEq, Default, Clone)
287)]
288#[cfg_attr(
289    all(feature = "serialize", feature = "bevy_reflect"),
290    reflect(Serialize, Deserialize)
291)]
292pub struct CircularSector {
293    /// The arc defining the sector
294    #[cfg_attr(all(feature = "serialize", feature = "alloc"), serde(flatten))]
295    pub arc: Arc2d,
296}
297
298impl Primitive2d for CircularSector {}
299
300impl Default for CircularSector {
301    /// Returns the default [`CircularSector`] with radius `0.5` and covering a third of a circle
302    fn default() -> Self {
303        Self::from(Arc2d::default())
304    }
305}
306
307impl Measured2d for CircularSector {
308    #[inline]
309    fn area(&self) -> f32 {
310        self.arc.radius.squared() * self.arc.half_angle
311    }
312
313    #[inline]
314    fn perimeter(&self) -> f32 {
315        if self.half_angle() >= PI {
316            self.arc.radius * 2.0 * PI
317        } else {
318            2.0 * self.radius() + self.arc_length()
319        }
320    }
321}
322
323impl CircularSector {
324    /// Create a new [`CircularSector`] from a `radius` and an `angle`
325    #[inline]
326    pub const fn new(radius: f32, angle: f32) -> Self {
327        Self {
328            arc: Arc2d::new(radius, angle),
329        }
330    }
331
332    /// Create a new [`CircularSector`] from a `radius` and an `angle` in radians.
333    #[inline]
334    pub const fn from_radians(radius: f32, angle: f32) -> Self {
335        Self {
336            arc: Arc2d::from_radians(radius, angle),
337        }
338    }
339
340    /// Create a new [`CircularSector`] from a `radius` and an `angle` in degrees.
341    #[inline]
342    pub const fn from_degrees(radius: f32, angle: f32) -> Self {
343        Self {
344            arc: Arc2d::from_degrees(radius, angle),
345        }
346    }
347
348    /// Create a new [`CircularSector`] from a `radius` and a number of `turns` of a circle.
349    ///
350    /// For instance, `0.5` turns is a semicircle.
351    #[inline]
352    pub const fn from_turns(radius: f32, fraction: f32) -> Self {
353        Self {
354            arc: Arc2d::from_turns(radius, fraction),
355        }
356    }
357
358    /// Get half the angle of the sector
359    #[inline]
360    pub const fn half_angle(&self) -> f32 {
361        self.arc.half_angle
362    }
363
364    /// Get the angle of the sector
365    #[inline]
366    pub const fn angle(&self) -> f32 {
367        self.arc.angle()
368    }
369
370    /// Get the radius of the sector
371    #[inline]
372    pub const fn radius(&self) -> f32 {
373        self.arc.radius
374    }
375
376    /// Get the length of the arc defining the sector
377    #[inline]
378    pub const fn arc_length(&self) -> f32 {
379        self.arc.length()
380    }
381
382    /// Get half the length of the chord defined by the sector
383    ///
384    /// See [`Arc2d::half_chord_length`]
385    #[inline]
386    pub fn half_chord_length(&self) -> f32 {
387        self.arc.half_chord_length()
388    }
389
390    /// Get the length of the chord defined by the sector
391    ///
392    /// See [`Arc2d::chord_length`]
393    #[inline]
394    pub fn chord_length(&self) -> f32 {
395        self.arc.chord_length()
396    }
397
398    /// Get the midpoint of the chord defined by the sector
399    ///
400    /// See [`Arc2d::chord_midpoint`]
401    #[inline]
402    pub fn chord_midpoint(&self) -> Vec2 {
403        self.arc.chord_midpoint()
404    }
405
406    /// Get the length of the apothem of this sector
407    ///
408    /// See [`Arc2d::apothem`]
409    #[inline]
410    pub fn apothem(&self) -> f32 {
411        self.arc.apothem()
412    }
413
414    /// Get the length of the sagitta of this sector
415    ///
416    /// See [`Arc2d::sagitta`]
417    #[inline]
418    pub fn sagitta(&self) -> f32 {
419        self.arc.sagitta()
420    }
421}
422
423/// A primitive representing a circular segment:
424/// the area enclosed by the arc of a circle and its chord (the line between its endpoints).
425///
426/// The segment is drawn starting from [`Vec2::Y`], extending equally on either side.
427/// To orient the segment differently, apply a rotation.
428/// The segment is drawn with the center of its circle at the origin [`Vec2::ZERO`].
429/// When positioning a segment, the [`apothem`](Self::apothem) function may be particularly useful.
430///
431/// **Warning:** Circular segments with negative angle or radius, or with angle greater than an entire circle, are not officially supported.
432/// We recommend normalizing circular segments to have an angle in [0, 2π].
433#[derive(Clone, Copy, Debug, PartialEq, From)]
434#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
435#[cfg_attr(
436    feature = "bevy_reflect",
437    derive(Reflect),
438    reflect(Debug, PartialEq, Default, Clone)
439)]
440#[cfg_attr(
441    all(feature = "serialize", feature = "bevy_reflect"),
442    reflect(Serialize, Deserialize)
443)]
444pub struct CircularSegment {
445    /// The arc defining the segment
446    #[cfg_attr(all(feature = "serialize", feature = "alloc"), serde(flatten))]
447    pub arc: Arc2d,
448}
449
450impl Primitive2d for CircularSegment {}
451
452impl Default for CircularSegment {
453    /// Returns the default [`CircularSegment`] with radius `0.5` and covering a third of a circle
454    fn default() -> Self {
455        Self::from(Arc2d::default())
456    }
457}
458
459impl Measured2d for CircularSegment {
460    #[inline]
461    fn area(&self) -> f32 {
462        0.5 * self.arc.radius.squared() * (self.arc.angle() - ops::sin(self.arc.angle()))
463    }
464
465    #[inline]
466    fn perimeter(&self) -> f32 {
467        self.chord_length() + self.arc_length()
468    }
469}
470
471impl CircularSegment {
472    /// Create a new [`CircularSegment`] from a `radius`, and a `half_angle` in radians.
473    #[inline]
474    pub const fn new(radius: f32, half_angle: f32) -> Self {
475        Self {
476            arc: Arc2d::new(radius, half_angle),
477        }
478    }
479
480    /// Create a new [`CircularSegment`] from a `radius` and an `angle` in radians.
481    #[inline]
482    pub const fn from_radians(radius: f32, angle: f32) -> Self {
483        Self {
484            arc: Arc2d::from_radians(radius, angle),
485        }
486    }
487
488    /// Create a new [`CircularSegment`] from a `radius` and an `angle` in degrees.
489    #[inline]
490    pub const fn from_degrees(radius: f32, angle: f32) -> Self {
491        Self {
492            arc: Arc2d::from_degrees(radius, angle),
493        }
494    }
495
496    /// Create a new [`CircularSegment`] from a `radius` and a number of `turns` of a circle.
497    ///
498    /// For instance, `0.5` turns is a semicircle.
499    #[inline]
500    pub const fn from_turns(radius: f32, fraction: f32) -> Self {
501        Self {
502            arc: Arc2d::from_turns(radius, fraction),
503        }
504    }
505
506    /// Get the half-angle of the segment
507    #[inline]
508    pub const fn half_angle(&self) -> f32 {
509        self.arc.half_angle
510    }
511
512    /// Get the angle of the segment
513    #[inline]
514    pub const fn angle(&self) -> f32 {
515        self.arc.angle()
516    }
517
518    /// Get the radius of the segment
519    #[inline]
520    pub const fn radius(&self) -> f32 {
521        self.arc.radius
522    }
523
524    /// Get the length of the arc defining the segment
525    #[inline]
526    pub const fn arc_length(&self) -> f32 {
527        self.arc.length()
528    }
529
530    /// Get half the length of the segment's base, also known as its chord
531    #[inline]
532    #[doc(alias = "half_base_length")]
533    pub fn half_chord_length(&self) -> f32 {
534        self.arc.half_chord_length()
535    }
536
537    /// Get the length of the segment's base, also known as its chord
538    #[inline]
539    #[doc(alias = "base_length")]
540    #[doc(alias = "base")]
541    pub fn chord_length(&self) -> f32 {
542        self.arc.chord_length()
543    }
544
545    /// Get the midpoint of the segment's base, also known as its chord
546    #[inline]
547    #[doc(alias = "base_midpoint")]
548    pub fn chord_midpoint(&self) -> Vec2 {
549        self.arc.chord_midpoint()
550    }
551
552    /// Get the length of the apothem of this segment,
553    /// which is the signed distance between the segment and the center of its circle
554    ///
555    /// See [`Arc2d::apothem`]
556    #[inline]
557    pub fn apothem(&self) -> f32 {
558        self.arc.apothem()
559    }
560
561    /// Get the length of the sagitta of this segment, also known as its height
562    ///
563    /// See [`Arc2d::sagitta`]
564    #[inline]
565    #[doc(alias = "height")]
566    pub fn sagitta(&self) -> f32 {
567        self.arc.sagitta()
568    }
569}
570
571#[cfg(test)]
572mod arc_tests {
573    use core::f32::consts::FRAC_PI_4;
574    use core::f32::consts::SQRT_2;
575
576    use approx::assert_abs_diff_eq;
577
578    use super::*;
579
580    struct ArcTestCase {
581        radius: f32,
582        half_angle: f32,
583        angle: f32,
584        length: f32,
585        right_endpoint: Vec2,
586        left_endpoint: Vec2,
587        endpoints: [Vec2; 2],
588        midpoint: Vec2,
589        half_chord_length: f32,
590        chord_length: f32,
591        chord_midpoint: Vec2,
592        apothem: f32,
593        sagitta: f32,
594        is_minor: bool,
595        is_major: bool,
596        sector_area: f32,
597        sector_perimeter: f32,
598        segment_area: f32,
599        segment_perimeter: f32,
600    }
601
602    impl ArcTestCase {
603        fn check_arc(&self, arc: Arc2d) {
604            assert_abs_diff_eq!(self.radius, arc.radius);
605            assert_abs_diff_eq!(self.half_angle, arc.half_angle);
606            assert_abs_diff_eq!(self.angle, arc.angle());
607            assert_abs_diff_eq!(self.length, arc.length());
608            assert_abs_diff_eq!(self.right_endpoint, arc.right_endpoint());
609            assert_abs_diff_eq!(self.left_endpoint, arc.left_endpoint());
610            assert_abs_diff_eq!(self.endpoints[0], arc.endpoints()[0]);
611            assert_abs_diff_eq!(self.endpoints[1], arc.endpoints()[1]);
612            assert_abs_diff_eq!(self.midpoint, arc.midpoint());
613            assert_abs_diff_eq!(self.half_chord_length, arc.half_chord_length());
614            assert_abs_diff_eq!(self.chord_length, arc.chord_length(), epsilon = 0.00001);
615            assert_abs_diff_eq!(self.chord_midpoint, arc.chord_midpoint());
616            assert_abs_diff_eq!(self.apothem, arc.apothem());
617            assert_abs_diff_eq!(self.sagitta, arc.sagitta());
618            assert_eq!(self.is_minor, arc.is_minor());
619            assert_eq!(self.is_major, arc.is_major());
620        }
621
622        fn check_sector(&self, sector: CircularSector) {
623            assert_abs_diff_eq!(self.radius, sector.radius());
624            assert_abs_diff_eq!(self.half_angle, sector.half_angle());
625            assert_abs_diff_eq!(self.angle, sector.angle());
626            assert_abs_diff_eq!(self.half_chord_length, sector.half_chord_length());
627            assert_abs_diff_eq!(self.chord_length, sector.chord_length(), epsilon = 0.00001);
628            assert_abs_diff_eq!(self.chord_midpoint, sector.chord_midpoint());
629            assert_abs_diff_eq!(self.apothem, sector.apothem());
630            assert_abs_diff_eq!(self.sagitta, sector.sagitta());
631            assert_abs_diff_eq!(self.sector_area, sector.area());
632            assert_abs_diff_eq!(self.sector_perimeter, sector.perimeter());
633        }
634
635        fn check_segment(&self, segment: CircularSegment) {
636            assert_abs_diff_eq!(self.radius, segment.radius());
637            assert_abs_diff_eq!(self.half_angle, segment.half_angle());
638            assert_abs_diff_eq!(self.angle, segment.angle());
639            assert_abs_diff_eq!(self.half_chord_length, segment.half_chord_length());
640            assert_abs_diff_eq!(self.chord_length, segment.chord_length(), epsilon = 0.00001);
641            assert_abs_diff_eq!(self.chord_midpoint, segment.chord_midpoint());
642            assert_abs_diff_eq!(self.apothem, segment.apothem());
643            assert_abs_diff_eq!(self.sagitta, segment.sagitta());
644            assert_abs_diff_eq!(self.segment_area, segment.area());
645            assert_abs_diff_eq!(self.segment_perimeter, segment.perimeter());
646        }
647    }
648
649    #[test]
650    fn zero_angle() {
651        let tests = ArcTestCase {
652            radius: 1.0,
653            half_angle: 0.0,
654            angle: 0.0,
655            length: 0.0,
656            left_endpoint: Vec2::Y,
657            right_endpoint: Vec2::Y,
658            endpoints: [Vec2::Y, Vec2::Y],
659            midpoint: Vec2::Y,
660            half_chord_length: 0.0,
661            chord_length: 0.0,
662            chord_midpoint: Vec2::Y,
663            apothem: 1.0,
664            sagitta: 0.0,
665            is_minor: true,
666            is_major: false,
667            sector_area: 0.0,
668            sector_perimeter: 2.0,
669            segment_area: 0.0,
670            segment_perimeter: 0.0,
671        };
672
673        tests.check_arc(Arc2d::new(1.0, 0.0));
674        tests.check_sector(CircularSector::new(1.0, 0.0));
675        tests.check_segment(CircularSegment::new(1.0, 0.0));
676    }
677
678    #[test]
679    fn zero_radius() {
680        let tests = ArcTestCase {
681            radius: 0.0,
682            half_angle: FRAC_PI_4,
683            angle: FRAC_PI_2,
684            length: 0.0,
685            left_endpoint: Vec2::ZERO,
686            right_endpoint: Vec2::ZERO,
687            endpoints: [Vec2::ZERO, Vec2::ZERO],
688            midpoint: Vec2::ZERO,
689            half_chord_length: 0.0,
690            chord_length: 0.0,
691            chord_midpoint: Vec2::ZERO,
692            apothem: 0.0,
693            sagitta: 0.0,
694            is_minor: true,
695            is_major: false,
696            sector_area: 0.0,
697            sector_perimeter: 0.0,
698            segment_area: 0.0,
699            segment_perimeter: 0.0,
700        };
701
702        tests.check_arc(Arc2d::new(0.0, FRAC_PI_4));
703        tests.check_sector(CircularSector::new(0.0, FRAC_PI_4));
704        tests.check_segment(CircularSegment::new(0.0, FRAC_PI_4));
705    }
706
707    #[test]
708    fn quarter_circle() {
709        let sqrt_half: f32 = ops::sqrt(0.5);
710        let tests = ArcTestCase {
711            radius: 1.0,
712            half_angle: FRAC_PI_4,
713            angle: FRAC_PI_2,
714            length: FRAC_PI_2,
715            left_endpoint: Vec2::new(-sqrt_half, sqrt_half),
716            right_endpoint: Vec2::splat(sqrt_half),
717            endpoints: [Vec2::new(-sqrt_half, sqrt_half), Vec2::splat(sqrt_half)],
718            midpoint: Vec2::Y,
719            half_chord_length: sqrt_half,
720            chord_length: ops::sqrt(2.0),
721            chord_midpoint: Vec2::new(0.0, sqrt_half),
722            apothem: sqrt_half,
723            sagitta: 1.0 - sqrt_half,
724            is_minor: true,
725            is_major: false,
726            sector_area: FRAC_PI_4,
727            sector_perimeter: FRAC_PI_2 + 2.0,
728            segment_area: FRAC_PI_4 - 0.5,
729            segment_perimeter: FRAC_PI_2 + SQRT_2,
730        };
731
732        tests.check_arc(Arc2d::from_turns(1.0, 0.25));
733        tests.check_sector(CircularSector::from_turns(1.0, 0.25));
734        tests.check_segment(CircularSegment::from_turns(1.0, 0.25));
735    }
736
737    #[test]
738    fn half_circle() {
739        let tests = ArcTestCase {
740            radius: 1.0,
741            half_angle: FRAC_PI_2,
742            angle: PI,
743            length: PI,
744            left_endpoint: Vec2::NEG_X,
745            right_endpoint: Vec2::X,
746            endpoints: [Vec2::NEG_X, Vec2::X],
747            midpoint: Vec2::Y,
748            half_chord_length: 1.0,
749            chord_length: 2.0,
750            chord_midpoint: Vec2::ZERO,
751            apothem: 0.0,
752            sagitta: 1.0,
753            is_minor: true,
754            is_major: true,
755            sector_area: FRAC_PI_2,
756            sector_perimeter: PI + 2.0,
757            segment_area: FRAC_PI_2,
758            segment_perimeter: PI + 2.0,
759        };
760
761        tests.check_arc(Arc2d::from_radians(1.0, PI));
762        tests.check_sector(CircularSector::from_radians(1.0, PI));
763        tests.check_segment(CircularSegment::from_radians(1.0, PI));
764    }
765
766    #[test]
767    fn full_circle() {
768        let tests = ArcTestCase {
769            radius: 1.0,
770            half_angle: PI,
771            angle: 2.0 * PI,
772            length: 2.0 * PI,
773            left_endpoint: Vec2::NEG_Y,
774            right_endpoint: Vec2::NEG_Y,
775            endpoints: [Vec2::NEG_Y, Vec2::NEG_Y],
776            midpoint: Vec2::Y,
777            half_chord_length: 0.0,
778            chord_length: 0.0,
779            chord_midpoint: Vec2::NEG_Y,
780            apothem: -1.0,
781            sagitta: 2.0,
782            is_minor: false,
783            is_major: true,
784            sector_area: PI,
785            sector_perimeter: 2.0 * PI,
786            segment_area: PI,
787            segment_perimeter: 2.0 * PI,
788        };
789
790        tests.check_arc(Arc2d::from_degrees(1.0, 360.0));
791        tests.check_sector(CircularSector::from_degrees(1.0, 360.0));
792        tests.check_segment(CircularSegment::from_degrees(1.0, 360.0));
793    }
794}
795
796/// An ellipse primitive, which is like a circle, but the width and height can be different
797///
798/// Ellipse does not implement [`Inset`] as concentric ellipses do not have parallel curves:
799/// if the ellipse is not a circle, the inset shape is not actually an ellipse (although it may look like one) but can also be a lens-like shape.
800#[derive(Clone, Copy, Debug, PartialEq)]
801#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
802#[cfg_attr(
803    feature = "bevy_reflect",
804    derive(Reflect),
805    reflect(Debug, PartialEq, Default, Clone)
806)]
807#[cfg_attr(
808    all(feature = "serialize", feature = "bevy_reflect"),
809    reflect(Serialize, Deserialize)
810)]
811pub struct Ellipse {
812    /// Half of the width and height of the ellipse.
813    ///
814    /// This corresponds to the two perpendicular radii defining the ellipse.
815    pub half_size: Vec2,
816}
817
818impl Primitive2d for Ellipse {}
819
820impl Default for Ellipse {
821    /// Returns the default [`Ellipse`] with a half-width of `1.0` and a half-height of `0.5`.
822    fn default() -> Self {
823        Self {
824            half_size: Vec2::new(1.0, 0.5),
825        }
826    }
827}
828
829impl Ellipse {
830    /// Create a new `Ellipse` from half of its width and height.
831    ///
832    /// This corresponds to the two perpendicular radii defining the ellipse.
833    #[inline]
834    pub const fn new(half_width: f32, half_height: f32) -> Self {
835        Self {
836            half_size: Vec2::new(half_width, half_height),
837        }
838    }
839
840    /// Create a new `Ellipse` from a given full size.
841    ///
842    /// `size.x` is the diameter along the X axis, and `size.y` is the diameter along the Y axis.
843    #[inline]
844    pub const fn from_size(size: Vec2) -> Self {
845        Self {
846            half_size: Vec2::new(size.x / 2.0, size.y / 2.0),
847        }
848    }
849
850    #[inline]
851    /// Returns the [eccentricity](https://en.wikipedia.org/wiki/Eccentricity_(mathematics)) of the ellipse.
852    /// It can be thought of as a measure of how "stretched" or elongated the ellipse is.
853    ///
854    /// The value should be in the range [0, 1), where 0 represents a circle, and 1 represents a parabola.
855    pub fn eccentricity(&self) -> f32 {
856        let a = self.semi_major();
857        let b = self.semi_minor();
858
859        ops::sqrt(a * a - b * b) / a
860    }
861
862    #[inline]
863    /// Get the focal length of the ellipse. This corresponds to the distance between one of the foci and the center of the ellipse.
864    ///
865    /// The focal length of an ellipse is related to its eccentricity by `eccentricity = focal_length / semi_major`
866    pub fn focal_length(&self) -> f32 {
867        let a = self.semi_major();
868        let b = self.semi_minor();
869
870        ops::sqrt(a * a - b * b)
871    }
872
873    /// Returns the length of the semi-major axis. This corresponds to the longest radius of the ellipse.
874    #[inline]
875    pub fn semi_major(&self) -> f32 {
876        self.half_size.max_element()
877    }
878
879    /// Returns the length of the semi-minor axis. This corresponds to the shortest radius of the ellipse.
880    #[inline]
881    pub fn semi_minor(&self) -> f32 {
882        self.half_size.min_element()
883    }
884}
885
886impl Measured2d for Ellipse {
887    /// Get the area of the ellipse
888    #[inline]
889    fn area(&self) -> f32 {
890        PI * self.half_size.x * self.half_size.y
891    }
892
893    #[inline]
894    /// Get an approximation for the perimeter or circumference of the ellipse.
895    ///
896    /// The approximation is reasonably precise with a relative error less than 0.007%, getting more precise as the eccentricity of the ellipse decreases.
897    fn perimeter(&self) -> f32 {
898        let a = self.semi_major();
899        let b = self.semi_minor();
900
901        // In the case that `a == b`, the ellipse is a circle
902        if a / b - 1. < 1e-5 {
903            return PI * (a + b);
904        };
905
906        // In the case that `a` is much larger than `b`, the ellipse is a line
907        if a / b > 1e4 {
908            return 4. * a;
909        };
910
911        // These values are  the result of (0.5 choose n)^2 where n is the index in the array
912        // They could be calculated on the fly but hardcoding them yields more accurate and faster results
913        // because the actual calculation for these values involves factorials and numbers > 10^23
914        const BINOMIAL_COEFFICIENTS: [f32; 21] = [
915            1.,
916            0.25,
917            0.015625,
918            0.00390625,
919            0.0015258789,
920            0.00074768066,
921            0.00042057037,
922            0.00025963783,
923            0.00017140154,
924            0.000119028846,
925            0.00008599834,
926            0.00006414339,
927            0.000049109784,
928            0.000038430585,
929            0.000030636627,
930            0.000024815668,
931            0.000020380836,
932            0.000016942893,
933            0.000014236736,
934            0.000012077564,
935            0.000010333865,
936        ];
937
938        // The algorithm used here is the Gauss-Kummer infinite series expansion of the elliptic integral expression for the perimeter of ellipses
939        // For more information see https://www.wolframalpha.com/input/?i=gauss-kummer+series
940        // We only use the terms up to `i == 20` for this approximation
941        let h = ((a - b) / (a + b)).squared();
942
943        PI * (a + b)
944            * (0..=20)
945                .map(|i| BINOMIAL_COEFFICIENTS[i] * ops::powf(h, i as f32))
946                .sum::<f32>()
947    }
948}
949
950/// A primitive shape formed by the region between two circles, also known as a ring.
951#[derive(Clone, Copy, Debug, PartialEq)]
952#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
953#[cfg_attr(
954    feature = "bevy_reflect",
955    derive(Reflect),
956    reflect(Debug, PartialEq, Default, Clone)
957)]
958#[cfg_attr(
959    all(feature = "serialize", feature = "bevy_reflect"),
960    reflect(Serialize, Deserialize)
961)]
962#[doc(alias = "Ring")]
963pub struct Annulus {
964    /// The inner circle of the annulus
965    pub inner_circle: Circle,
966    /// The outer circle of the annulus
967    pub outer_circle: Circle,
968}
969
970impl Primitive2d for Annulus {}
971
972impl Default for Annulus {
973    /// Returns the default [`Annulus`] with radii of `0.5` and `1.0`.
974    fn default() -> Self {
975        Self {
976            inner_circle: Circle::new(0.5),
977            outer_circle: Circle::new(1.0),
978        }
979    }
980}
981
982impl Annulus {
983    /// Create a new [`Annulus`] from the radii of the inner and outer circle
984    #[inline]
985    pub const fn new(inner_radius: f32, outer_radius: f32) -> Self {
986        Self {
987            inner_circle: Circle::new(inner_radius),
988            outer_circle: Circle::new(outer_radius),
989        }
990    }
991
992    /// Get the diameter of the annulus
993    #[inline]
994    pub const fn diameter(&self) -> f32 {
995        self.outer_circle.diameter()
996    }
997
998    /// Get the thickness of the annulus
999    #[inline]
1000    pub const fn thickness(&self) -> f32 {
1001        self.outer_circle.radius - self.inner_circle.radius
1002    }
1003
1004    /// Finds the point on the annulus that is closest to the given `point`:
1005    ///
1006    /// - If the point is outside of the annulus completely, the returned point will be on the outer perimeter.
1007    /// - If the point is inside of the inner circle (hole) of the annulus, the returned point will be on the inner perimeter.
1008    /// - Otherwise, the returned point is overlapping the annulus and returned as is.
1009    #[inline]
1010    pub fn closest_point(&self, point: Vec2) -> Vec2 {
1011        let distance_squared = point.length_squared();
1012
1013        if self.inner_circle.radius.squared() <= distance_squared {
1014            if distance_squared <= self.outer_circle.radius.squared() {
1015                // The point is inside the annulus.
1016                point
1017            } else {
1018                // The point is outside the annulus and closer to the outer perimeter.
1019                // Find the closest point on the perimeter of the annulus.
1020                let dir_to_point = point / ops::sqrt(distance_squared);
1021                self.outer_circle.radius * dir_to_point
1022            }
1023        } else {
1024            // The point is outside the annulus and closer to the inner perimeter.
1025            // Find the closest point on the perimeter of the annulus.
1026            let dir_to_point = point / ops::sqrt(distance_squared);
1027            self.inner_circle.radius * dir_to_point
1028        }
1029    }
1030}
1031
1032impl Measured2d for Annulus {
1033    /// Get the area of the annulus
1034    #[inline]
1035    fn area(&self) -> f32 {
1036        PI * (self.outer_circle.radius.squared() - self.inner_circle.radius.squared())
1037    }
1038
1039    /// Get the perimeter or circumference of the annulus,
1040    /// which is the sum of the perimeters of the inner and outer circles.
1041    #[inline]
1042    #[doc(alias = "circumference")]
1043    fn perimeter(&self) -> f32 {
1044        2.0 * PI * (self.outer_circle.radius + self.inner_circle.radius)
1045    }
1046}
1047
1048/// A rhombus primitive, also known as a diamond shape.
1049/// A four sided polygon, centered on the origin, where opposite sides are parallel but without
1050/// requiring right angles.
1051#[derive(Clone, Copy, Debug, PartialEq)]
1052#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1053#[cfg_attr(
1054    feature = "bevy_reflect",
1055    derive(Reflect),
1056    reflect(Debug, PartialEq, Default, Clone)
1057)]
1058#[cfg_attr(
1059    all(feature = "serialize", feature = "bevy_reflect"),
1060    reflect(Serialize, Deserialize)
1061)]
1062#[doc(alias = "Diamond")]
1063pub struct Rhombus {
1064    /// Size of the horizontal and vertical diagonals of the rhombus
1065    pub half_diagonals: Vec2,
1066}
1067
1068impl Primitive2d for Rhombus {}
1069
1070impl Default for Rhombus {
1071    /// Returns the default [`Rhombus`] with a half-horizontal and half-vertical diagonal of `0.5`.
1072    fn default() -> Self {
1073        Self {
1074            half_diagonals: Vec2::splat(0.5),
1075        }
1076    }
1077}
1078
1079impl Rhombus {
1080    /// Create a new `Rhombus` from a vertical and horizontal diagonal sizes.
1081    #[inline]
1082    pub const fn new(horizontal_diagonal: f32, vertical_diagonal: f32) -> Self {
1083        Self {
1084            half_diagonals: Vec2::new(horizontal_diagonal / 2.0, vertical_diagonal / 2.0),
1085        }
1086    }
1087
1088    /// Create a new `Rhombus` from a side length with all inner angles equal.
1089    #[inline]
1090    pub const fn from_side(side: f32) -> Self {
1091        Self {
1092            half_diagonals: Vec2::splat(side * FRAC_1_SQRT_2),
1093        }
1094    }
1095
1096    /// Create a new `Rhombus` from a given inradius with all inner angles equal.
1097    #[inline]
1098    pub const fn from_inradius(inradius: f32) -> Self {
1099        let half_diagonal = inradius * 2.0 / core::f32::consts::SQRT_2;
1100        Self {
1101            half_diagonals: Vec2::new(half_diagonal, half_diagonal),
1102        }
1103    }
1104
1105    /// Get the length of each side of the rhombus
1106    #[inline]
1107    pub fn side(&self) -> f32 {
1108        self.half_diagonals.length()
1109    }
1110
1111    /// Get the radius of the circumcircle on which all vertices
1112    /// of the rhombus lie
1113    #[inline]
1114    pub const fn circumradius(&self) -> f32 {
1115        self.half_diagonals.x.max(self.half_diagonals.y)
1116    }
1117
1118    /// Get the radius of the largest circle that can
1119    /// be drawn within the rhombus
1120    #[inline]
1121    #[doc(alias = "apothem")]
1122    pub fn inradius(&self) -> f32 {
1123        let side = self.side();
1124        if side == 0.0 {
1125            0.0
1126        } else {
1127            (self.half_diagonals.x * self.half_diagonals.y) / side
1128        }
1129    }
1130
1131    /// Finds the point on the rhombus that is closest to the given `point`.
1132    ///
1133    /// If the point is outside the rhombus, the returned point will be on the perimeter of the rhombus.
1134    /// Otherwise, it will be inside the rhombus and returned as is.
1135    #[inline]
1136    pub fn closest_point(&self, point: Vec2) -> Vec2 {
1137        // Fold the problem into the positive quadrant
1138        let point_abs = point.abs();
1139        let half_diagonals = self.half_diagonals.abs(); // to ensure correct sign
1140
1141        // The unnormalised normal vector perpendicular to the side of the rhombus
1142        let normal = Vec2::new(half_diagonals.y, half_diagonals.x);
1143        let normal_magnitude_squared = normal.length_squared();
1144        if normal_magnitude_squared == 0.0 {
1145            return Vec2::ZERO; // A null Rhombus has only one point anyway.
1146        }
1147
1148        // The last term corresponds to normal.dot(rhombus_vertex)
1149        let distance_unnormalised = normal.dot(point_abs) - half_diagonals.x * half_diagonals.y;
1150
1151        // The point is already inside so we simply return it.
1152        if distance_unnormalised <= 0.0 {
1153            return point;
1154        }
1155
1156        // Clamp the point to the edge
1157        let mut result = point_abs - normal * distance_unnormalised / normal_magnitude_squared;
1158
1159        // Clamp the point back to the positive quadrant
1160        // if it's outside, it needs to be clamped to either vertex
1161        if result.x <= 0.0 {
1162            result = Vec2::new(0.0, half_diagonals.y);
1163        } else if result.y <= 0.0 {
1164            result = Vec2::new(half_diagonals.x, 0.0);
1165        }
1166
1167        // Finally, we restore the signs of the original vector
1168        result.copysign(point)
1169    }
1170}
1171
1172impl Measured2d for Rhombus {
1173    /// Get the area of the rhombus
1174    #[inline]
1175    fn area(&self) -> f32 {
1176        2.0 * self.half_diagonals.x * self.half_diagonals.y
1177    }
1178
1179    /// Get the perimeter of the rhombus
1180    #[inline]
1181    fn perimeter(&self) -> f32 {
1182        4.0 * self.side()
1183    }
1184}
1185
1186/// An unbounded plane in 2D space. It forms a separating surface through the origin,
1187/// stretching infinitely far
1188#[derive(Clone, Copy, Debug, PartialEq)]
1189#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1190#[cfg_attr(
1191    feature = "bevy_reflect",
1192    derive(Reflect),
1193    reflect(Debug, PartialEq, Default, Clone)
1194)]
1195#[cfg_attr(
1196    all(feature = "serialize", feature = "bevy_reflect"),
1197    reflect(Serialize, Deserialize)
1198)]
1199pub struct Plane2d {
1200    /// The normal of the plane. The plane will be placed perpendicular to this direction
1201    pub normal: Dir2,
1202}
1203
1204impl Primitive2d for Plane2d {}
1205
1206impl Default for Plane2d {
1207    /// Returns the default [`Plane2d`] with a normal pointing in the `+Y` direction.
1208    fn default() -> Self {
1209        Self { normal: Dir2::Y }
1210    }
1211}
1212
1213impl Plane2d {
1214    /// Create a new `Plane2d` from a normal
1215    ///
1216    /// # Panics
1217    ///
1218    /// Panics if the given `normal` is zero (or very close to zero), or non-finite.
1219    #[inline]
1220    pub fn new(normal: Vec2) -> Self {
1221        Self {
1222            normal: Dir2::new(normal).expect("normal must be nonzero and finite"),
1223        }
1224    }
1225}
1226
1227/// An infinite line going through the origin along a direction in 2D space.
1228///
1229/// For a finite line: [`Segment2d`]
1230#[derive(Clone, Copy, Debug, PartialEq)]
1231#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1232#[cfg_attr(
1233    feature = "bevy_reflect",
1234    derive(Reflect),
1235    reflect(Debug, PartialEq, Clone)
1236)]
1237#[cfg_attr(
1238    all(feature = "serialize", feature = "bevy_reflect"),
1239    reflect(Serialize, Deserialize)
1240)]
1241pub struct Line2d {
1242    /// The direction of the line. The line extends infinitely in both the given direction
1243    /// and its opposite direction
1244    pub direction: Dir2,
1245}
1246
1247impl Primitive2d for Line2d {}
1248
1249/// A line segment defined by two endpoints in 2D space.
1250#[derive(Clone, Copy, Debug, PartialEq)]
1251#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1252#[cfg_attr(
1253    feature = "bevy_reflect",
1254    derive(Reflect),
1255    reflect(Debug, PartialEq, Clone)
1256)]
1257#[cfg_attr(
1258    all(feature = "serialize", feature = "bevy_reflect"),
1259    reflect(Serialize, Deserialize)
1260)]
1261#[doc(alias = "LineSegment2d")]
1262pub struct Segment2d {
1263    /// The endpoints of the line segment.
1264    pub vertices: [Vec2; 2],
1265}
1266
1267impl Primitive2d for Segment2d {}
1268
1269impl Default for Segment2d {
1270    fn default() -> Self {
1271        Self {
1272            vertices: [Vec2::new(-0.5, 0.0), Vec2::new(0.5, 0.0)],
1273        }
1274    }
1275}
1276
1277impl Segment2d {
1278    /// Create a new `Segment2d` from its endpoints.
1279    #[inline]
1280    pub const fn new(point1: Vec2, point2: Vec2) -> Self {
1281        Self {
1282            vertices: [point1, point2],
1283        }
1284    }
1285
1286    /// Create a new `Segment2d` centered at the origin with the given direction and length.
1287    ///
1288    /// The endpoints will be at `-direction * length / 2.0` and `direction * length / 2.0`.
1289    #[inline]
1290    pub fn from_direction_and_length(direction: Dir2, length: f32) -> Self {
1291        let endpoint = 0.5 * length * direction;
1292        Self {
1293            vertices: [-endpoint, endpoint],
1294        }
1295    }
1296
1297    /// Create a new `Segment2d` centered at the origin from a vector representing
1298    /// the direction and length of the line segment.
1299    ///
1300    /// The endpoints will be at `-scaled_direction / 2.0` and `scaled_direction / 2.0`.
1301    #[inline]
1302    pub fn from_scaled_direction(scaled_direction: Vec2) -> Self {
1303        let endpoint = 0.5 * scaled_direction;
1304        Self {
1305            vertices: [-endpoint, endpoint],
1306        }
1307    }
1308
1309    /// Create a new `Segment2d` starting from the origin of the given `ray`,
1310    /// going in the direction of the ray for the given `length`.
1311    ///
1312    /// The endpoints will be at `ray.origin` and `ray.origin + length * ray.direction`.
1313    #[inline]
1314    pub fn from_ray_and_length(ray: Ray2d, length: f32) -> Self {
1315        Self {
1316            vertices: [ray.origin, ray.get_point(length)],
1317        }
1318    }
1319
1320    /// Get the position of the first endpoint of the line segment.
1321    #[inline]
1322    pub const fn point1(&self) -> Vec2 {
1323        self.vertices[0]
1324    }
1325
1326    /// Get the position of the second endpoint of the line segment.
1327    #[inline]
1328    pub const fn point2(&self) -> Vec2 {
1329        self.vertices[1]
1330    }
1331
1332    /// Compute the midpoint between the two endpoints of the line segment.
1333    #[inline]
1334    #[doc(alias = "midpoint")]
1335    pub fn center(&self) -> Vec2 {
1336        self.point1().midpoint(self.point2())
1337    }
1338
1339    /// Compute the length of the line segment.
1340    #[inline]
1341    pub fn length(&self) -> f32 {
1342        self.point1().distance(self.point2())
1343    }
1344
1345    /// Compute the squared length of the line segment.
1346    #[inline]
1347    pub fn length_squared(&self) -> f32 {
1348        self.point1().distance_squared(self.point2())
1349    }
1350
1351    /// Compute the normalized direction pointing from the first endpoint to the second endpoint.
1352    ///
1353    /// For the non-panicking version, see [`Segment2d::try_direction`].
1354    ///
1355    /// # Panics
1356    ///
1357    /// Panics if a valid direction could not be computed, for example when the endpoints are coincident, NaN, or infinite.
1358    #[inline]
1359    pub fn direction(&self) -> Dir2 {
1360        self.try_direction().unwrap_or_else(|err| {
1361            panic!("Failed to compute the direction of a line segment: {err}")
1362        })
1363    }
1364
1365    /// Try to compute the normalized direction pointing from the first endpoint to the second endpoint.
1366    ///
1367    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if a valid direction could not be computed,
1368    /// for example when the endpoints are coincident, NaN, or infinite.
1369    #[inline]
1370    pub fn try_direction(&self) -> Result<Dir2, InvalidDirectionError> {
1371        Dir2::new(self.scaled_direction())
1372    }
1373
1374    /// Compute the vector from the first endpoint to the second endpoint.
1375    #[inline]
1376    pub fn scaled_direction(&self) -> Vec2 {
1377        self.point2() - self.point1()
1378    }
1379
1380    /// Compute the normalized counterclockwise normal on the left-hand side of the line segment.
1381    ///
1382    /// For the non-panicking version, see [`Segment2d::try_left_normal`].
1383    ///
1384    /// # Panics
1385    ///
1386    /// Panics if a valid normal could not be computed, for example when the endpoints are coincident, NaN, or infinite.
1387    #[inline]
1388    pub fn left_normal(&self) -> Dir2 {
1389        self.try_left_normal().unwrap_or_else(|err| {
1390            panic!("Failed to compute the left-hand side normal of a line segment: {err}")
1391        })
1392    }
1393
1394    /// Try to compute the normalized counterclockwise normal on the left-hand side of the line segment.
1395    ///
1396    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if a valid normal could not be computed,
1397    /// for example when the endpoints are coincident, NaN, or infinite.
1398    #[inline]
1399    pub fn try_left_normal(&self) -> Result<Dir2, InvalidDirectionError> {
1400        Dir2::new(self.scaled_left_normal())
1401    }
1402
1403    /// Compute the non-normalized counterclockwise normal on the left-hand side of the line segment.
1404    ///
1405    /// The length of the normal is the distance between the endpoints.
1406    #[inline]
1407    pub fn scaled_left_normal(&self) -> Vec2 {
1408        let scaled_direction = self.scaled_direction();
1409        Vec2::new(-scaled_direction.y, scaled_direction.x)
1410    }
1411
1412    /// Compute the normalized clockwise normal on the right-hand side of the line segment.
1413    ///
1414    /// For the non-panicking version, see [`Segment2d::try_right_normal`].
1415    ///
1416    /// # Panics
1417    ///
1418    /// Panics if a valid normal could not be computed, for example when the endpoints are coincident, NaN, or infinite.
1419    #[inline]
1420    pub fn right_normal(&self) -> Dir2 {
1421        self.try_right_normal().unwrap_or_else(|err| {
1422            panic!("Failed to compute the right-hand side normal of a line segment: {err}")
1423        })
1424    }
1425
1426    /// Try to compute the normalized clockwise normal on the right-hand side of the line segment.
1427    ///
1428    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if a valid normal could not be computed,
1429    /// for example when the endpoints are coincident, NaN, or infinite.
1430    #[inline]
1431    pub fn try_right_normal(&self) -> Result<Dir2, InvalidDirectionError> {
1432        Dir2::new(self.scaled_right_normal())
1433    }
1434
1435    /// Compute the non-normalized clockwise normal on the right-hand side of the line segment.
1436    ///
1437    /// The length of the normal is the distance between the endpoints.
1438    #[inline]
1439    pub fn scaled_right_normal(&self) -> Vec2 {
1440        let scaled_direction = self.scaled_direction();
1441        Vec2::new(scaled_direction.y, -scaled_direction.x)
1442    }
1443
1444    /// Compute the segment transformed by the given [`Isometry2d`].
1445    #[inline]
1446    pub fn transformed(&self, isometry: impl Into<Isometry2d>) -> Self {
1447        let isometry: Isometry2d = isometry.into();
1448        Self::new(
1449            isometry.transform_point(self.point1()),
1450            isometry.transform_point(self.point2()),
1451        )
1452    }
1453
1454    /// Compute the segment translated by the given vector.
1455    #[inline]
1456    pub fn translated(&self, translation: Vec2) -> Segment2d {
1457        Self::new(self.point1() + translation, self.point2() + translation)
1458    }
1459
1460    /// Compute the segment rotated around the origin by the given rotation.
1461    #[inline]
1462    pub fn rotated(&self, rotation: Rot2) -> Segment2d {
1463        Segment2d::new(rotation * self.point1(), rotation * self.point2())
1464    }
1465
1466    /// Compute the segment rotated around the given point by the given rotation.
1467    #[inline]
1468    pub fn rotated_around(&self, rotation: Rot2, point: Vec2) -> Segment2d {
1469        // We offset our segment so that our segment is rotated as if from the origin, then we can apply the offset back
1470        let offset = self.translated(-point);
1471        let rotated = offset.rotated(rotation);
1472        rotated.translated(point)
1473    }
1474
1475    /// Compute the segment rotated around its own center.
1476    #[inline]
1477    pub fn rotated_around_center(&self, rotation: Rot2) -> Segment2d {
1478        self.rotated_around(rotation, self.center())
1479    }
1480
1481    /// Compute the segment with its center at the origin, keeping the same direction and length.
1482    #[inline]
1483    pub fn centered(&self) -> Segment2d {
1484        let center = self.center();
1485        self.translated(-center)
1486    }
1487
1488    /// Compute the segment with a new length, keeping the same direction and center.
1489    #[inline]
1490    pub fn resized(&self, length: f32) -> Segment2d {
1491        let offset_from_origin = self.center();
1492        let centered = self.translated(-offset_from_origin);
1493        let ratio = length / self.length();
1494        let segment = Segment2d::new(centered.point1() * ratio, centered.point2() * ratio);
1495        segment.translated(offset_from_origin)
1496    }
1497
1498    /// Reverses the direction of the line segment by swapping the endpoints.
1499    #[inline]
1500    pub fn reverse(&mut self) {
1501        let [point1, point2] = &mut self.vertices;
1502        core::mem::swap(point1, point2);
1503    }
1504
1505    /// Returns the line segment with its direction reversed by swapping the endpoints.
1506    #[inline]
1507    #[must_use]
1508    pub fn reversed(mut self) -> Self {
1509        self.reverse();
1510        self
1511    }
1512
1513    /// Returns the point on the [`Segment2d`] that is closest to the specified `point`.
1514    #[inline]
1515    pub fn closest_point(&self, point: Vec2) -> Vec2 {
1516        //       `point`
1517        //           x
1518        //          ^|
1519        //         / |
1520        //`offset`/  |
1521        //       /   |  `segment_vector`
1522        //      x----.-------------->x
1523        //      0    t               1
1524        let segment_vector = self.vertices[1] - self.vertices[0];
1525        let offset = point - self.vertices[0];
1526        // The signed projection of `offset` onto `segment_vector`, scaled by the length of the segment.
1527        let projection_scaled = segment_vector.dot(offset);
1528
1529        // `point` is too far "left" in the picture
1530        if projection_scaled <= 0.0 {
1531            return self.vertices[0];
1532        }
1533
1534        let length_squared = segment_vector.length_squared();
1535        // `point` is too far "right" in the picture
1536        if projection_scaled >= length_squared {
1537            return self.vertices[1];
1538        }
1539
1540        // Point lies somewhere in the middle, we compute the closest point by finding the parameter along the line.
1541        let t = projection_scaled / length_squared;
1542        self.vertices[0] + t * segment_vector
1543    }
1544}
1545
1546impl From<[Vec2; 2]> for Segment2d {
1547    #[inline]
1548    fn from(vertices: [Vec2; 2]) -> Self {
1549        Self { vertices }
1550    }
1551}
1552
1553impl From<(Vec2, Vec2)> for Segment2d {
1554    #[inline]
1555    fn from((point1, point2): (Vec2, Vec2)) -> Self {
1556        Self::new(point1, point2)
1557    }
1558}
1559
1560/// A series of connected line segments in 2D space.
1561#[cfg(feature = "alloc")]
1562#[derive(Clone, Debug, PartialEq)]
1563#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1564#[cfg_attr(
1565    feature = "bevy_reflect",
1566    derive(Reflect),
1567    reflect(Debug, PartialEq, Clone)
1568)]
1569#[cfg_attr(
1570    all(feature = "serialize", feature = "bevy_reflect"),
1571    reflect(Serialize, Deserialize)
1572)]
1573pub struct Polyline2d {
1574    /// The vertices of the polyline
1575    pub vertices: Vec<Vec2>,
1576}
1577
1578#[cfg(feature = "alloc")]
1579impl Primitive2d for Polyline2d {}
1580
1581#[cfg(feature = "alloc")]
1582impl FromIterator<Vec2> for Polyline2d {
1583    fn from_iter<I: IntoIterator<Item = Vec2>>(iter: I) -> Self {
1584        Self {
1585            vertices: iter.into_iter().collect(),
1586        }
1587    }
1588}
1589
1590#[cfg(feature = "alloc")]
1591impl Default for Polyline2d {
1592    fn default() -> Self {
1593        Self {
1594            vertices: Vec::from([Vec2::new(-0.5, 0.0), Vec2::new(0.5, 0.0)]),
1595        }
1596    }
1597}
1598
1599#[cfg(feature = "alloc")]
1600impl Polyline2d {
1601    /// Create a new `Polyline2d` from its vertices
1602    pub fn new(vertices: impl IntoIterator<Item = Vec2>) -> Self {
1603        Self::from_iter(vertices)
1604    }
1605
1606    /// Create a new `Polyline2d` from two endpoints with subdivision points.
1607    /// `subdivisions = 0` creates a simple line with just start and end points.
1608    /// `subdivisions = 1` adds one point in the middle, creating 2 segments, etc.
1609    pub fn with_subdivisions(start: Vec2, end: Vec2, subdivisions: usize) -> Self {
1610        let total_vertices = subdivisions + 2;
1611        let mut vertices = Vec::with_capacity(total_vertices);
1612
1613        let step = (end - start) / (subdivisions + 1) as f32;
1614        for i in 0..total_vertices {
1615            vertices.push(start + step * i as f32);
1616        }
1617
1618        Self { vertices }
1619    }
1620}
1621
1622/// A triangle in 2D space
1623#[derive(Clone, Copy, Debug, PartialEq)]
1624#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1625#[cfg_attr(
1626    feature = "bevy_reflect",
1627    derive(Reflect),
1628    reflect(Debug, PartialEq, Default, Clone)
1629)]
1630#[cfg_attr(
1631    all(feature = "serialize", feature = "bevy_reflect"),
1632    reflect(Serialize, Deserialize)
1633)]
1634pub struct Triangle2d {
1635    /// The vertices of the triangle
1636    pub vertices: [Vec2; 3],
1637}
1638
1639impl Primitive2d for Triangle2d {}
1640
1641impl Default for Triangle2d {
1642    /// Returns the default [`Triangle2d`] with the vertices `[0.0, 0.5]`, `[-0.5, -0.5]`, and `[0.5, -0.5]`.
1643    fn default() -> Self {
1644        Self {
1645            vertices: [Vec2::Y * 0.5, Vec2::new(-0.5, -0.5), Vec2::new(0.5, -0.5)],
1646        }
1647    }
1648}
1649
1650impl Triangle2d {
1651    /// Create a new `Triangle2d` from points `a`, `b`, and `c`
1652    #[inline]
1653    pub const fn new(a: Vec2, b: Vec2, c: Vec2) -> Self {
1654        Self {
1655            vertices: [a, b, c],
1656        }
1657    }
1658
1659    /// Get the [`WindingOrder`] of the triangle
1660    #[inline]
1661    #[doc(alias = "orientation")]
1662    pub fn winding_order(&self) -> WindingOrder {
1663        let [a, b, c] = self.vertices;
1664        let area = (b - a).perp_dot(c - a);
1665        if area > f32::EPSILON {
1666            WindingOrder::CounterClockwise
1667        } else if area < -f32::EPSILON {
1668            WindingOrder::Clockwise
1669        } else {
1670            WindingOrder::Invalid
1671        }
1672    }
1673
1674    /// Compute the circle passing through all three vertices of the triangle.
1675    /// The vector in the returned tuple is the circumcenter.
1676    pub fn circumcircle(&self) -> (Circle, Vec2) {
1677        // We treat the triangle as translated so that vertex A is at the origin. This simplifies calculations.
1678        //
1679        //     A = (0, 0)
1680        //        *
1681        //       / \
1682        //      /   \
1683        //     /     \
1684        //    /       \
1685        //   /    U    \
1686        //  /           \
1687        // *-------------*
1688        // B             C
1689
1690        let a = self.vertices[0];
1691        let (b, c) = (self.vertices[1] - a, self.vertices[2] - a);
1692        let b_length_sq = b.length_squared();
1693        let c_length_sq = c.length_squared();
1694
1695        // Reference: https://en.wikipedia.org/wiki/Circumcircle#Cartesian_coordinates_2
1696        let inv_d = (2.0 * (b.x * c.y - b.y * c.x)).recip();
1697        let ux = inv_d * (c.y * b_length_sq - b.y * c_length_sq);
1698        let uy = inv_d * (b.x * c_length_sq - c.x * b_length_sq);
1699        let u = Vec2::new(ux, uy);
1700
1701        // Compute true circumcenter and circumradius, adding the tip coordinate so that
1702        // A is translated back to its actual coordinate.
1703        let center = u + a;
1704        let radius = u.length();
1705
1706        (Circle { radius }, center)
1707    }
1708
1709    /// Checks if the triangle is degenerate, meaning it has zero area.
1710    ///
1711    /// A triangle is degenerate if the cross product of the vectors `ab` and `ac` has a length less than `10e-7`.
1712    /// This indicates that the three vertices are collinear or nearly collinear.
1713    #[inline]
1714    pub fn is_degenerate(&self) -> bool {
1715        let [a, b, c] = self.vertices;
1716        let ab = (b - a).extend(0.);
1717        let ac = (c - a).extend(0.);
1718        ab.cross(ac).length() < 10e-7
1719    }
1720
1721    /// Checks if the triangle is acute, meaning all angles are less than 90 degrees
1722    #[inline]
1723    pub fn is_acute(&self) -> bool {
1724        let [a, b, c] = self.vertices;
1725        let ab = b - a;
1726        let bc = c - b;
1727        let ca = a - c;
1728
1729        // a^2 + b^2 < c^2 for an acute triangle
1730        let side_lengths = [
1731            ab.length_squared(),
1732            bc.length_squared(),
1733            ca.length_squared(),
1734        ];
1735        let sum = side_lengths[0] + side_lengths[1] + side_lengths[2];
1736        let max = side_lengths[0].max(side_lengths[1]).max(side_lengths[2]);
1737        sum - max > max
1738    }
1739
1740    /// Checks if the triangle is obtuse, meaning one angle is greater than 90 degrees
1741    #[inline]
1742    pub fn is_obtuse(&self) -> bool {
1743        let [a, b, c] = self.vertices;
1744        let ab = b - a;
1745        let bc = c - b;
1746        let ca = a - c;
1747
1748        // a^2 + b^2 > c^2 for an obtuse triangle
1749        let side_lengths = [
1750            ab.length_squared(),
1751            bc.length_squared(),
1752            ca.length_squared(),
1753        ];
1754        let sum = side_lengths[0] + side_lengths[1] + side_lengths[2];
1755        let max = side_lengths[0].max(side_lengths[1]).max(side_lengths[2]);
1756        sum - max < max
1757    }
1758
1759    /// Reverse the [`WindingOrder`] of the triangle
1760    /// by swapping the first and last vertices.
1761    #[inline]
1762    pub fn reverse(&mut self) {
1763        self.vertices.swap(0, 2);
1764    }
1765
1766    /// This triangle but reversed.
1767    #[inline]
1768    #[must_use]
1769    pub fn reversed(mut self) -> Self {
1770        self.reverse();
1771        self
1772    }
1773}
1774
1775impl Measured2d for Triangle2d {
1776    /// Get the area of the triangle
1777    #[inline]
1778    fn area(&self) -> f32 {
1779        let [a, b, c] = self.vertices;
1780        ops::abs(a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y)) / 2.0
1781    }
1782
1783    /// Get the perimeter of the triangle
1784    #[inline]
1785    fn perimeter(&self) -> f32 {
1786        let [a, b, c] = self.vertices;
1787
1788        let ab = a.distance(b);
1789        let bc = b.distance(c);
1790        let ca = c.distance(a);
1791
1792        ab + bc + ca
1793    }
1794}
1795
1796/// A rectangle primitive, which is like a square, except that the width and height can be different
1797#[derive(Clone, Copy, Debug, PartialEq)]
1798#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1799#[cfg_attr(
1800    feature = "bevy_reflect",
1801    derive(Reflect),
1802    reflect(Debug, PartialEq, Default, Clone)
1803)]
1804#[cfg_attr(
1805    all(feature = "serialize", feature = "bevy_reflect"),
1806    reflect(Serialize, Deserialize)
1807)]
1808#[doc(alias = "Quad")]
1809pub struct Rectangle {
1810    /// Half of the width and height of the rectangle
1811    pub half_size: Vec2,
1812}
1813
1814impl Primitive2d for Rectangle {}
1815
1816impl Default for Rectangle {
1817    /// Returns the default [`Rectangle`] with a half-width and half-height of `0.5`.
1818    fn default() -> Self {
1819        Self {
1820            half_size: Vec2::splat(0.5),
1821        }
1822    }
1823}
1824
1825impl Rectangle {
1826    /// Create a new `Rectangle` from a full width and height
1827    #[inline]
1828    pub const fn new(width: f32, height: f32) -> Self {
1829        Self::from_size(Vec2::new(width, height))
1830    }
1831
1832    /// Create a new `Rectangle` from a given full size
1833    #[inline]
1834    pub const fn from_size(size: Vec2) -> Self {
1835        Self {
1836            half_size: Vec2::new(size.x / 2.0, size.y / 2.0),
1837        }
1838    }
1839
1840    /// Create a new `Rectangle` from two corner points
1841    #[inline]
1842    pub fn from_corners(point1: Vec2, point2: Vec2) -> Self {
1843        Self {
1844            half_size: (point2 - point1).abs() / 2.0,
1845        }
1846    }
1847
1848    /// Create a `Rectangle` from a single length.
1849    /// The resulting `Rectangle` will be the same size in every direction.
1850    #[inline]
1851    pub const fn from_length(length: f32) -> Self {
1852        Self {
1853            half_size: Vec2::splat(length / 2.0),
1854        }
1855    }
1856
1857    /// Get the size of the rectangle
1858    #[inline]
1859    pub fn size(&self) -> Vec2 {
1860        2.0 * self.half_size
1861    }
1862
1863    /// Finds the point on the rectangle that is closest to the given `point`.
1864    ///
1865    /// If the point is outside the rectangle, the returned point will be on the perimeter of the rectangle.
1866    /// Otherwise, it will be inside the rectangle and returned as is.
1867    #[inline]
1868    pub fn closest_point(&self, point: Vec2) -> Vec2 {
1869        // Clamp point coordinates to the rectangle
1870        point.clamp(-self.half_size, self.half_size)
1871    }
1872}
1873
1874impl Measured2d for Rectangle {
1875    /// Get the area of the rectangle
1876    #[inline]
1877    fn area(&self) -> f32 {
1878        4.0 * self.half_size.x * self.half_size.y
1879    }
1880
1881    /// Get the perimeter of the rectangle
1882    #[inline]
1883    fn perimeter(&self) -> f32 {
1884        4.0 * (self.half_size.x + self.half_size.y)
1885    }
1886}
1887
1888/// A polygon with N vertices.
1889#[cfg(feature = "alloc")]
1890#[derive(Clone, Debug, PartialEq)]
1891#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1892#[cfg_attr(
1893    feature = "bevy_reflect",
1894    derive(Reflect),
1895    reflect(Debug, PartialEq, Clone)
1896)]
1897#[cfg_attr(
1898    all(feature = "serialize", feature = "bevy_reflect"),
1899    reflect(Serialize, Deserialize)
1900)]
1901pub struct Polygon {
1902    /// The vertices of the `Polygon`
1903    pub vertices: Vec<Vec2>,
1904}
1905
1906#[cfg(feature = "alloc")]
1907impl Primitive2d for Polygon {}
1908
1909#[cfg(feature = "alloc")]
1910impl FromIterator<Vec2> for Polygon {
1911    fn from_iter<I: IntoIterator<Item = Vec2>>(iter: I) -> Self {
1912        Self {
1913            vertices: iter.into_iter().collect(),
1914        }
1915    }
1916}
1917
1918#[cfg(feature = "alloc")]
1919impl Polygon {
1920    /// Create a new `Polygon` from its vertices
1921    pub fn new(vertices: impl IntoIterator<Item = Vec2>) -> Self {
1922        Self::from_iter(vertices)
1923    }
1924
1925    /// Tests if the polygon is simple.
1926    ///
1927    /// A polygon is simple if it is not self intersecting and not self tangent.
1928    /// As such, no two edges of the polygon may cross each other and each vertex must not lie on another edge.
1929    #[cfg(feature = "alloc")]
1930    pub fn is_simple(&self) -> bool {
1931        is_polygon_simple(&self.vertices)
1932    }
1933}
1934
1935#[cfg(feature = "alloc")]
1936impl From<ConvexPolygon> for Polygon {
1937    fn from(val: ConvexPolygon) -> Self {
1938        Polygon {
1939            vertices: val.vertices,
1940        }
1941    }
1942}
1943
1944/// A convex polygon with `N` vertices.
1945#[cfg(feature = "alloc")]
1946#[derive(Clone, Debug, PartialEq)]
1947#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1948#[cfg_attr(
1949    feature = "bevy_reflect",
1950    derive(Reflect),
1951    reflect(Debug, PartialEq, Clone)
1952)]
1953#[cfg_attr(
1954    all(feature = "serialize", feature = "bevy_reflect"),
1955    reflect(Serialize, Deserialize)
1956)]
1957pub struct ConvexPolygon {
1958    /// The vertices of the [`ConvexPolygon`].
1959    vertices: Vec<Vec2>,
1960}
1961
1962#[cfg(feature = "alloc")]
1963impl Primitive2d for ConvexPolygon {}
1964
1965/// An error that happens when creating a [`ConvexPolygon`].
1966#[cfg(feature = "alloc")]
1967#[derive(Error, Debug, Clone)]
1968pub enum ConvexPolygonError {
1969    /// The created polygon is not convex.
1970    #[error("The created polygon is not convex")]
1971    Concave,
1972}
1973
1974#[cfg(feature = "alloc")]
1975impl ConvexPolygon {
1976    fn triangle_winding_order(
1977        &self,
1978        a_index: usize,
1979        b_index: usize,
1980        c_index: usize,
1981    ) -> WindingOrder {
1982        let a = self.vertices[a_index];
1983        let b = self.vertices[b_index];
1984        let c = self.vertices[c_index];
1985        Triangle2d::new(a, b, c).winding_order()
1986    }
1987
1988    /// Create a [`ConvexPolygon`] from its `vertices`.
1989    ///
1990    /// # Errors
1991    ///
1992    /// Returns [`ConvexPolygonError::Concave`] if the `vertices` do not form a convex polygon.
1993    pub fn new(vertices: impl IntoIterator<Item = Vec2>) -> Result<Self, ConvexPolygonError> {
1994        let polygon = Self::new_unchecked(vertices);
1995        let len = polygon.vertices.len();
1996        let ref_winding_order = polygon.triangle_winding_order(len - 1, 0, 1);
1997        for i in 1..len {
1998            let winding_order = polygon.triangle_winding_order(i - 1, i, (i + 1) % len);
1999            if winding_order != ref_winding_order {
2000                return Err(ConvexPolygonError::Concave);
2001            }
2002        }
2003        Ok(polygon)
2004    }
2005
2006    /// Create a [`ConvexPolygon`] from its `vertices`, without checks.
2007    /// Use this version only if you know that the `vertices` make up a convex polygon.
2008    #[inline]
2009    pub fn new_unchecked(vertices: impl IntoIterator<Item = Vec2>) -> Self {
2010        Self {
2011            vertices: vertices.into_iter().collect(),
2012        }
2013    }
2014
2015    /// Get the vertices of this polygon
2016    #[inline]
2017    pub fn vertices(&self) -> &[Vec2] {
2018        &self.vertices
2019    }
2020}
2021
2022#[cfg(feature = "alloc")]
2023impl TryFrom<Polygon> for ConvexPolygon {
2024    type Error = ConvexPolygonError;
2025
2026    fn try_from(val: Polygon) -> Result<Self, Self::Error> {
2027        ConvexPolygon::new(val.vertices)
2028    }
2029}
2030
2031/// A polygon centered on the origin where all vertices lie on a circle, equally far apart.
2032#[derive(Clone, Copy, Debug, PartialEq)]
2033#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
2034#[cfg_attr(
2035    feature = "bevy_reflect",
2036    derive(Reflect),
2037    reflect(Debug, PartialEq, Default, Clone)
2038)]
2039#[cfg_attr(
2040    all(feature = "serialize", feature = "bevy_reflect"),
2041    reflect(Serialize, Deserialize)
2042)]
2043pub struct RegularPolygon {
2044    /// The circumcircle on which all vertices lie
2045    pub circumcircle: Circle,
2046    /// The number of sides
2047    pub sides: u32,
2048}
2049
2050impl Primitive2d for RegularPolygon {}
2051
2052impl Default for RegularPolygon {
2053    /// Returns the default [`RegularPolygon`] with six sides (a hexagon) and a circumradius of `0.5`.
2054    fn default() -> Self {
2055        Self {
2056            circumcircle: Circle { radius: 0.5 },
2057            sides: 6,
2058        }
2059    }
2060}
2061
2062impl RegularPolygon {
2063    /// Create a new `RegularPolygon`
2064    /// from the radius of the circumcircle and a number of sides
2065    ///
2066    /// # Panics
2067    ///
2068    /// Panics if `circumradius` is negative
2069    #[inline]
2070    pub const fn new(circumradius: f32, sides: u32) -> Self {
2071        assert!(
2072            circumradius.is_sign_positive(),
2073            "polygon has a negative radius"
2074        );
2075        assert!(sides > 2, "polygon has less than 3 sides");
2076
2077        Self {
2078            circumcircle: Circle {
2079                radius: circumradius,
2080            },
2081            sides,
2082        }
2083    }
2084
2085    /// Get the radius of the circumcircle on which all vertices
2086    /// of the regular polygon lie
2087    #[inline]
2088    pub const fn circumradius(&self) -> f32 {
2089        self.circumcircle.radius
2090    }
2091
2092    /// Get the inradius or apothem of the regular polygon.
2093    /// This is the radius of the largest circle that can
2094    /// be drawn within the polygon
2095    #[inline]
2096    #[doc(alias = "apothem")]
2097    pub fn inradius(&self) -> f32 {
2098        self.circumradius() * ops::cos(PI / self.sides as f32)
2099    }
2100
2101    /// Get the length of one side of the regular polygon
2102    #[inline]
2103    pub fn side_length(&self) -> f32 {
2104        2.0 * self.circumradius() * ops::sin(PI / self.sides as f32)
2105    }
2106
2107    /// Get the internal angle of the regular polygon in degrees.
2108    ///
2109    /// This is the angle formed by two adjacent sides with points
2110    /// within the angle being in the interior of the polygon
2111    #[inline]
2112    pub const fn internal_angle_degrees(&self) -> f32 {
2113        (self.sides - 2) as f32 / self.sides as f32 * 180.0
2114    }
2115
2116    /// Get the internal angle of the regular polygon in radians.
2117    ///
2118    /// This is the angle formed by two adjacent sides with points
2119    /// within the angle being in the interior of the polygon
2120    #[inline]
2121    pub const fn internal_angle_radians(&self) -> f32 {
2122        (self.sides - 2) as f32 * PI / self.sides as f32
2123    }
2124
2125    /// Get the external angle of the regular polygon in degrees.
2126    ///
2127    /// This is the angle formed by two adjacent sides with points
2128    /// within the angle being in the exterior of the polygon
2129    #[inline]
2130    pub const fn external_angle_degrees(&self) -> f32 {
2131        360.0 / self.sides as f32
2132    }
2133
2134    /// Get the external angle of the regular polygon in radians.
2135    ///
2136    /// This is the angle formed by two adjacent sides with points
2137    /// within the angle being in the exterior of the polygon
2138    #[inline]
2139    pub const fn external_angle_radians(&self) -> f32 {
2140        2.0 * PI / self.sides as f32
2141    }
2142
2143    /// Returns an iterator over the vertices of the regular polygon,
2144    /// rotated counterclockwise by the given angle in radians.
2145    ///
2146    /// With a rotation of 0, a vertex will be placed at the top `(0.0, circumradius)`.
2147    pub fn vertices(self, rotation: f32) -> impl IntoIterator<Item = Vec2> {
2148        // Add pi/2 so that the polygon has a vertex at the top (sin is 1.0 and cos is 0.0)
2149        let start_angle = rotation + FRAC_PI_2;
2150        let step = core::f32::consts::TAU / self.sides as f32;
2151
2152        (0..self.sides).map(move |i| {
2153            let theta = start_angle + i as f32 * step;
2154            let (sin, cos) = ops::sin_cos(theta);
2155            Vec2::new(cos, sin) * self.circumcircle.radius
2156        })
2157    }
2158}
2159
2160impl Measured2d for RegularPolygon {
2161    /// Get the area of the regular polygon
2162    #[inline]
2163    fn area(&self) -> f32 {
2164        let angle: f32 = 2.0 * PI / (self.sides as f32);
2165        (self.sides as f32) * self.circumradius().squared() * ops::sin(angle) / 2.0
2166    }
2167
2168    /// Get the perimeter of the regular polygon.
2169    /// This is the sum of its sides
2170    #[inline]
2171    fn perimeter(&self) -> f32 {
2172        self.sides as f32 * self.side_length()
2173    }
2174}
2175
2176/// A 2D capsule primitive, also known as a stadium or pill shape.
2177///
2178/// A two-dimensional capsule is defined as a neighborhood of points at a distance (radius) from a line
2179#[derive(Clone, Copy, Debug, PartialEq)]
2180#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
2181#[cfg_attr(
2182    feature = "bevy_reflect",
2183    derive(Reflect),
2184    reflect(Debug, PartialEq, Default, Clone)
2185)]
2186#[cfg_attr(
2187    all(feature = "serialize", feature = "bevy_reflect"),
2188    reflect(Serialize, Deserialize)
2189)]
2190#[doc(alias = "stadium", alias = "pill")]
2191pub struct Capsule2d {
2192    /// The radius of the capsule
2193    pub radius: f32,
2194    /// Half the height of the capsule, excluding the semicircles
2195    pub half_length: f32,
2196}
2197
2198impl Primitive2d for Capsule2d {}
2199
2200impl Default for Capsule2d {
2201    /// Returns the default [`Capsule2d`] with a radius of `0.5` and a half-height of `0.5`,
2202    /// excluding the semicircles.
2203    fn default() -> Self {
2204        Self {
2205            radius: 0.5,
2206            half_length: 0.5,
2207        }
2208    }
2209}
2210
2211impl Capsule2d {
2212    /// Create a new `Capsule2d` from a radius and length
2213    pub const fn new(radius: f32, length: f32) -> Self {
2214        Self {
2215            radius,
2216            half_length: length / 2.0,
2217        }
2218    }
2219
2220    /// Get the part connecting the semicircular ends of the capsule as a [`Rectangle`]
2221    #[inline]
2222    pub const fn to_inner_rectangle(&self) -> Rectangle {
2223        Rectangle::new(self.radius * 2.0, self.half_length * 2.0)
2224    }
2225}
2226
2227impl Measured2d for Capsule2d {
2228    /// Get the area of the capsule
2229    #[inline]
2230    fn area(&self) -> f32 {
2231        // pi*r^2 + (2r)*l
2232        PI * self.radius.squared() + self.to_inner_rectangle().area()
2233    }
2234
2235    /// Get the perimeter of the capsule
2236    #[inline]
2237    fn perimeter(&self) -> f32 {
2238        // 2pi*r + 2l
2239        2.0 * PI * self.radius + 4.0 * self.half_length
2240    }
2241}
2242
2243/// A 2D shape representing the ring version of a base shape.
2244///
2245/// The `inner_shape` forms the "hollow" of the `outer_shape`.
2246///
2247/// The resulting shapes are rings or hollow shapes.
2248/// For example, a circle becomes an annulus.
2249///
2250/// # Warning
2251///
2252/// The `outer_shape` must contain the `inner_shape` for the generated meshes to be accurate.
2253///
2254/// If there are vertices in the `inner_shape` that escape the `outer_shape`
2255/// (for example, if the `inner_shape` is in fact larger),
2256/// it may result in incorrect geometries.
2257#[derive(Clone, Copy, Debug, PartialEq)]
2258#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
2259pub struct Ring<P: Primitive2d> {
2260    /// The outer shape
2261    pub outer_shape: P,
2262    /// The inner shape (the same shape of a different size)
2263    pub inner_shape: P,
2264}
2265
2266impl<P: Primitive2d> Ring<P> {
2267    /// Create a new `Ring` from a given `outer_shape` and `inner_shape`.
2268    ///
2269    /// If the primitive implements [`Inset`] and you would like a uniform thickness, consider using [`ToRing::to_ring`]
2270    pub const fn new(outer_shape: P, inner_shape: P) -> Self {
2271        Self {
2272            outer_shape,
2273            inner_shape,
2274        }
2275    }
2276}
2277
2278impl<T: Primitive2d> Primitive2d for Ring<T> {}
2279
2280impl<P: Primitive2d + Clone + Inset> Ring<P> {
2281    /// Generate a `Ring` from a given `primitive` and a `thickness`.
2282    pub fn from_primitive_and_thickness(primitive: P, thickness: f32) -> Self {
2283        let hollow = primitive.clone().inset(thickness);
2284        Ring::new(primitive, hollow)
2285    }
2286}
2287
2288impl<P: Primitive2d + Measured2d> Measured2d for Ring<P> {
2289    #[inline]
2290    fn area(&self) -> f32 {
2291        self.outer_shape.area() - self.inner_shape.area()
2292    }
2293
2294    #[inline]
2295    fn perimeter(&self) -> f32 {
2296        self.outer_shape.perimeter() + self.inner_shape.perimeter()
2297    }
2298}
2299
2300/// Provides a convenience method for converting a primitive to a [`Ring`], with a given thickness.
2301///
2302/// The primitive must implement [`Inset`].
2303pub trait ToRing: Primitive2d + Inset
2304where
2305    Self: Sized,
2306{
2307    /// Construct a `Ring`
2308    fn to_ring(self, thickness: f32) -> Ring<Self>;
2309}
2310
2311impl<P> ToRing for P
2312where
2313    P: Primitive2d + Clone + Inset,
2314{
2315    fn to_ring(self, thickness: f32) -> Ring<Self> {
2316        Ring::from_primitive_and_thickness(self, thickness)
2317    }
2318}
2319
2320#[cfg(test)]
2321mod tests {
2322    // Reference values were computed by hand and/or with external tools
2323
2324    use super::*;
2325    use approx::{assert_abs_diff_eq, assert_relative_eq};
2326
2327    #[test]
2328    fn rectangle_closest_point() {
2329        let rectangle = Rectangle::new(2.0, 2.0);
2330        assert_eq!(rectangle.closest_point(Vec2::X * 10.0), Vec2::X);
2331        assert_eq!(rectangle.closest_point(Vec2::NEG_ONE * 10.0), Vec2::NEG_ONE);
2332        assert_eq!(
2333            rectangle.closest_point(Vec2::new(0.25, 0.1)),
2334            Vec2::new(0.25, 0.1)
2335        );
2336    }
2337
2338    #[test]
2339    fn circle_closest_point() {
2340        let circle = Circle { radius: 1.0 };
2341        assert_eq!(circle.closest_point(Vec2::X * 10.0), Vec2::X);
2342        assert_eq!(
2343            circle.closest_point(Vec2::NEG_ONE * 10.0),
2344            Vec2::NEG_ONE.normalize()
2345        );
2346        assert_eq!(
2347            circle.closest_point(Vec2::new(0.25, 0.1)),
2348            Vec2::new(0.25, 0.1)
2349        );
2350    }
2351
2352    #[test]
2353    fn annulus_closest_point() {
2354        let annulus = Annulus::new(1.5, 2.0);
2355        assert_eq!(annulus.closest_point(Vec2::X * 10.0), Vec2::X * 2.0);
2356        assert_eq!(
2357            annulus.closest_point(Vec2::NEG_ONE),
2358            Vec2::NEG_ONE.normalize() * 1.5
2359        );
2360        assert_eq!(
2361            annulus.closest_point(Vec2::new(1.55, 0.85)),
2362            Vec2::new(1.55, 0.85)
2363        );
2364    }
2365
2366    #[test]
2367    fn rhombus_closest_point() {
2368        let rhombus = Rhombus::new(2.0, 1.0);
2369        assert_eq!(rhombus.closest_point(Vec2::X * 10.0), Vec2::X);
2370        assert_eq!(
2371            rhombus.closest_point(Vec2::NEG_ONE * 0.2),
2372            Vec2::NEG_ONE * 0.2
2373        );
2374        assert_eq!(
2375            rhombus.closest_point(Vec2::new(-0.55, 0.35)),
2376            Vec2::new(-0.5, 0.25)
2377        );
2378
2379        let rhombus = Rhombus::new(0.0, 0.0);
2380        assert_eq!(rhombus.closest_point(Vec2::X * 10.0), Vec2::ZERO);
2381        assert_eq!(rhombus.closest_point(Vec2::NEG_ONE * 0.2), Vec2::ZERO);
2382        assert_eq!(rhombus.closest_point(Vec2::new(-0.55, 0.35)), Vec2::ZERO);
2383    }
2384
2385    #[test]
2386    fn segment_closest_point() {
2387        assert_eq!(
2388            Segment2d::new(Vec2::new(0.0, 0.0), Vec2::new(3.0, 0.0))
2389                .closest_point(Vec2::new(1.0, 6.0)),
2390            Vec2::new(1.0, 0.0)
2391        );
2392
2393        let segments = [
2394            Segment2d::new(Vec2::new(0.0, 0.0), Vec2::new(0.0, 0.0)),
2395            Segment2d::new(Vec2::new(0.0, 0.0), Vec2::new(1.0, 0.0)),
2396            Segment2d::new(Vec2::new(1.0, 0.0), Vec2::new(0.0, 1.0)),
2397            Segment2d::new(Vec2::new(1.0, 0.0), Vec2::new(1.0, 5.0 * f32::EPSILON)),
2398        ];
2399        let points = [
2400            Vec2::new(0.0, 0.0),
2401            Vec2::new(1.0, 0.0),
2402            Vec2::new(-1.0, 1.0),
2403            Vec2::new(1.0, 1.0),
2404            Vec2::new(-1.0, 0.0),
2405            Vec2::new(5.0, -1.0),
2406            Vec2::new(1.0, f32::EPSILON),
2407        ];
2408
2409        for point in points.iter() {
2410            for segment in segments.iter() {
2411                let closest = segment.closest_point(*point);
2412                assert!(
2413                    point.distance_squared(closest) <= point.distance_squared(segment.point1()),
2414                    "Closest point must always be at least as close as either vertex."
2415                );
2416                assert!(
2417                    point.distance_squared(closest) <= point.distance_squared(segment.point2()),
2418                    "Closest point must always be at least as close as either vertex."
2419                );
2420                assert!(
2421                    point.distance_squared(closest) <= point.distance_squared(segment.center()),
2422                    "Closest point must always be at least as close as the center."
2423                );
2424                let closest_to_closest = segment.closest_point(closest);
2425                // Closest point must already be on the segment
2426                assert_relative_eq!(closest_to_closest, closest);
2427            }
2428        }
2429    }
2430
2431    #[test]
2432    fn circle_math() {
2433        let circle = Circle { radius: 3.0 };
2434        assert_eq!(circle.diameter(), 6.0, "incorrect diameter");
2435        assert_eq!(circle.area(), 28.274334, "incorrect area");
2436        assert_eq!(circle.perimeter(), 18.849556, "incorrect perimeter");
2437    }
2438
2439    #[test]
2440    fn capsule_math() {
2441        let capsule = Capsule2d::new(2.0, 9.0);
2442        assert_eq!(
2443            capsule.to_inner_rectangle(),
2444            Rectangle::new(4.0, 9.0),
2445            "rectangle wasn't created correctly from a capsule"
2446        );
2447        assert_eq!(capsule.area(), 48.566371, "incorrect area");
2448        assert_eq!(capsule.perimeter(), 30.566371, "incorrect perimeter");
2449    }
2450
2451    #[test]
2452    fn annulus_math() {
2453        let annulus = Annulus::new(2.5, 3.5);
2454        assert_eq!(annulus.diameter(), 7.0, "incorrect diameter");
2455        assert_eq!(annulus.thickness(), 1.0, "incorrect thickness");
2456        assert_eq!(annulus.area(), 18.849556, "incorrect area");
2457        assert_eq!(annulus.perimeter(), 37.699112, "incorrect perimeter");
2458    }
2459
2460    #[test]
2461    fn rhombus_math() {
2462        let rhombus = Rhombus::new(3.0, 4.0);
2463        assert_eq!(rhombus.area(), 6.0, "incorrect area");
2464        assert_eq!(rhombus.perimeter(), 10.0, "incorrect perimeter");
2465        assert_eq!(rhombus.side(), 2.5, "incorrect side");
2466        assert_eq!(rhombus.inradius(), 1.2, "incorrect inradius");
2467        assert_eq!(rhombus.circumradius(), 2.0, "incorrect circumradius");
2468        let rhombus = Rhombus::new(0.0, 0.0);
2469        assert_eq!(rhombus.area(), 0.0, "incorrect area");
2470        assert_eq!(rhombus.perimeter(), 0.0, "incorrect perimeter");
2471        assert_eq!(rhombus.side(), 0.0, "incorrect side");
2472        assert_eq!(rhombus.inradius(), 0.0, "incorrect inradius");
2473        assert_eq!(rhombus.circumradius(), 0.0, "incorrect circumradius");
2474        let rhombus = Rhombus::from_side(core::f32::consts::SQRT_2);
2475        assert_abs_diff_eq!(rhombus.half_diagonals, Vec2::new(1.0, 1.0));
2476        assert_abs_diff_eq!(
2477            rhombus.half_diagonals,
2478            Rhombus::from_inradius(FRAC_1_SQRT_2).half_diagonals
2479        );
2480    }
2481
2482    #[test]
2483    fn ellipse_math() {
2484        let ellipse = Ellipse::new(3.0, 1.0);
2485        assert_eq!(ellipse.area(), 9.424778, "incorrect area");
2486
2487        assert_eq!(ellipse.eccentricity(), 0.94280905, "incorrect eccentricity");
2488
2489        let line = Ellipse::new(1., 0.);
2490        assert_eq!(line.eccentricity(), 1., "incorrect line eccentricity");
2491
2492        let circle = Ellipse::new(2., 2.);
2493        assert_eq!(circle.eccentricity(), 0., "incorrect circle eccentricity");
2494    }
2495
2496    #[test]
2497    fn ellipse_perimeter() {
2498        let circle = Ellipse::new(1., 1.);
2499        assert_relative_eq!(circle.perimeter(), 6.2831855);
2500
2501        let line = Ellipse::new(75_000., 0.5);
2502        assert_relative_eq!(line.perimeter(), 300_000.);
2503
2504        let ellipse = Ellipse::new(0.5, 2.);
2505        assert_relative_eq!(ellipse.perimeter(), 8.578423);
2506
2507        let ellipse = Ellipse::new(5., 3.);
2508        assert_relative_eq!(ellipse.perimeter(), 25.526999);
2509    }
2510
2511    #[test]
2512    fn triangle_math() {
2513        let triangle = Triangle2d::new(
2514            Vec2::new(-2.0, -1.0),
2515            Vec2::new(1.0, 4.0),
2516            Vec2::new(7.0, 0.0),
2517        );
2518        assert_eq!(triangle.area(), 21.0, "incorrect area");
2519        assert_eq!(triangle.perimeter(), 22.097439, "incorrect perimeter");
2520
2521        let degenerate_triangle =
2522            Triangle2d::new(Vec2::new(-1., 0.), Vec2::new(0., 0.), Vec2::new(1., 0.));
2523        assert!(degenerate_triangle.is_degenerate());
2524
2525        let acute_triangle =
2526            Triangle2d::new(Vec2::new(-1., 0.), Vec2::new(1., 0.), Vec2::new(0., 5.));
2527        let obtuse_triangle =
2528            Triangle2d::new(Vec2::new(-1., 0.), Vec2::new(1., 0.), Vec2::new(0., 0.5));
2529
2530        assert!(acute_triangle.is_acute());
2531        assert!(!acute_triangle.is_obtuse());
2532        assert!(!obtuse_triangle.is_acute());
2533        assert!(obtuse_triangle.is_obtuse());
2534    }
2535
2536    #[test]
2537    fn triangle_winding_order() {
2538        let mut cw_triangle = Triangle2d::new(
2539            Vec2::new(0.0, 2.0),
2540            Vec2::new(-0.5, -1.2),
2541            Vec2::new(-1.0, -1.0),
2542        );
2543        assert_eq!(cw_triangle.winding_order(), WindingOrder::Clockwise);
2544
2545        let ccw_triangle = Triangle2d::new(
2546            Vec2::new(-1.0, -1.0),
2547            Vec2::new(-0.5, -1.2),
2548            Vec2::new(0.0, 2.0),
2549        );
2550        assert_eq!(ccw_triangle.winding_order(), WindingOrder::CounterClockwise);
2551
2552        // The clockwise triangle should be the same as the counterclockwise
2553        // triangle when reversed
2554        cw_triangle.reverse();
2555        assert_eq!(cw_triangle, ccw_triangle);
2556
2557        let invalid_triangle = Triangle2d::new(
2558            Vec2::new(0.0, 2.0),
2559            Vec2::new(0.0, -1.0),
2560            Vec2::new(0.0, -1.2),
2561        );
2562        assert_eq!(invalid_triangle.winding_order(), WindingOrder::Invalid);
2563    }
2564
2565    #[test]
2566    fn rectangle_math() {
2567        let rectangle = Rectangle::new(3.0, 7.0);
2568        assert_eq!(
2569            rectangle,
2570            Rectangle::from_corners(Vec2::new(-1.5, -3.5), Vec2::new(1.5, 3.5))
2571        );
2572        assert_eq!(rectangle.area(), 21.0, "incorrect area");
2573        assert_eq!(rectangle.perimeter(), 20.0, "incorrect perimeter");
2574    }
2575
2576    #[test]
2577    fn regular_polygon_math() {
2578        let polygon = RegularPolygon::new(3.0, 6);
2579        assert_eq!(polygon.inradius(), 2.598076, "incorrect inradius");
2580        assert_eq!(polygon.side_length(), 3.0, "incorrect side length");
2581        assert_relative_eq!(polygon.area(), 23.38268, epsilon = 0.00001);
2582        assert_eq!(polygon.perimeter(), 18.0, "incorrect perimeter");
2583        assert_eq!(
2584            polygon.internal_angle_degrees(),
2585            120.0,
2586            "incorrect internal angle"
2587        );
2588        assert_eq!(
2589            polygon.internal_angle_radians(),
2590            120_f32.to_radians(),
2591            "incorrect internal angle"
2592        );
2593        assert_eq!(
2594            polygon.external_angle_degrees(),
2595            60.0,
2596            "incorrect external angle"
2597        );
2598        assert_eq!(
2599            polygon.external_angle_radians(),
2600            60_f32.to_radians(),
2601            "incorrect external angle"
2602        );
2603    }
2604
2605    #[test]
2606    fn triangle_circumcenter() {
2607        let triangle = Triangle2d::new(
2608            Vec2::new(10.0, 2.0),
2609            Vec2::new(-5.0, -3.0),
2610            Vec2::new(2.0, -1.0),
2611        );
2612        let (Circle { radius }, circumcenter) = triangle.circumcircle();
2613
2614        // Calculated with external calculator
2615        assert_eq!(radius, 98.34887);
2616        assert_eq!(circumcenter, Vec2::new(-28.5, 92.5));
2617    }
2618
2619    #[test]
2620    fn regular_polygon_vertices() {
2621        let polygon = RegularPolygon::new(1.0, 4);
2622
2623        // Regular polygons have a vertex at the top by default
2624        let mut vertices = polygon.vertices(0.0).into_iter();
2625        assert!((vertices.next().unwrap() - Vec2::Y).length() < 1e-7);
2626
2627        // Rotate by 45 degrees, forming an axis-aligned square
2628        let mut rotated_vertices = polygon.vertices(core::f32::consts::FRAC_PI_4).into_iter();
2629
2630        // Distance from the origin to the middle of a side, derived using Pythagorean theorem
2631        let side_distance = FRAC_1_SQRT_2;
2632        assert!(
2633            (rotated_vertices.next().unwrap() - Vec2::new(-side_distance, side_distance)).length()
2634                < 1e-7,
2635        );
2636    }
2637}