embedded_graphics_core/geometry/
size.rs

1use core::{
2    fmt,
3    ops::{Add, AddAssign, Div, DivAssign, Index, Mul, MulAssign, Sub, SubAssign},
4};
5
6use crate::geometry::Point;
7
8/// 2D size.
9///
10/// `Size` is used to define the width and height of an object.
11///
12/// [Nalgebra] support can be enabled with the `nalgebra_support` feature. This implements
13/// `From<Vector2<N>>` and `From<&Vector2<N>>` where `N` is `Scalar + Into<u32>`. This allows use
14/// of Nalgebra's [`Vector2`] with embedded-graphics where `u32`, `u16` or `u8` is used for value
15/// storage.
16///
17/// # Examples
18///
19/// ## Create a `Size` from two integers
20///
21///
22/// ```rust
23/// use embedded_graphics::geometry::Size;
24///
25/// // Create a size using the `new` constructor method
26/// let s = Size::new(10, 20);
27/// ```
28///
29/// ## Create a `Size` from a Nalgebra `Vector2`
30///
31/// _Be sure to enable the `nalgebra_support` feature to get [Nalgebra] integration._
32///
33/// Any `Vector2<N>` can be used where `N: Into<u32> + nalgebra::Scalar`. This includes the primitive types `u32`, `u16` and `u8`.
34///
35/// ```rust
36/// # #[cfg(feature = "nalgebra_support")] {
37/// use embedded_graphics::geometry::Size;
38/// use nalgebra::Vector2;
39///
40/// assert_eq!(Size::from(Vector2::new(10u32, 20)), Size::new(10u32, 20));
41/// assert_eq!(Size::from(Vector2::new(10u16, 20)), Size::new(10u32, 20));
42/// assert_eq!(Size::from(Vector2::new(10u8, 20)), Size::new(10u32, 20));
43/// # }
44/// ```
45///
46/// `.into()` can also be used, but may require more type annotations:
47///
48/// ```rust
49/// # #[cfg(feature = "nalgebra_support")] {
50/// use embedded_graphics::geometry::Size;
51/// use nalgebra::Vector2;
52///
53/// let c: Size = Vector2::new(10u32, 20).into();
54///
55/// assert_eq!(c, Size::new(10u32, 20));
56/// # }
57/// ```
58///
59/// [`Drawable`]: super::drawable::Drawable
60/// [`Vector2<N>`]: https://docs.rs/nalgebra/0.18.0/nalgebra/base/type.Vector2.html
61/// [`Vector2`]: https://docs.rs/nalgebra/0.18.0/nalgebra/base/type.Vector2.html
62/// [Nalgebra]: https://docs.rs/nalgebra
63#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
64#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
65pub struct Size {
66    /// The width.
67    pub width: u32,
68
69    /// The height.
70    pub height: u32,
71}
72
73impl Size {
74    /// Creates a size from a width and a height.
75    pub const fn new(width: u32, height: u32) -> Self {
76        Size { width, height }
77    }
78
79    /// Creates a size with width and height set to an equal value.
80    ///
81    /// ```rust
82    /// use embedded_graphics::geometry::Size;
83    ///
84    /// let size = Size::new_equal(11);
85    ///
86    /// assert_eq!(
87    ///     size,
88    ///     Size {
89    ///         width: 11,
90    ///         height: 11
91    ///     }
92    /// );
93    /// ```
94    pub const fn new_equal(value: u32) -> Self {
95        Size {
96            width: value,
97            height: value,
98        }
99    }
100
101    /// Creates a size with width and height equal to zero.
102    pub const fn zero() -> Self {
103        Size {
104            width: 0,
105            height: 0,
106        }
107    }
108
109    /// Returns a size with equal `width` value and `height` set to `0`.
110    ///
111    /// # Examples
112    ///
113    /// ## Move a `Point` along the X axis.
114    ///
115    /// ```rust
116    /// use embedded_graphics::geometry::{Point, Size};
117    ///
118    /// let size = Size::new(20, 30);
119    ///
120    /// let point = Point::new(10, 15);
121    ///
122    /// let moved_x = point + size.x_axis();
123    ///
124    /// assert_eq!(moved_x, Point::new(30, 15));
125    /// ```
126    pub const fn x_axis(self) -> Self {
127        Self {
128            width: self.width,
129            height: 0,
130        }
131    }
132
133    /// Returns a size with equal `height` value and `width` set to `0`.
134    ///
135    /// # Examples
136    ///
137    /// ## Move a `Point` along the Y axis.
138    ///
139    /// ```rust
140    /// use embedded_graphics::geometry::{Point, Size};
141    ///
142    /// let size = Size::new(20, 30);
143    ///
144    /// let point = Point::new(10, 15);
145    ///
146    /// let moved_y = point + size.y_axis();
147    ///
148    /// assert_eq!(moved_y, Point::new(10, 45));
149    /// ```
150    pub const fn y_axis(self) -> Self {
151        Self {
152            width: 0,
153            height: self.height,
154        }
155    }
156
157    /// Saturating addition.
158    ///
159    /// Returns `u32::max_value()` for `width` and/or `height` instead of overflowing.
160    pub const fn saturating_add(self, other: Self) -> Self {
161        Self {
162            width: self.width.saturating_add(other.width),
163            height: self.height.saturating_add(other.height),
164        }
165    }
166
167    /// Saturating subtraction.
168    ///
169    /// Returns `0` for `width` and/or `height` instead of overflowing, if the
170    /// value in `other` is larger then in `self`.
171    pub const fn saturating_sub(self, other: Self) -> Self {
172        Self {
173            width: self.width.saturating_sub(other.width),
174            height: self.height.saturating_sub(other.height),
175        }
176    }
177
178    /// Division.
179    ///
180    /// This method provides a workaround for the `Div` trait not being usable in `const` contexts.
181    pub(crate) const fn div_u32(self, rhs: u32) -> Size {
182        Size::new(self.width / rhs, self.height / rhs)
183    }
184
185    /// Creates a size from two corner points of a bounding box.
186    pub(crate) const fn from_bounding_box(corner_1: Point, corner_2: Point) -> Self {
187        let width = (corner_1.x - corner_2.x).abs() as u32 + 1;
188        let height = (corner_1.y - corner_2.y).abs() as u32 + 1;
189
190        Self { width, height }
191    }
192
193    /// Returns the componentwise minimum of two `Size`s.
194    ///
195    /// ```rust
196    /// use embedded_graphics::geometry::Size;
197    ///
198    /// let min = Size::new(20, 30).component_min(Size::new(15, 50));
199    ///
200    /// assert_eq!(min, Size::new(15, 30));
201    /// ```
202    pub fn component_min(self, other: Self) -> Self {
203        Self::new(self.width.min(other.width), self.height.min(other.height))
204    }
205
206    /// Returns the componentwise maximum of two `Size`s.
207    ///
208    /// ```rust
209    /// use embedded_graphics::geometry::Size;
210    ///
211    /// let min = Size::new(20, 30).component_max(Size::new(15, 50));
212    ///
213    /// assert_eq!(min, Size::new(20, 50));
214    /// ```
215    pub fn component_max(self, other: Self) -> Self {
216        Self::new(self.width.max(other.width), self.height.max(other.height))
217    }
218
219    /// Returns the componentwise multiplication of two `Size`s.
220    ///
221    /// ```rust
222    /// use embedded_graphics::geometry::Size;
223    ///
224    /// let result = Size::new(20, 30).component_mul(Size::new(2, 3));
225    ///
226    /// assert_eq!(result, Size::new(40, 90));
227    /// ```
228    pub const fn component_mul(self, other: Self) -> Self {
229        Self::new(self.width * other.width, self.height * other.height)
230    }
231
232    /// Returns the componentwise division of two `Size`s.
233    ///
234    /// # Panics
235    ///
236    /// Panics if one of the components of `other` equals zero.
237    ///
238    /// ```rust
239    /// use embedded_graphics::geometry::Size;
240    ///
241    /// let result = Size::new(20, 30).component_div(Size::new(5, 10));
242    ///
243    /// assert_eq!(result, Size::new(4, 3));
244    /// ```
245    pub const fn component_div(self, other: Self) -> Self {
246        Self::new(self.width / other.width, self.height / other.height)
247    }
248}
249
250impl Add for Size {
251    type Output = Size;
252
253    fn add(self, other: Size) -> Size {
254        Size::new(self.width + other.width, self.height + other.height)
255    }
256}
257
258impl AddAssign for Size {
259    fn add_assign(&mut self, other: Size) {
260        self.width += other.width;
261        self.height += other.height;
262    }
263}
264
265impl Sub for Size {
266    type Output = Size;
267
268    fn sub(self, other: Size) -> Size {
269        Size::new(self.width - other.width, self.height - other.height)
270    }
271}
272
273impl SubAssign for Size {
274    fn sub_assign(&mut self, other: Size) {
275        self.width -= other.width;
276        self.height -= other.height;
277    }
278}
279
280impl Mul<u32> for Size {
281    type Output = Size;
282
283    fn mul(self, rhs: u32) -> Size {
284        Size::new(self.width * rhs, self.height * rhs)
285    }
286}
287
288impl MulAssign<u32> for Size {
289    fn mul_assign(&mut self, rhs: u32) {
290        self.width *= rhs;
291        self.height *= rhs;
292    }
293}
294
295impl Div<u32> for Size {
296    type Output = Size;
297
298    fn div(self, rhs: u32) -> Size {
299        self.div_u32(rhs)
300    }
301}
302
303impl DivAssign<u32> for Size {
304    fn div_assign(&mut self, rhs: u32) {
305        self.width /= rhs;
306        self.height /= rhs;
307    }
308}
309
310impl Index<usize> for Size {
311    type Output = u32;
312
313    fn index(&self, idx: usize) -> &u32 {
314        match idx {
315            0 => &self.width,
316            1 => &self.height,
317            _ => panic!("index out of bounds: the len is 2 but the index is {}", idx),
318        }
319    }
320}
321
322impl From<(u32, u32)> for Size {
323    fn from(other: (u32, u32)) -> Self {
324        Size::new(other.0, other.1)
325    }
326}
327
328impl From<[u32; 2]> for Size {
329    fn from(other: [u32; 2]) -> Self {
330        Size::new(other[0], other[1])
331    }
332}
333
334impl From<&[u32; 2]> for Size {
335    fn from(other: &[u32; 2]) -> Self {
336        Size::new(other[0], other[1])
337    }
338}
339
340impl From<Size> for (u32, u32) {
341    fn from(other: Size) -> (u32, u32) {
342        (other.width, other.height)
343    }
344}
345
346impl From<Size> for [u32; 2] {
347    fn from(other: Size) -> [u32; 2] {
348        [other.width, other.height]
349    }
350}
351
352impl From<&Size> for (u32, u32) {
353    fn from(other: &Size) -> (u32, u32) {
354        (other.width, other.height)
355    }
356}
357
358impl fmt::Display for Size {
359    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
360        write!(f, "{} x {}", self.width, self.height)
361    }
362}
363
364#[cfg(feature = "nalgebra_support")]
365use nalgebra::{base::Scalar, Vector2};
366
367#[cfg(feature = "nalgebra_support")]
368impl<N> From<Vector2<N>> for Size
369where
370    N: Into<u32> + Scalar + Copy,
371{
372    fn from(other: Vector2<N>) -> Self {
373        Self::new(other[0].into(), other[1].into())
374    }
375}
376
377#[cfg(feature = "nalgebra_support")]
378impl<N> From<&Vector2<N>> for Size
379where
380    N: Into<u32> + Scalar + Copy,
381{
382    fn from(other: &Vector2<N>) -> Self {
383        Self::new(other[0].into(), other[1].into())
384    }
385}
386
387#[cfg(test)]
388mod tests {
389    use super::*;
390
391    use core::fmt::Write;
392
393    #[test]
394    fn sizes_can_be_added() {
395        let left = Size::new(10, 20);
396        let right = Size::new(30, 40);
397
398        assert_eq!(left + right, Size::new(40, 60));
399    }
400
401    #[test]
402    fn sizes_can_be_subtracted() {
403        let left = Size::new(30, 40);
404        let right = Size::new(10, 20);
405
406        assert_eq!(left - right, Size::new(20, 20));
407    }
408
409    #[test]
410    fn saturating_sub() {
411        let p = Size::new(10, 20);
412
413        assert_eq!(p.saturating_sub(Size::new(9, 18)), Size::new(1, 2));
414        assert_eq!(p.saturating_sub(Size::new(11, 18)), Size::new(0, 2));
415        assert_eq!(p.saturating_sub(Size::new(9, 21)), Size::new(1, 0));
416        assert_eq!(p.saturating_sub(Size::new(11, 21)), Size::new(0, 0));
417    }
418
419    #[test]
420    fn sizes_can_be_multiplied_by_scalar() {
421        let s = Size::new(1, 2);
422        assert_eq!(s * 3, Size::new(3, 6));
423
424        let mut s = Size::new(2, 3);
425        s *= 4;
426        assert_eq!(s, Size::new(8, 12));
427    }
428
429    #[test]
430    fn sizes_can_be_divided_by_scalar() {
431        let s = Size::new(10, 20);
432        assert_eq!(s / 2, Size::new(5, 10));
433
434        let mut s = Size::new(20, 30);
435        s /= 5;
436        assert_eq!(s, Size::new(4, 6));
437    }
438
439    #[test]
440    fn from_tuple() {
441        assert_eq!(Size::from((20, 30)), Size::new(20, 30));
442    }
443
444    #[test]
445    fn from_array() {
446        assert_eq!(Size::from([20, 30]), Size::new(20, 30));
447    }
448
449    #[test]
450    fn to_array() {
451        let array: [u32; 2] = Size::new(20, 30).into();
452
453        assert_eq!(array, [20, 30]);
454    }
455
456    #[test]
457    fn from_array_ref() {
458        assert_eq!(Size::from(&[20, 30]), Size::new(20, 30));
459    }
460
461    #[test]
462    fn index() {
463        let size = Size::new(1, 2);
464
465        assert_eq!(size.width, size[0]);
466        assert_eq!(size.height, size[1]);
467    }
468
469    #[test]
470    #[should_panic]
471    fn index_out_of_bounds() {
472        let size = Size::new(1, 2);
473        let _ = size[2];
474    }
475
476    #[test]
477    #[cfg(feature = "nalgebra_support")]
478    fn nalgebra_support() {
479        let left = nalgebra::Vector2::new(30u32, 40);
480        let right = nalgebra::Vector2::new(10, 20);
481
482        assert_eq!(Size::from(left - right), Size::new(20, 20));
483    }
484
485    #[test]
486    fn component_min_max() {
487        let a = Size::new(20, 30);
488        let b = Size::new(15, 50);
489
490        assert_eq!(a.component_min(b), Size::new(15, 30));
491        assert_eq!(a.component_max(b), Size::new(20, 50));
492    }
493
494    #[test]
495    fn display() {
496        let mut buffer = arrayvec::ArrayString::<32>::new();
497        write!(buffer, "{}", Size::new(123, 456)).unwrap();
498
499        assert_eq!(&buffer, "123 x 456");
500    }
501}