math2d/
ellipse.rs

1//! Axis-aligned ellipse constructed from a center point and the x and y radii.
2
3use matrix3x2f::Matrix3x2f;
4use point2f::Point2f;
5
6#[cfg(all(windows, feature = "d2d"))]
7use winapi::um::d2d1::D2D1_ELLIPSE;
8
9/// Contains the center point, x-radius, and y-radius of an ellipse.
10#[derive(Copy, Clone, Debug, Default, PartialEq)]
11#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
12#[repr(C)]
13pub struct Ellipse {
14    /// The center point of the ellipse.
15    pub center: Point2f,
16    /// The X-radius of the ellipse.
17    pub radius_x: f32,
18    /// The Y-radius of the ellipse.
19    pub radius_y: f32,
20}
21
22impl Ellipse {
23    /// Constructs an ellipse from its components
24    #[inline]
25    pub fn new(center: impl Into<Point2f>, rx: f32, ry: f32) -> Ellipse {
26        Ellipse {
27            center: center.into(),
28            radius_x: rx,
29            radius_y: ry,
30        }
31    }
32
33    /// Checks if an ellipse contains a point
34    #[inline]
35    pub fn contains_point(&self, point: impl Into<Point2f>) -> bool {
36        let point = point.into();
37        let px = point.x - self.center.x;
38        let px2 = px * px;
39        let py = point.y - self.center.y;
40        let py2 = py * py;
41        let rx2 = self.radius_x * self.radius_x;
42        let ry2 = self.radius_y * self.radius_y;
43
44        px2 / rx2 + py2 / ry2 <= 1.0
45    }
46
47    /// Determines if an ellipse which has a transform applied to it contains a specified
48    /// (non- or pre-transformed) point.
49    ///
50    /// Will always return false if `!transform.is_invertible()`
51    #[inline]
52    pub fn contains_point_transformed(
53        &self,
54        transform: &Matrix3x2f,
55        point: impl Into<Point2f>,
56    ) -> bool {
57        if let Some(inverse) = transform.try_inverse() {
58            let point = point.into();
59            let point = point * inverse;
60            self.contains_point(point)
61        } else {
62            false
63        }
64    }
65}
66
67impl<P> From<(P, f32, f32)> for Ellipse
68where
69    P: Into<Point2f>,
70{
71    #[inline]
72    fn from(data: (P, f32, f32)) -> Ellipse {
73        Ellipse::new(data.0, data.1, data.2)
74    }
75}
76
77#[cfg(all(windows, feature = "d2d"))]
78impl From<Ellipse> for D2D1_ELLIPSE {
79    #[inline]
80    fn from(e: Ellipse) -> D2D1_ELLIPSE {
81        D2D1_ELLIPSE {
82            point: e.center.into(),
83            radiusX: e.radius_x,
84            radiusY: e.radius_y,
85        }
86    }
87}
88
89#[cfg(all(windows, feature = "d2d"))]
90impl From<D2D1_ELLIPSE> for Ellipse {
91    #[inline]
92    fn from(e: D2D1_ELLIPSE) -> Ellipse {
93        Ellipse {
94            center: e.point.into(),
95            radius_x: e.radiusX,
96            radius_y: e.radiusY,
97        }
98    }
99}
100
101#[cfg(all(test, windows, feature = "d2d"))]
102#[test]
103fn ellipse_d2d_bin_compat() {
104    use std::mem::size_of_val;
105
106    fn ptr_eq<T>(a: &T, b: &T) -> bool {
107        (a as *const T) == (b as *const T)
108    }
109
110    let ellipse = Ellipse::new((0.0, 0.0), 1.0, 0.5);
111    let d2d = unsafe { &*((&ellipse) as *const _ as *const D2D1_ELLIPSE) };
112
113    assert!(ptr_eq(&ellipse.center.x, &d2d.point.x));
114    assert!(ptr_eq(&ellipse.center.y, &d2d.point.y));
115    assert!(ptr_eq(&ellipse.radius_x, &d2d.radiusX));
116    assert!(ptr_eq(&ellipse.radius_y, &d2d.radiusY));
117    assert_eq!(size_of_val(&ellipse), size_of_val(d2d));
118}