Skip to main content

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