geometry/
dims.rs

1//! A horizontal and vertical rectangular dimension with no specified location.
2
3use std::cmp::Ordering;
4
5use serde::{Deserialize, Serialize};
6
7use crate::dir::Dir;
8use crate::point::Point;
9use crate::rect::Rect;
10
11/// A horizontal and vertical rectangular dimension with no specified location.
12#[derive(
13    Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Serialize, Deserialize,
14)]
15pub struct Dims {
16    /// The width dimension.
17    w: i64,
18    /// The height dimension.
19    h: i64,
20}
21
22impl Dims {
23    /// Creates a new [`Dims`] from a width and height.
24    pub fn new(w: i64, h: i64) -> Self {
25        Self { w, h }
26    }
27    /// Creates a new [`Dims`] with width and height equal to `value`.
28    ///
29    /// # Example
30    ///
31    /// ```
32    /// # use geometry::prelude::*;
33    /// assert_eq!(Dims::square(100), Dims::new(100, 100));
34    /// ```
35    pub fn square(value: i64) -> Self {
36        Self { w: value, h: value }
37    }
38
39    /// Returns the dimension in the specified direction.
40    ///
41    /// # Example
42    ///
43    /// ```
44    /// # use geometry::prelude::*;
45    /// let dims = Dims::new(100, 200);
46    /// assert_eq!(dims.dim(Dir::Vert), 200);
47    /// assert_eq!(dims.dim(Dir::Horiz), 100);
48    /// ```
49    pub fn dim(&self, dir: Dir) -> i64 {
50        match dir {
51            Dir::Vert => self.h,
52            Dir::Horiz => self.w,
53        }
54    }
55
56    /// Returns the direction of the longer dimension.
57    ///
58    /// If the width and height are equal, returns [`Dir::Horiz`].
59    ///
60    /// # Example
61    ///
62    /// ```
63    /// # use geometry::prelude::*;
64    /// let dims = Dims::new(100, 200);
65    /// assert_eq!(dims.longer_dir(), Dir::Vert);
66    /// let dims = Dims::new(200, 100);
67    /// assert_eq!(dims.longer_dir(), Dir::Horiz);
68    /// let dims = Dims::new(100, 100);
69    /// assert_eq!(dims.longer_dir(), Dir::Horiz);
70    /// ```
71    pub fn longer_dir(&self) -> Dir {
72        if self.w >= self.h {
73            Dir::Horiz
74        } else {
75            Dir::Vert
76        }
77    }
78
79    /// Returns the direction of the longer dimension.
80    ///
81    /// If the width and height are equal, returns [`None`].
82    /// Otherwise, returns a `Some` variant containing the longer direction.
83    pub fn longer_dir_strict(&self) -> Option<Dir> {
84        match self.w.cmp(&self.h) {
85            Ordering::Greater => Some(Dir::Horiz),
86            Ordering::Equal => None,
87            Ordering::Less => Some(Dir::Vert),
88        }
89    }
90
91    /// Returns a new [`Dims`] object with the horizontal and vertical dimensions flipped.
92    pub fn transpose(self) -> Self {
93        Self {
94            w: self.h,
95            h: self.w,
96        }
97    }
98
99    /// Returns the width (i.e. the horizontal dimension).
100    #[inline]
101    pub fn width(&self) -> i64 {
102        self.w
103    }
104
105    /// Returns the height (i.e. the vertical dimension).
106    #[inline]
107    pub fn height(&self) -> i64 {
108        self.h
109    }
110
111    /// Returns the width (i.e. the horizontal dimension).
112    ///
113    /// A shorthand for [`Dims::width`].
114    #[inline]
115    pub fn w(&self) -> i64 {
116        self.width()
117    }
118
119    /// Returns the height (i.e. the vertical dimension).
120    ///
121    /// A shorthand for [`Dims::height`].
122    #[inline]
123    pub fn h(&self) -> i64 {
124        self.height()
125    }
126
127    /// Converts this dimension object into a [`Rect`].
128    ///
129    /// See [`Rect::from_dims`] for more information.
130    #[inline]
131    pub fn into_rect(self) -> Rect {
132        Rect::from_dims(self)
133    }
134
135    /// Converts this dimension object into a [`Point`] with coordinates `(self.w(), self.h())`.
136    #[inline]
137    pub fn into_point(self) -> Point {
138        Point::new(self.w(), self.h())
139    }
140}
141
142impl std::ops::Add<Dims> for Dims {
143    type Output = Self;
144    fn add(self, rhs: Dims) -> Self::Output {
145        Self {
146            w: self.w + rhs.w,
147            h: self.h + rhs.h,
148        }
149    }
150}
151
152impl std::ops::Sub<Dims> for Dims {
153    type Output = Self;
154    fn sub(self, rhs: Dims) -> Self::Output {
155        Self {
156            w: self.w - rhs.w,
157            h: self.h - rhs.h,
158        }
159    }
160}
161
162impl std::ops::Mul<i64> for Dims {
163    type Output = Self;
164    fn mul(self, rhs: i64) -> Self::Output {
165        Self {
166            w: self.w * rhs,
167            h: self.h * rhs,
168        }
169    }
170}
171
172impl std::ops::Mul<(usize, usize)> for Dims {
173    type Output = Self;
174    fn mul(self, rhs: (usize, usize)) -> Self::Output {
175        Self {
176            w: self.w * rhs.0 as i64,
177            h: self.h * rhs.1 as i64,
178        }
179    }
180}
181
182impl std::ops::AddAssign<Dims> for Dims {
183    fn add_assign(&mut self, rhs: Dims) {
184        self.w += rhs.w;
185        self.h += rhs.h;
186    }
187}
188
189impl std::ops::SubAssign<Dims> for Dims {
190    fn sub_assign(&mut self, rhs: Dims) {
191        self.w -= rhs.w;
192        self.h -= rhs.h;
193    }
194}
195
196impl std::ops::MulAssign<i64> for Dims {
197    fn mul_assign(&mut self, rhs: i64) {
198        self.w *= rhs;
199        self.h *= rhs;
200    }
201}
202
203impl From<Rect> for Dims {
204    /// Obtains [`Dims`] from the given [`Rect`] using [`Rect::dims`].
205    #[inline]
206    fn from(value: Rect) -> Self {
207        value.dims()
208    }
209}
210
211impl From<Point> for Dims {
212    /// Create a new dimension object from a point.
213    ///
214    /// The width field of the resulting [`Dims`] will be the point's x-coordinate.
215    /// The height field of the resulting [`Dims`] will be the point's y-coordinate.
216    #[inline]
217    fn from(value: Point) -> Self {
218        Self::new(value.x, value.y)
219    }
220}