1 //! A 2D size. 2 3 use std::fmt; 4 use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}; 5 6 use crate::common::FloatExt; 7 use crate::{Rect, RoundedRect, Vec2}; 8 9 /// A 2D size. 10 #[derive(Clone, Copy, Default, PartialEq)] 11 pub struct Size { 12 /// The width. 13 pub width: f64, 14 /// The height. 15 pub height: f64, 16 } 17 18 impl Size { 19 /// A size with zero width or height. 20 pub const ZERO: Size = Size::new(0., 0.); 21 22 /// Create a new `Size` with the provided `width` and `height`. 23 #[inline] new(width: f64, height: f64) -> Self24 pub const fn new(width: f64, height: f64) -> Self { 25 Size { width, height } 26 } 27 28 /// Returns the max of `width` and `height`. 29 /// 30 /// # Examples 31 /// 32 /// ``` 33 /// use kurbo::Size; 34 /// let size = Size::new(-10.5, 42.0); 35 /// assert_eq!(size.max_side(), 42.0); 36 /// ``` max_side(self) -> f6437 pub fn max_side(self) -> f64 { 38 self.width.max(self.height) 39 } 40 41 /// Returns the min of `width` and `height`. 42 /// 43 /// # Examples 44 /// 45 /// ``` 46 /// use kurbo::Size; 47 /// let size = Size::new(-10.5, 42.0); 48 /// assert_eq!(size.min_side(), -10.5); 49 /// ``` min_side(self) -> f6450 pub fn min_side(self) -> f64 { 51 self.width.min(self.height) 52 } 53 54 /// Returns a new size bounded by `min` and `max.` 55 /// 56 /// # Examples 57 /// 58 /// ``` 59 /// use kurbo::Size; 60 /// 61 /// let this = Size::new(0., 100.); 62 /// let min = Size::new(10., 10.,); 63 /// let max = Size::new(50., 50.); 64 /// assert_eq!(this.clamp(min, max), Size::new(10., 50.)) 65 /// ``` clamp(self, min: Size, max: Size) -> Self66 pub fn clamp(self, min: Size, max: Size) -> Self { 67 let width = self.width.max(min.width).min(max.width); 68 let height = self.height.max(min.height).min(max.height); 69 Size { width, height } 70 } 71 72 /// Convert this size into a [`Vec2`], with `width` mapped to `x` and `height` 73 /// mapped to `y`. 74 /// 75 /// [`Vec2`]: struct.Vec2.html 76 #[inline] to_vec2(self) -> Vec277 pub const fn to_vec2(self) -> Vec2 { 78 Vec2::new(self.width, self.height) 79 } 80 81 /// Returns a new `Size`, 82 /// with `width` and `height` rounded to the nearest integer. 83 /// 84 /// # Examples 85 /// 86 /// ``` 87 /// use kurbo::Size; 88 /// let size_pos = Size::new(3.3, 3.6).round(); 89 /// assert_eq!(size_pos.width, 3.0); 90 /// assert_eq!(size_pos.height, 4.0); 91 /// let size_neg = Size::new(-3.3, -3.6).round(); 92 /// assert_eq!(size_neg.width, -3.0); 93 /// assert_eq!(size_neg.height, -4.0); 94 /// ``` 95 #[inline] round(self) -> Size96 pub fn round(self) -> Size { 97 Size::new(self.width.round(), self.height.round()) 98 } 99 100 /// Returns a new `Size`, 101 /// with `width` and `height` rounded up to the nearest integer, 102 /// unless they are already an integer. 103 /// 104 /// # Examples 105 /// 106 /// ``` 107 /// use kurbo::Size; 108 /// let size_pos = Size::new(3.3, 3.6).ceil(); 109 /// assert_eq!(size_pos.width, 4.0); 110 /// assert_eq!(size_pos.height, 4.0); 111 /// let size_neg = Size::new(-3.3, -3.6).ceil(); 112 /// assert_eq!(size_neg.width, -3.0); 113 /// assert_eq!(size_neg.height, -3.0); 114 /// ``` 115 #[inline] ceil(self) -> Size116 pub fn ceil(self) -> Size { 117 Size::new(self.width.ceil(), self.height.ceil()) 118 } 119 120 /// Returns a new `Size`, 121 /// with `width` and `height` rounded down to the nearest integer, 122 /// unless they are already an integer. 123 /// 124 /// # Examples 125 /// 126 /// ``` 127 /// use kurbo::Size; 128 /// let size_pos = Size::new(3.3, 3.6).floor(); 129 /// assert_eq!(size_pos.width, 3.0); 130 /// assert_eq!(size_pos.height, 3.0); 131 /// let size_neg = Size::new(-3.3, -3.6).floor(); 132 /// assert_eq!(size_neg.width, -4.0); 133 /// assert_eq!(size_neg.height, -4.0); 134 /// ``` 135 #[inline] floor(self) -> Size136 pub fn floor(self) -> Size { 137 Size::new(self.width.floor(), self.height.floor()) 138 } 139 140 /// Returns a new `Size`, 141 /// with `width` and `height` rounded away from zero to the nearest integer, 142 /// unless they are already an integer. 143 /// 144 /// # Examples 145 /// 146 /// ``` 147 /// use kurbo::Size; 148 /// let size_pos = Size::new(3.3, 3.6).expand(); 149 /// assert_eq!(size_pos.width, 4.0); 150 /// assert_eq!(size_pos.height, 4.0); 151 /// let size_neg = Size::new(-3.3, -3.6).expand(); 152 /// assert_eq!(size_neg.width, -4.0); 153 /// assert_eq!(size_neg.height, -4.0); 154 /// ``` 155 #[inline] expand(self) -> Size156 pub fn expand(self) -> Size { 157 Size::new(self.width.expand(), self.height.expand()) 158 } 159 160 /// Returns a new `Size`, 161 /// with `width` and `height` rounded down towards zero the nearest integer, 162 /// unless they are already an integer. 163 /// 164 /// # Examples 165 /// 166 /// ``` 167 /// use kurbo::Size; 168 /// let size_pos = Size::new(3.3, 3.6).trunc(); 169 /// assert_eq!(size_pos.width, 3.0); 170 /// assert_eq!(size_pos.height, 3.0); 171 /// let size_neg = Size::new(-3.3, -3.6).trunc(); 172 /// assert_eq!(size_neg.width, -3.0); 173 /// assert_eq!(size_neg.height, -3.0); 174 /// ``` 175 #[inline] trunc(self) -> Size176 pub fn trunc(self) -> Size { 177 Size::new(self.width.trunc(), self.height.trunc()) 178 } 179 180 /// Convert this `Size` into a [`Rect`] with origin `(0.0, 0.0)`. 181 /// 182 /// [`Rect`]: struct.Rect.html 183 #[inline] to_rect(self) -> Rect184 pub const fn to_rect(self) -> Rect { 185 Rect::new(0., 0., self.width, self.height) 186 } 187 188 /// Convert this `Size` into a [`RoundedRect`] with origin `(0.0, 0.0)` and 189 /// the provided corner radius. 190 /// 191 /// [`RoundedRect`]: struct.RoundedRect.html 192 #[inline] to_rounded_rect(self, radius: f64) -> RoundedRect193 pub fn to_rounded_rect(self, radius: f64) -> RoundedRect { 194 self.to_rect().to_rounded_rect(radius) 195 } 196 } 197 198 impl fmt::Debug for Size { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result199 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 200 write!(f, "{:?}W×{:?}H", self.width, self.height) 201 } 202 } 203 204 impl fmt::Display for Size { fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result205 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 206 write!(formatter, "(")?; 207 fmt::Display::fmt(&self.width, formatter)?; 208 write!(formatter, "×")?; 209 fmt::Display::fmt(&self.height, formatter)?; 210 write!(formatter, ")") 211 } 212 } 213 214 impl MulAssign<f64> for Size { 215 #[inline] mul_assign(&mut self, other: f64)216 fn mul_assign(&mut self, other: f64) { 217 *self = Size { 218 width: self.width * other, 219 height: self.height * other, 220 }; 221 } 222 } 223 224 impl Mul<Size> for f64 { 225 type Output = Size; 226 227 #[inline] mul(self, other: Size) -> Size228 fn mul(self, other: Size) -> Size { 229 other * self 230 } 231 } 232 233 impl Mul<f64> for Size { 234 type Output = Size; 235 236 #[inline] mul(self, other: f64) -> Size237 fn mul(self, other: f64) -> Size { 238 Size { 239 width: self.width * other, 240 height: self.height * other, 241 } 242 } 243 } 244 245 impl Add<Size> for Size { 246 type Output = Size; 247 #[inline] add(self, other: Size) -> Size248 fn add(self, other: Size) -> Size { 249 Size { 250 width: self.width + other.width, 251 height: self.height + other.height, 252 } 253 } 254 } 255 256 impl AddAssign<Size> for Size { 257 #[inline] add_assign(&mut self, other: Size)258 fn add_assign(&mut self, other: Size) { 259 *self = *self + other; 260 } 261 } 262 263 impl Sub<Size> for Size { 264 type Output = Size; 265 #[inline] sub(self, other: Size) -> Size266 fn sub(self, other: Size) -> Size { 267 Size { 268 width: self.width - other.width, 269 height: self.height - other.height, 270 } 271 } 272 } 273 274 impl SubAssign<Size> for Size { 275 #[inline] sub_assign(&mut self, other: Size)276 fn sub_assign(&mut self, other: Size) { 277 *self = *self - other; 278 } 279 } 280 281 impl From<(f64, f64)> for Size { 282 #[inline] from(v: (f64, f64)) -> Size283 fn from(v: (f64, f64)) -> Size { 284 Size { 285 width: v.0, 286 height: v.1, 287 } 288 } 289 } 290 291 impl From<Size> for (f64, f64) { 292 #[inline] from(v: Size) -> (f64, f64)293 fn from(v: Size) -> (f64, f64) { 294 (v.width, v.height) 295 } 296 } 297 298 //FIXME: remove for 0.6.0 https://github.com/linebender/kurbo/issues/95 299 impl Into<Size> for Vec2 { 300 #[inline] into(self) -> Size301 fn into(self) -> Size { 302 self.to_size() 303 } 304 } 305 306 #[cfg(test)] 307 mod tests { 308 use super::*; 309 310 #[test] display()311 fn display() { 312 let s = Size::new(-0.12345, 9.87654); 313 assert_eq!(format!("{}", s), "(-0.12345×9.87654)"); 314 315 let s = Size::new(-0.12345, 9.87654); 316 assert_eq!(format!("{:+6.2}", s), "( -0.12× +9.88)"); 317 } 318 } 319