use crate::CornerRadius; /// How rounded the corners of things should be, in `f32`. /// /// This is used for calculations, but storage is usually done with the more compact [`CornerRadius`]. #[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct CornerRadiusF32 { /// Radius of the rounding of the North-West (left top) corner. pub nw: f32, /// Radius of the rounding of the North-East (right top) corner. pub ne: f32, /// Radius of the rounding of the South-West (left bottom) corner. pub sw: f32, /// Radius of the rounding of the South-East (right bottom) corner. pub se: f32, } impl From for CornerRadiusF32 { #[inline] fn from(cr: CornerRadius) -> Self { Self { nw: cr.nw as f32, ne: cr.ne as f32, sw: cr.sw as f32, se: cr.se as f32, } } } impl From for CornerRadius { #[inline] fn from(cr: CornerRadiusF32) -> Self { Self { nw: cr.nw.round() as u8, ne: cr.ne.round() as u8, sw: cr.sw.round() as u8, se: cr.se.round() as u8, } } } impl Default for CornerRadiusF32 { #[inline] fn default() -> Self { Self::ZERO } } impl From for CornerRadiusF32 { #[inline] fn from(radius: f32) -> Self { Self { nw: radius, ne: radius, sw: radius, se: radius, } } } impl CornerRadiusF32 { /// No rounding on any corner. pub const ZERO: Self = Self { nw: 0.0, ne: 0.0, sw: 0.0, se: 0.0, }; /// Same rounding on all four corners. #[inline] #[must_use] pub const fn same(radius: f32) -> Self { Self { nw: radius, ne: radius, sw: radius, se: radius, } } /// Do all corners have the same rounding? #[inline] pub fn is_same(&self) -> bool { self.nw == self.ne && self.nw == self.sw && self.nw == self.se } /// Make sure each corner has a rounding of at least this. #[inline] #[must_use] pub fn at_least(&self, min: f32) -> Self { Self { nw: self.nw.max(min), ne: self.ne.max(min), sw: self.sw.max(min), se: self.se.max(min), } } /// Make sure each corner has a rounding of at most this. #[inline] #[must_use] pub fn at_most(&self, max: f32) -> Self { Self { nw: self.nw.min(max), ne: self.ne.min(max), sw: self.sw.min(max), se: self.se.min(max), } } /// Set the rounding of the two east (right) corners. #[inline] #[must_use] pub fn with_east(self, radius: f32) -> Self { Self { ne: radius, se: radius, ..self } } /// Set the rounding of the two north (top) corners. #[inline] #[must_use] pub fn with_north(self, radius: f32) -> Self { Self { nw: radius, ne: radius, ..self } } /// Set the rounding of the two south (bottom) corners. #[inline] #[must_use] pub fn with_south(self, radius: f32) -> Self { Self { sw: radius, se: radius, ..self } } /// Set the rounding of the two west (left) corners. #[inline] #[must_use] pub fn with_west(self, radius: f32) -> Self { Self { nw: radius, sw: radius, ..self } } } impl core::ops::Add for CornerRadiusF32 { type Output = Self; /// Saturates at zero: no corner radius will go negative. #[inline] fn add(self, rhs: Self) -> Self { Self { nw: (self.nw + rhs.nw).max(0.0), ne: (self.ne + rhs.ne).max(0.0), sw: (self.sw + rhs.sw).max(0.0), se: (self.se + rhs.se).max(0.0), } } } impl core::ops::AddAssign for CornerRadiusF32 { /// Saturates at zero: no corner radius will go negative. #[inline] fn add_assign(&mut self, rhs: Self) { *self = *self + rhs; } } impl core::ops::AddAssign for CornerRadiusF32 { /// Saturates at zero: no corner radius will go negative. #[inline] fn add_assign(&mut self, rhs: f32) { *self = *self + Self::same(rhs); } } impl core::ops::Sub for CornerRadiusF32 { type Output = Self; /// Saturates at zero: no corner radius will go negative. #[inline] fn sub(self, rhs: Self) -> Self { Self { nw: (self.nw - rhs.nw).max(0.0), ne: (self.ne - rhs.ne).max(0.0), sw: (self.sw - rhs.sw).max(0.0), se: (self.se - rhs.se).max(0.0), } } } impl core::ops::SubAssign for CornerRadiusF32 { /// Saturates at zero: no corner radius will go negative. #[inline] fn sub_assign(&mut self, rhs: Self) { *self = *self - rhs; } } impl core::ops::SubAssign for CornerRadiusF32 { /// Saturates at zero: no corner radius will go negative. #[inline] fn sub_assign(&mut self, rhs: f32) { *self = *self - Self::same(rhs); } } impl core::ops::Div for CornerRadiusF32 { type Output = Self; #[inline] fn div(self, rhs: f32) -> Self { Self { nw: self.nw / rhs, ne: self.ne / rhs, sw: self.sw / rhs, se: self.se / rhs, } } } impl core::ops::DivAssign for CornerRadiusF32 { #[inline] fn div_assign(&mut self, rhs: f32) { *self = Self { nw: self.nw / rhs, ne: self.ne / rhs, sw: self.sw / rhs, se: self.se / rhs, }; } } impl core::ops::Mul for CornerRadiusF32 { type Output = Self; #[inline] fn mul(self, rhs: f32) -> Self { Self { nw: self.nw * rhs, ne: self.ne * rhs, sw: self.sw * rhs, se: self.se * rhs, } } } impl core::ops::MulAssign for CornerRadiusF32 { #[inline] fn mul_assign(&mut self, rhs: f32) { *self = Self { nw: self.nw * rhs, ne: self.ne * rhs, sw: self.sw * rhs, se: self.se * rhs, }; } } #[cfg(test)] mod tests { use super::CornerRadiusF32; #[test] fn add_and_sub_saturate_at_zero() { assert_eq!( CornerRadiusF32::same(2.0) + CornerRadiusF32::same(-5.0), CornerRadiusF32::ZERO ); assert_eq!( CornerRadiusF32::same(2.0) - CornerRadiusF32::same(5.0), CornerRadiusF32::ZERO ); let mut cr = CornerRadiusF32::same(1.0); cr += -3.0; assert_eq!(cr, CornerRadiusF32::ZERO); cr -= -2.0; assert_eq!(cr, CornerRadiusF32::same(2.0)); } }