1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! Axis-aligned ellipse constructed from a center point and the x and y radii.

use crate::matrix3x2f::Matrix3x2f;
use crate::point2f::Point2f;

#[cfg(all(windows, feature = "d2d"))]
use winapi::um::d2d1::D2D1_ELLIPSE;

/// Contains the center point, x-radius, and y-radius of an ellipse.
#[derive(Copy, Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
#[repr(C)]
pub struct Ellipse {
    /// The center point of the ellipse.
    pub center: Point2f,
    /// The X-radius of the ellipse.
    pub radius_x: f32,
    /// The Y-radius of the ellipse.
    pub radius_y: f32,
}

impl Ellipse {
    /// Constructs an ellipse from its components
    #[inline]
    pub fn new(center: impl Into<Point2f>, rx: f32, ry: f32) -> Ellipse {
        Ellipse {
            center: center.into(),
            radius_x: rx,
            radius_y: ry,
        }
    }

    /// Checks if an ellipse contains a point
    #[inline]
    pub fn contains_point(&self, point: impl Into<Point2f>) -> bool {
        let point = point.into();
        let px = point.x - self.center.x;
        let px2 = px * px;
        let py = point.y - self.center.y;
        let py2 = py * py;
        let rx2 = self.radius_x * self.radius_x;
        let ry2 = self.radius_y * self.radius_y;

        px2 / rx2 + py2 / ry2 <= 1.0
    }

    /// Determines if an ellipse which has a transform applied to it contains a specified
    /// (non- or pre-transformed) point.
    ///
    /// Will always return false if `!transform.is_invertible()`
    #[inline]
    pub fn contains_point_transformed(
        &self,
        transform: &Matrix3x2f,
        point: impl Into<Point2f>,
    ) -> bool {
        if let Some(inverse) = transform.try_inverse() {
            let point = point.into();
            let point = point * inverse;
            self.contains_point(point)
        } else {
            false
        }
    }
}

impl<P> From<(P, f32, f32)> for Ellipse
where
    P: Into<Point2f>,
{
    #[inline]
    fn from(data: (P, f32, f32)) -> Ellipse {
        Ellipse::new(data.0, data.1, data.2)
    }
}

#[cfg(all(windows, feature = "d2d"))]
impl From<Ellipse> for D2D1_ELLIPSE {
    #[inline]
    fn from(e: Ellipse) -> D2D1_ELLIPSE {
        D2D1_ELLIPSE {
            point: e.center.into(),
            radiusX: e.radius_x,
            radiusY: e.radius_y,
        }
    }
}

#[cfg(all(windows, feature = "d2d"))]
impl From<D2D1_ELLIPSE> for Ellipse {
    #[inline]
    fn from(e: D2D1_ELLIPSE) -> Ellipse {
        Ellipse {
            center: e.point.into(),
            radius_x: e.radiusX,
            radius_y: e.radiusY,
        }
    }
}

#[cfg(all(test, windows, feature = "d2d"))]
#[test]
fn ellipse_d2d_bin_compat() {
    use std::mem::size_of_val;

    fn ptr_eq<T>(a: &T, b: &T) -> bool {
        (a as *const T) == (b as *const T)
    }

    let ellipse = Ellipse::new((0.0, 0.0), 1.0, 0.5);
    let d2d = unsafe { &*((&ellipse) as *const _ as *const D2D1_ELLIPSE) };

    assert!(ptr_eq(&ellipse.center.x, &d2d.point.x));
    assert!(ptr_eq(&ellipse.center.y, &d2d.point.y));
    assert!(ptr_eq(&ellipse.radius_x, &d2d.radiusX));
    assert!(ptr_eq(&ellipse.radius_y, &d2d.radiusY));
    assert_eq!(size_of_val(&ellipse), size_of_val(d2d));
}