From 3c7bbf65c4cc7c09edb244284659b7caca4fc447 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sat, 27 Nov 2021 20:51:32 +0100 Subject: [PATCH 1/9] utils: Add Rectangle::to_i32_* variants --- CHANGELOG.md | 4 ++++ src/utils/geometry.rs | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e66d0da..21982a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,10 @@ - The button code for a `PointerButtonEvent` may now be obtained using `PointerButtonEvent::button_code`. - `Renderer` now allows texture filtering methods to be set. +#### Utils + +- `Rectangle` can now also be converted from f64 to i32 variants + ### Bugfixes #### Clients & Protocols diff --git a/src/utils/geometry.rs b/src/utils/geometry.rs index 80b6823..3bb5823 100644 --- a/src/utils/geometry.rs +++ b/src/utils/geometry.rs @@ -725,6 +725,29 @@ impl Rectangle { } } +impl Rectangle { + /// Convert to i32 for integer-space manipulations by rounding float values + #[inline] + pub fn to_i32_round(self) -> Rectangle { + Rectangle { + loc: self.loc.to_i32_round(), + size: self.size.to_i32_round(), + } + } + + /// Convert to i32 by returning the largest integer-space rectangle fitting into the float-based rectangle + #[inline] + pub fn to_i32_down(self) -> Rectangle { + Rectangle::from_extemities(self.loc.to_i32_ceil(), (self.loc + self.size).to_i32_floor()) + } + + /// Convert to i32 by returning the smallest integet-space rectangle encapsulating the float-based rectangle + #[inline] + pub fn to_i32_up(self) -> Rectangle { + Rectangle::from_extemities(self.loc.to_i32_floor(), (self.loc + self.size).to_i32_ceil()) + } +} + impl Rectangle { /// Create a new [`Rectangle`] from the coordinates of its top-left corner and its dimensions #[inline] From 37adc1174c81ac69fa4b8a745b4d6182f60932eb Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sat, 27 Nov 2021 20:53:26 +0100 Subject: [PATCH 2/9] utils: Add Rectangle::contains_rect --- CHANGELOG.md | 1 + src/utils/geometry.rs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21982a9..3fab971 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,7 @@ #### Utils - `Rectangle` can now also be converted from f64 to i32 variants +- `Rectangle::contains_rect` can be used to check if a rectangle is contained within another ### Bugfixes diff --git a/src/utils/geometry.rs b/src/utils/geometry.rs index 3bb5823..8f3e97f 100644 --- a/src/utils/geometry.rs +++ b/src/utils/geometry.rs @@ -782,6 +782,13 @@ impl Rectangle { && (p.y < self.loc.y + self.size.h) } + /// Checks whether given [`Rectangle`] is inside the rectangle + #[inline] + pub fn contains_rect>>(self, rect: R) -> bool { + let r: Rectangle = rect.into(); + self.contains(r.loc) && self.contains(r.loc + r.size) + } + /// Checks whether a given [`Rectangle`] overlaps with this one #[inline] pub fn overlaps(self, other: Rectangle) -> bool { From 95b915d4291079601ccca81f3638a527b100e384 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sat, 27 Nov 2021 21:10:55 +0100 Subject: [PATCH 3/9] utils: Make Coordinate public --- CHANGELOG.md | 1 + src/utils/geometry.rs | 7 +++++++ src/utils/mod.rs | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fab971..0f7a992 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ - `Rectangle` can now also be converted from f64 to i32 variants - `Rectangle::contains_rect` can be used to check if a rectangle is contained within another +- `Coordinate` is now part of the public api, so it can be used for coordinate agnositic functions outside of the utils module or even out-of-tree ### Bugfixes diff --git a/src/utils/geometry.rs b/src/utils/geometry.rs index 8f3e97f..dd2f796 100644 --- a/src/utils/geometry.rs +++ b/src/utils/geometry.rs @@ -17,14 +17,21 @@ pub struct Buffer; #[derive(Debug)] pub struct Raw; +/// Trait for types serving as a coordinate for other geometry utils pub trait Coordinate: Sized + Add + Sub + PartialOrd + Default + Copy + fmt::Debug { + /// Downscale the coordinate fn downscale(self, scale: Self) -> Self; + /// Upscale the coordinate fn upscale(self, scale: Self) -> Self; + /// Convert the coordinate to a f64 fn to_f64(self) -> f64; + /// Convert to this coordinate from a f64 fn from_f64(v: f64) -> Self; + /// Test if the coordinate is not negative fn non_negative(self) -> bool; + /// Returns the absolute value of this coordinate fn abs(self) -> Self; } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 1732236..9b8edd3 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -8,7 +8,7 @@ pub mod x11rb; pub mod user_data; -pub use self::geometry::{Buffer, Logical, Physical, Point, Raw, Rectangle, Size}; +pub use self::geometry::{Buffer, Coordinate, Logical, Physical, Point, Raw, Rectangle, Size}; /// This resource is not managed by Smithay #[derive(Debug)] From 2f683112a7416eb8c6b67952eb2e874020065ce6 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Thu, 16 Dec 2021 16:53:16 +0100 Subject: [PATCH 4/9] utils: Add min/max to Coordinate --- src/utils/geometry.rs | 55 +++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/src/utils/geometry.rs b/src/utils/geometry.rs index dd2f796..3b647a3 100644 --- a/src/utils/geometry.rs +++ b/src/utils/geometry.rs @@ -29,6 +29,22 @@ pub trait Coordinate: fn to_f64(self) -> f64; /// Convert to this coordinate from a f64 fn from_f64(v: f64) -> Self; + /// Compare and return the smaller one + fn min(self, other: Self) -> Self { + if self < other { + self + } else { + other + } + } + /// Compare and return the larger one + fn max(self, other: Self) -> Self { + if self > other { + self + } else { + other + } + } /// Test if the coordinate is not negative fn non_negative(self) -> bool; /// Returns the absolute value of this coordinate @@ -815,39 +831,12 @@ impl Rectangle { /// Compute the bounding box of a given set of points pub fn bounding_box(points: impl IntoIterator>) -> Self { - let ret = points.into_iter().fold(None, |acc, point| { - match acc { - None => Some((point, point)), - // we don't have cmp::{min,max} for f64 :( - Some((min_point, max_point)) => Some(( - ( - if min_point.x > point.x { - point.x - } else { - min_point.x - }, - if min_point.y > point.y { - point.y - } else { - min_point.y - }, - ) - .into(), - ( - if max_point.x < point.x { - point.x - } else { - max_point.x - }, - if max_point.y < point.y { - point.y - } else { - max_point.y - }, - ) - .into(), - )), - } + let ret = points.into_iter().fold(None, |acc, point| match acc { + None => Some((point, point)), + Some((min_point, max_point)) => Some(( + (point.x.min(min_point.x), point.y.min(min_point.y)).into(), + (point.x.max(max_point.x), point.y.max(max_point.y)).into(), + )), }); match ret { From ee5c14e67362b88c2acd0f87813dc3e9f3807a7c Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Thu, 16 Dec 2021 16:54:05 +0100 Subject: [PATCH 5/9] utils: use stable memory layout for Point/Size/Rectangle --- src/utils/geometry.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/utils/geometry.rs b/src/utils/geometry.rs index 3b647a3..d96bb7e 100644 --- a/src/utils/geometry.rs +++ b/src/utils/geometry.rs @@ -208,6 +208,7 @@ floating_point_coordinate_impl! { */ /// A point as defined by its x and y coordinates +#[repr(C)] pub struct Point { /// horizontal coordinate pub x: N, @@ -471,6 +472,7 @@ impl Default for Point { /// Constructors of this type ensure that the values are always positive via /// `debug_assert!()`, however manually changing the values of the fields /// can break this invariant. +#[repr(C)] pub struct Size { /// horizontal coordinate pub w: N, @@ -731,6 +733,7 @@ impl, Kind> Sub> for Point { } /// A rectangle defined by its top-left corner and dimensions +#[repr(C)] pub struct Rectangle { /// Location of the top-left corner of the rectangle pub loc: Point, From 1a7027eefc14d4bb50c58fddfd4d48de6deac580 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Thu, 16 Dec 2021 16:54:32 +0100 Subject: [PATCH 6/9] utils: overlap support on-the-fly rect conversion --- src/utils/geometry.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/geometry.rs b/src/utils/geometry.rs index d96bb7e..603007d 100644 --- a/src/utils/geometry.rs +++ b/src/utils/geometry.rs @@ -817,7 +817,8 @@ impl Rectangle { /// Checks whether a given [`Rectangle`] overlaps with this one #[inline] - pub fn overlaps(self, other: Rectangle) -> bool { + pub fn overlaps(self, other: impl Into>) -> bool { + let other = other.into(); // if the rectangle is not outside of the other // they must overlap !( From 4628fc6bcc5a9e80a1b9f3f25852726981ff84e1 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Thu, 16 Dec 2021 16:55:22 +0100 Subject: [PATCH 7/9] utils: Add intersection to Rectangle --- src/utils/geometry.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/utils/geometry.rs b/src/utils/geometry.rs index 603007d..d031d02 100644 --- a/src/utils/geometry.rs +++ b/src/utils/geometry.rs @@ -833,6 +833,19 @@ impl Rectangle { ) } + /// Clamp rectangle to min and max corners resulting in the overlapping area of two rectangles + #[inline] + pub fn intersection(self, other: impl Into>) -> Self { + let other = other.into(); + Rectangle::from_extemities( + (self.loc.x.max(other.loc.x), self.loc.y.max(other.loc.y)), + ( + (self.loc.x + self.size.w).min(other.loc.x + other.size.w), + (self.loc.y + self.size.h).min(other.loc.y + other.size.h), + ), + ) + } + /// Compute the bounding box of a given set of points pub fn bounding_box(points: impl IntoIterator>) -> Self { let ret = points.into_iter().fold(None, |acc, point| match acc { From 7ad5edd6a3b64a303c315522af912b77856fe923 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Thu, 16 Dec 2021 16:56:53 +0100 Subject: [PATCH 8/9] utils: make all geometry operations saturating --- src/utils/geometry.rs | 122 ++++++++++++++++++++++++++++++------------ 1 file changed, 87 insertions(+), 35 deletions(-) diff --git a/src/utils/geometry.rs b/src/utils/geometry.rs index d031d02..fb22b72 100644 --- a/src/utils/geometry.rs +++ b/src/utils/geometry.rs @@ -49,6 +49,13 @@ pub trait Coordinate: fn non_negative(self) -> bool; /// Returns the absolute value of this coordinate fn abs(self) -> Self; + + /// Saturating integer addition. Computes self + other, saturating at the numeric bounds instead of overflowing. + fn saturating_add(self, other: Self) -> Self; + /// Saturating integer subtraction. Computes self - other, saturating at the numeric bounds instead of overflowing. + fn saturating_sub(self, other: Self) -> Self; + /// Saturating integer multiplication. Computes self * other, saturating at the numeric bounds instead of overflowing. + fn saturating_mul(self, other: Self) -> Self; } /// Implements Coordinate for an unsigned numerical type. @@ -91,6 +98,19 @@ macro_rules! unsigned_coordinate_impl { fn abs(self) -> Self { self } + + #[inline] + fn saturating_add(self, other: Self) -> Self { + self.saturating_add(other) + } + #[inline] + fn saturating_sub(self, other: Self) -> Self { + self.saturating_sub(other) + } + #[inline] + fn saturating_mul(self, other: Self) -> Self { + self.saturating_mul(other) + } } }; } @@ -143,6 +163,19 @@ macro_rules! signed_coordinate_impl { fn abs(self) -> Self { self.abs() } + + #[inline] + fn saturating_add(self, other: Self) -> Self { + self.saturating_add(other) + } + #[inline] + fn saturating_sub(self, other: Self) -> Self { + self.saturating_sub(other) + } + #[inline] + fn saturating_mul(self, other: Self) -> Self { + self.saturating_mul(other) + } } }; } @@ -194,6 +227,19 @@ macro_rules! floating_point_coordinate_impl { fn abs(self) -> Self { self.abs() } + + #[inline] + fn saturating_add(self, other: Self) -> Self { + self + other + } + #[inline] + fn saturating_sub(self, other: Self) -> Self { + self - other + } + #[inline] + fn saturating_mul(self, other: Self) -> Self { + self * other + } } }; } @@ -208,6 +254,8 @@ floating_point_coordinate_impl! { */ /// A point as defined by its x and y coordinates +/// +/// Operations on points are saturating. #[repr(C)] pub struct Point { /// horizontal coordinate @@ -392,41 +440,41 @@ impl From> for (N, N) { } } -impl, Kind> Add for Point { +impl Add for Point { type Output = Point; #[inline] fn add(self, other: Point) -> Point { Point { - x: self.x + other.x, - y: self.y + other.y, + x: self.x.saturating_add(other.x), + y: self.y.saturating_add(other.y), _kind: std::marker::PhantomData, } } } -impl AddAssign for Point { +impl AddAssign for Point { #[inline] fn add_assign(&mut self, rhs: Self) { - self.x += rhs.x; - self.y += rhs.y + self.x = self.x.saturating_add(rhs.x); + self.y = self.y.saturating_add(rhs.y); } } -impl SubAssign for Point { +impl SubAssign for Point { #[inline] fn sub_assign(&mut self, rhs: Self) { - self.x -= rhs.x; - self.y -= rhs.y + self.x = self.x.saturating_sub(rhs.x); + self.y = self.y.saturating_sub(rhs.y); } } -impl, Kind> Sub for Point { +impl Sub for Point { type Output = Point; #[inline] fn sub(self, other: Point) -> Point { Point { - x: self.x - other.x, - y: self.y - other.y, + x: self.x.saturating_sub(other.x), + y: self.y.saturating_sub(other.y), _kind: std::marker::PhantomData, } } @@ -472,6 +520,8 @@ impl Default for Point { /// Constructors of this type ensure that the values are always positive via /// `debug_assert!()`, however manually changing the values of the fields /// can break this invariant. +/// +/// Operations on sizes are saturating. #[repr(C)] pub struct Size { /// horizontal coordinate @@ -642,27 +692,27 @@ impl From> for (N, N) { } } -impl, Kind> Add for Size { +impl Add for Size { type Output = Size; #[inline] fn add(self, other: Size) -> Size { Size { - w: self.w + other.w, - h: self.h + other.h, + w: self.w.saturating_add(other.w), + h: self.h.saturating_add(other.h), _kind: std::marker::PhantomData, } } } -impl AddAssign for Size { +impl AddAssign for Size { #[inline] fn add_assign(&mut self, rhs: Self) { - self.w += rhs.w; - self.h += rhs.h + self.w = self.w.saturating_add(rhs.w); + self.h = self.h.saturating_add(rhs.h); } } -impl SubAssign for Size { +impl SubAssign for Size { #[inline] fn sub_assign(&mut self, rhs: Self) { debug_assert!( @@ -672,8 +722,8 @@ impl SubAssign for Size { (&rhs.w, &rhs.h), ); - self.w -= rhs.w; - self.h -= rhs.h + self.w = self.w.saturating_sub(rhs.w); + self.h = self.h.saturating_sub(rhs.h); } } @@ -708,31 +758,33 @@ impl Default for Size { } } -impl, Kind> Add> for Point { +impl Add> for Point { type Output = Point; #[inline] fn add(self, other: Size) -> Point { Point { - x: self.x + other.w, - y: self.y + other.h, + x: self.x.saturating_add(other.w), + y: self.y.saturating_add(other.h), _kind: std::marker::PhantomData, } } } -impl, Kind> Sub> for Point { +impl Sub> for Point { type Output = Point; #[inline] fn sub(self, other: Size) -> Point { Point { - x: self.x - other.w, - y: self.y - other.h, + x: self.x.saturating_sub(other.w), + y: self.y.saturating_sub(other.h), _kind: std::marker::PhantomData, } } } /// A rectangle defined by its top-left corner and dimensions +/// +/// Operations on retangles are saturating. #[repr(C)] pub struct Rectangle { /// Location of the top-left corner of the rectangle @@ -803,9 +855,9 @@ impl Rectangle { pub fn contains>>(self, point: P) -> bool { let p: Point = point.into(); (p.x >= self.loc.x) - && (p.x < self.loc.x + self.size.w) + && (p.x < self.loc.x.saturating_add(self.size.w)) && (p.y >= self.loc.y) - && (p.y < self.loc.y + self.size.h) + && (p.y < self.loc.y.saturating_add(self.size.h)) } /// Checks whether given [`Rectangle`] is inside the rectangle @@ -823,13 +875,13 @@ impl Rectangle { // they must overlap !( // self is left of other - self.loc.x + self.size.w < other.loc.x + self.loc.x.saturating_add(self.size.w) < other.loc.x // self is right of other - || self.loc.x > other.loc.x + other.size.w + || self.loc.x > other.loc.x.saturating_add(other.size.w) // self is above of other - || self.loc.y + self.size.h < other.loc.y + || self.loc.y.saturating_add(self.size.h) < other.loc.y // self is below of other - || self.loc.y > other.loc.y + other.size.h + || self.loc.y > other.loc.y.saturating_add(other.size.h) ) } @@ -840,8 +892,8 @@ impl Rectangle { Rectangle::from_extemities( (self.loc.x.max(other.loc.x), self.loc.y.max(other.loc.y)), ( - (self.loc.x + self.size.w).min(other.loc.x + other.size.w), - (self.loc.y + self.size.h).min(other.loc.y + other.size.h), + (self.loc.x.saturating_add(self.size.w)).min(other.loc.x.saturating_add(other.size.w)), + (self.loc.y.saturating_add(self.size.h)).min(other.loc.y.saturating_add(other.size.h)), ), ) } From 8022a60089ecca716206145a50a0a1651545503d Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Thu, 16 Dec 2021 16:57:07 +0100 Subject: [PATCH 9/9] utils: documentation fixup --- src/utils/geometry.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/geometry.rs b/src/utils/geometry.rs index fb22b72..0018de6 100644 --- a/src/utils/geometry.rs +++ b/src/utils/geometry.rs @@ -836,7 +836,7 @@ impl Rectangle { } } - /// Create a new [`Rectangle`] from the coordinates of its top-left corner and its dimensions + /// Create a new [`Rectangle`] from the coordinates of its top-left corner and its bottom-right corner #[inline] pub fn from_extemities( topleft: impl Into>,