use ::num_traits::{AsPrimitive, Num};
use ::std::{fmt::Debug, ops::Add};
#[cfg(feature = "d2d")]
pub use d2d::*;
#[cfg(feature = "win32")]
pub use win32::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(C)]
pub struct Point2D<T>
where
T: Num + Clone + Copy + Debug,
{
pub x: T,
pub y: T,
}
impl<T> Default for Point2D<T>
where
T: Num + Clone + Copy + Debug,
{
fn default() -> Self {
Self {
x: T::zero(),
y: T::zero(),
}
}
}
impl<T> Point2D<T>
where
T: Num + Clone + Copy + Debug,
{
pub fn zero() -> Self {
Self::default()
}
pub fn cast<U>(self) -> Point2D<U>
where
T: AsPrimitive<U>,
U: Num + Clone + Copy + Debug + 'static,
{
Point2D::<U> {
x: self.x.as_(),
y: self.y.as_(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(C)]
pub struct Size2D<T>
where
T: Num + Clone + Copy + Debug,
{
pub width: T,
pub height: T,
}
impl<T> Default for Size2D<T>
where
T: Num + Clone + Copy + Debug,
{
fn default() -> Self {
Self {
width: T::zero(),
height: T::zero(),
}
}
}
impl<T> Size2D<T>
where
T: Num + Clone + Copy + Debug,
{
pub fn zero() -> Self {
Self::default()
}
pub fn pixel() -> Self {
Self {
width: T::one(),
height: T::one(),
}
}
}
impl<T> Size2D<T>
where
T: Num + Clone + Copy + Debug,
{
pub fn cast<U>(self) -> Size2D<U>
where
T: AsPrimitive<U>,
U: Num + Clone + Copy + Debug + 'static,
{
Size2D::<U> {
width: self.width.as_(),
height: self.height.as_(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(C)]
pub struct Rect2D<T>
where
T: Num + Clone + Copy + Debug,
{
pub left: T,
pub top: T,
pub right: T,
pub bottom: T,
}
impl<T> Default for Rect2D<T>
where
T: Num + Clone + Copy + Debug,
{
fn default() -> Self {
Self {
left: T::zero(),
top: T::zero(),
right: T::zero(),
bottom: T::zero(),
}
}
}
impl<T> Rect2D<T>
where
T: Num + Clone + Copy + Debug,
{
pub fn zero() -> Self {
Self::default()
}
}
impl<T> Rect2D<T>
where
T: Num + Clone + Copy + Debug,
{
pub fn with_size_and_origin(size: Size2D<T>, origin: Point2D<T>) -> Self
where
T: Add<Output = T>,
{
Self {
left: origin.x,
top: origin.y,
right: origin.x + size.width,
bottom: origin.y + size.height,
}
}
pub fn width(&self) -> T {
self.right - self.left
}
pub fn height(&self) -> T {
self.bottom - self.top
}
pub fn cast<U>(self) -> Rect2D<U>
where
T: AsPrimitive<U>,
U: Num + Clone + Copy + Debug + 'static,
{
Rect2D::<U> {
left: self.left.as_(),
top: self.top.as_(),
right: self.right.as_(),
bottom: self.bottom.as_(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(C)]
pub struct RoundedRect2D<T>
where
T: Num + Clone + Copy + Debug,
{
pub rect: Rect2D<T>,
pub radius_x: T,
pub radius_y: T,
}
impl<T> Default for RoundedRect2D<T>
where
T: Num + Clone + Copy + Debug,
{
fn default() -> Self {
Self {
rect: Rect2D::zero(),
radius_x: T::zero(),
radius_y: T::zero(),
}
}
}
impl<T> RoundedRect2D<T>
where
T: Num + Clone + Copy + Debug,
{
pub fn zero() -> Self {
Self::default()
}
}
impl<T> RoundedRect2D<T>
where
T: Num + Clone + Copy + Debug,
{
pub fn with_size_and_origin(size: Size2D<T>, origin: Point2D<T>, corner_radius: T) -> Self
where
T: Add<Output = T>,
{
Self {
rect: Rect2D::with_size_and_origin(size, origin),
radius_x: corner_radius,
radius_y: corner_radius,
}
}
pub fn width(&self) -> T {
self.rect.width()
}
pub fn height(&self) -> T {
self.rect.height()
}
pub fn cast<U>(self) -> RoundedRect2D<U>
where
T: AsPrimitive<U>,
U: Num + Clone + Copy + Debug + 'static,
{
RoundedRect2D::<U> {
rect: self.rect.cast(),
radius_x: self.radius_x.as_(),
radius_y: self.radius_y.as_(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(C)]
pub struct Ellipse2D<T>
where
T: Num + Clone + Copy + Debug,
{
pub center: Point2D<T>,
pub radius_x: T,
pub radius_y: T,
}
impl<T> Default for Ellipse2D<T>
where
T: Num + Clone + Copy + Debug,
{
fn default() -> Self {
Self {
center: Point2D::zero(),
radius_x: T::zero(),
radius_y: T::zero(),
}
}
}
impl<T> Ellipse2D<T>
where
T: Num + Clone + Copy + Debug,
{
pub fn zero() -> Self {
Self::default()
}
}
impl<T> Ellipse2D<T>
where
T: Num + Clone + Copy + Debug,
{
pub fn new_circle(center: Point2D<T>, radius: T) -> Self {
Self {
center,
radius_x: radius,
radius_y: radius,
}
}
pub fn cast<U>(self) -> Ellipse2D<U>
where
T: AsPrimitive<U>,
U: Num + Clone + Copy + Debug + 'static,
{
Ellipse2D::<U> {
center: self.center.cast(),
radius_x: self.radius_x.as_(),
radius_y: self.radius_y.as_(),
}
}
}
#[cfg(feature = "win32")]
mod win32 {
use super::*;
use ::windows::Win32::Foundation::RECT;
impl From<Rect2D<i32>> for RECT {
fn from(val: Rect2D<i32>) -> Self {
unsafe { ::std::mem::transmute(val) }
}
}
impl From<RECT> for Rect2D<i32> {
fn from(val: RECT) -> Self {
unsafe { ::std::mem::transmute(val) }
}
}
}
#[cfg(feature = "d2d")]
mod d2d {
use super::*;
use ::windows::Win32::Graphics::Direct2D::{
Common::{D2D_POINT_2F, D2D_RECT_F, D2D_SIZE_U},
D2D1_ELLIPSE, D2D1_ROUNDED_RECT,
};
impl From<Point2D<f32>> for D2D_POINT_2F {
fn from(val: Point2D<f32>) -> Self {
unsafe { ::std::mem::transmute(val) }
}
}
impl From<D2D_POINT_2F> for Point2D<f32> {
fn from(val: D2D_POINT_2F) -> Self {
unsafe { ::std::mem::transmute(val) }
}
}
impl From<Size2D<u32>> for D2D_SIZE_U {
fn from(val: Size2D<u32>) -> Self {
unsafe { ::std::mem::transmute(val) }
}
}
impl From<D2D_SIZE_U> for Size2D<u32> {
fn from(val: D2D_SIZE_U) -> Self {
unsafe { ::std::mem::transmute(val) }
}
}
impl From<Rect2D<f32>> for D2D_RECT_F {
fn from(val: Rect2D<f32>) -> Self {
unsafe { ::std::mem::transmute(val) }
}
}
impl From<D2D_RECT_F> for Rect2D<f32> {
fn from(val: D2D_RECT_F) -> Self {
unsafe { ::std::mem::transmute(val) }
}
}
impl From<RoundedRect2D<f32>> for D2D1_ROUNDED_RECT {
fn from(val: RoundedRect2D<f32>) -> Self {
unsafe { ::std::mem::transmute(val) }
}
}
impl From<D2D1_ROUNDED_RECT> for RoundedRect2D<f32> {
fn from(val: D2D1_ROUNDED_RECT) -> Self {
unsafe { ::std::mem::transmute(val) }
}
}
impl From<Ellipse2D<f32>> for D2D1_ELLIPSE {
fn from(val: Ellipse2D<f32>) -> Self {
unsafe { ::std::mem::transmute(val) }
}
}
impl From<D2D1_ELLIPSE> for Ellipse2D<f32> {
fn from(val: D2D1_ELLIPSE) -> Self {
unsafe { ::std::mem::transmute(val) }
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use ::pretty_assertions::assert_eq;
#[cfg(feature = "win32")]
use ::windows::Win32::Foundation::RECT;
#[cfg(feature = "d2d")]
use ::windows::Win32::Graphics::Direct2D::{
Common::{D2D_POINT_2F, D2D_RECT_F, D2D_SIZE_U},
D2D1_ELLIPSE, D2D1_ROUNDED_RECT,
};
#[test]
#[cfg(feature = "d2d")]
fn test_round_trip_point_f() {
let point = Point2D::<f32> {
x: 123.456,
y: 20_000.0,
};
let d2d_point: D2D_POINT_2F = point.into();
let point_round_trip: Point2D<f32> = d2d_point.into();
assert_eq!(point, point_round_trip);
assert_eq!(d2d_point.x, 123.456);
assert_eq!(d2d_point.y, 20_000.0);
}
#[test]
#[cfg(feature = "d2d")]
fn test_round_trip_size_u() {
let size = Size2D::<u32> {
width: 123,
height: 20_000,
};
let d2d_size: D2D_SIZE_U = size.into();
let size_round_trip: Size2D<u32> = d2d_size.into();
assert_eq!(size, size_round_trip);
assert_eq!(d2d_size.width, 123);
assert_eq!(d2d_size.height, 20_000);
}
#[test]
#[cfg(feature = "d2d")]
fn test_round_trip_d2d_rect_f() {
let rect = Rect2D::<f32> {
left: 123.456,
right: 20_000.0,
top: 0.0,
bottom: 0.5,
};
let d2d_rect: D2D_RECT_F = rect.into();
let rect_round_trip: Rect2D<f32> = d2d_rect.into();
assert_eq!(rect, rect_round_trip);
assert_eq!(d2d_rect.left, 123.456);
assert_eq!(d2d_rect.right, 20_000.0);
assert_eq!(d2d_rect.top, 0.0);
assert_eq!(d2d_rect.bottom, 0.5);
}
#[test]
#[cfg(feature = "d2d")]
fn test_round_trip_rounded_rect_f() {
let rect = RoundedRect2D::<f32> {
rect: Rect2D {
left: 123.456,
right: 20_000.0,
top: 0.0,
bottom: 0.5,
},
radius_x: 1.1,
radius_y: 2.2,
};
let d2d_rect: D2D1_ROUNDED_RECT = rect.into();
let rect_round_trip: RoundedRect2D<f32> = d2d_rect.into();
assert_eq!(rect, rect_round_trip);
assert_eq!(d2d_rect.rect.left, 123.456);
assert_eq!(d2d_rect.rect.right, 20_000.0);
assert_eq!(d2d_rect.rect.top, 0.0);
assert_eq!(d2d_rect.rect.bottom, 0.5);
assert_eq!(d2d_rect.radiusX, 1.1);
assert_eq!(d2d_rect.radiusY, 2.2);
}
#[test]
#[cfg(feature = "d2d")]
fn test_round_trip_d2d_ellipse_f() {
let ellipse = Ellipse2D::<f32> {
center: Point2D {
x: 123.456,
y: 20_000.0,
},
radius_x: 1.1,
radius_y: 2.2,
};
let d2d_ellipse: D2D1_ELLIPSE = ellipse.into();
let ellipse_round_trip: Ellipse2D<f32> = d2d_ellipse.into();
assert_eq!(ellipse, ellipse_round_trip);
assert_eq!(d2d_ellipse.point.x, 123.456);
assert_eq!(d2d_ellipse.point.y, 20_000.0);
assert_eq!(d2d_ellipse.radiusX, 1.1);
assert_eq!(d2d_ellipse.radiusY, 2.2);
}
#[test]
#[cfg(feature = "win32")]
fn test_round_trip_win32_rect() {
let rect = Rect2D::<i32> {
left: -123,
right: 20_000,
top: 0,
bottom: 456,
};
let win32_rect: RECT = rect.into();
let rect_round_trip: Rect2D<i32> = win32_rect.into();
assert_eq!(rect, rect_round_trip);
assert_eq!(win32_rect.left, -123);
assert_eq!(win32_rect.right, 20_000);
assert_eq!(win32_rect.top, 0);
assert_eq!(win32_rect.bottom, 456);
}
}