From d94b067e0cc6d2a482eaa3e8edda7a089bef75f3 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 25 Oct 2024 15:48:05 +0200 Subject: [PATCH] dpi: Add Rect, PhysicalRect and LogicalRect --- dpi/CHANGELOG.md | 2 + dpi/src/lib.rs | 114 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/dpi/CHANGELOG.md b/dpi/CHANGELOG.md index 0c4e4544f3..352922ea6e 100644 --- a/dpi/CHANGELOG.md +++ b/dpi/CHANGELOG.md @@ -11,6 +11,8 @@ Unreleased` header. ## Unreleased +- Add `Rect`, `PhysicalRect` and `LogicalRect`. + ## 0.1.1 - Derive `Debug`, `Copy`, `Clone`, `PartialEq`, `Serialize`, `Deserialize` traits for `PixelUnit`. diff --git a/dpi/src/lib.rs b/dpi/src/lib.rs index 04b7df00bc..ea3ca46402 100644 --- a/dpi/src/lib.rs +++ b/dpi/src/lib.rs @@ -759,6 +759,120 @@ impl From> for Position { } } +/// A rectangle represented in logical pixels. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct LogicalRect

{ + pub origin: LogicalPosition

, + pub size: LogicalSize

, +} + +impl

LogicalRect

{ + #[inline] + pub const fn new(origin: LogicalPosition

, size: LogicalSize

) -> Self { + Self { origin, size } + } +} + +impl LogicalRect

{ + #[inline] + pub fn from_physical>, X: Pixel>( + physical: T, + scale_factor: f64, + ) -> Self { + physical.into().to_logical(scale_factor) + } + + #[inline] + pub fn to_physical(&self, scale_factor: f64) -> PhysicalRect { + let origin = self.origin.to_physical(scale_factor); + let size = self.size.to_physical(scale_factor); + PhysicalRect::new(origin, size) + } + + #[inline] + pub fn cast(&self) -> LogicalRect { + LogicalRect { origin: self.origin.cast(), size: self.size.cast() } + } +} + +/// A rectangle represented in physical pixels. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct PhysicalRect

{ + pub origin: PhysicalPosition

, + pub size: PhysicalSize

, +} + +impl

PhysicalRect

{ + #[inline] + pub const fn new(origin: PhysicalPosition

, size: PhysicalSize

) -> Self { + Self { origin, size } + } +} + +impl PhysicalRect

{ + #[inline] + pub fn from_logical>, X: Pixel>(logical: T, scale_factor: f64) -> Self { + logical.into().to_physical(scale_factor) + } + + #[inline] + pub fn to_logical(&self, scale_factor: f64) -> LogicalRect { + assert!(validate_scale_factor(scale_factor)); + let origin = self.origin.to_logical(scale_factor); + let size = self.size.to_logical(scale_factor); + LogicalRect::new(origin, size) + } + + #[inline] + pub fn cast(&self) -> PhysicalRect { + PhysicalRect { origin: self.origin.cast(), size: self.size.cast() } + } +} + +/// A rectangle that's either physical or logical. +#[derive(Debug, Copy, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum Rect { + Physical(PhysicalRect), + Logical(LogicalRect), +} + +impl Rect { + pub fn new>(rect: R) -> Self { + rect.into() + } + + pub fn to_logical(&self, scale_factor: f64) -> LogicalRect

{ + match *self { + Self::Physical(rect) => rect.to_logical(scale_factor), + Self::Logical(rect) => rect.cast(), + } + } + + pub fn to_physical(&self, scale_factor: f64) -> PhysicalRect

{ + match *self { + Self::Physical(rect) => rect.cast(), + Self::Logical(rect) => rect.to_physical(scale_factor), + } + } +} + +impl From> for Rect { + #[inline] + fn from(rect: PhysicalRect

) -> Self { + Self::Physical(rect.cast()) + } +} + +impl From> for Rect { + #[inline] + fn from(rect: LogicalRect

) -> Self { + Self::Logical(rect.cast()) + } +} + #[cfg(test)] mod tests { use std::collections::HashSet;