geometry/
align.rs

1//! Traits for aligning geometric objects.
2
3use serde::{Deserialize, Serialize};
4
5use crate::{
6    bbox::Bbox,
7    point::Point,
8    rect::Rect,
9    transform::{Translate, TranslateMut},
10};
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
13/// An enumeration of possible alignment modes between two geometric shapes.
14pub enum AlignMode {
15    /// Align the left sides of the two shapes.
16    Left,
17    /// Align the right sides of the two shapes.
18    Right,
19    /// Align the bottom sides of the two shapes.
20    Bottom,
21    /// Align the top sides of the two shapes.
22    Top,
23    /// Align the centers of the two shapes horizontally.
24    CenterHorizontal,
25    /// Align the centers of the two shapes vertically.
26    CenterVertical,
27    /// Align the left side of one shape to the right of
28    /// the right side of the other.
29    ToTheRight,
30    /// Align the right side of one shape to the left of
31    /// the left side of the other.
32    ToTheLeft,
33    /// Align the top side of one shape beneath
34    /// the bottom side of the other.
35    Beneath,
36    /// Align the bottom side of one shape above
37    /// the top side of the other.
38    Above,
39}
40
41/// A geometric shape that can be aligned using the relationship between two [`Rect`]s.
42///
43/// # Examples
44///
45/// ```
46/// # use geometry::prelude::*;
47/// # use geometry::align::AlignRectMut;
48/// let mut rect1 = Rect::from_sides(0, 0, 100, 200);
49/// let rect2 = Rect::from_sides(500, 600, 700, 700);
50/// rect1.align_mut(AlignMode::Left, rect1, rect2, 0);
51/// assert_eq!(rect1.left(), rect2.left());
52/// assert_eq!(rect1, Rect::from_sides(500, 0, 600, 200));
53///
54/// // Alternate rectangle to align `rect1` with.
55/// // Conceptually, this represents that `rect1` has
56/// // 5 units of hangover space on all sides that should
57/// // not contribute to alignment.
58/// let rect1_alt = rect1.shrink_all(5).unwrap();
59/// rect1.align_mut(AlignMode::Left, rect1_alt, rect2, 0);
60/// assert_eq!(rect1, Rect::from_sides(495, 0, 595, 200));
61/// ```
62pub trait AlignRectMut: TranslateMut {
63    /// Align `self` based on the relationship between `srect` and `orect`.
64    ///
65    /// `offset` represents an offset from the base alignment in the positive direction
66    /// along the alignment axis.
67    ///
68    /// For center alignments, if the centers are a non-integer number of units apart,
69    /// the translation amount is rounded down to the nearest integer. This behavior is subject
70    /// to change and should not be relied upon.
71    fn align_mut(&mut self, mode: AlignMode, srect: Rect, orect: Rect, offset: i64) {
72        match mode {
73            AlignMode::Left => {
74                self.translate_mut(Point::new(orect.left() - srect.left() + offset, 0));
75            }
76            AlignMode::Right => {
77                self.translate_mut(Point::new(orect.right() - srect.right() + offset, 0));
78            }
79            AlignMode::Bottom => {
80                self.translate_mut(Point::new(0, orect.bot() - srect.bot() + offset));
81            }
82            AlignMode::Top => {
83                self.translate_mut(Point::new(0, orect.top() - srect.top() + offset));
84            }
85            AlignMode::ToTheRight => {
86                self.translate_mut(Point::new(orect.right() - srect.left() + offset, 0));
87            }
88            AlignMode::ToTheLeft => {
89                self.translate_mut(Point::new(orect.left() - srect.right() + offset, 0));
90            }
91            AlignMode::CenterHorizontal => {
92                self.translate_mut(Point::new(
93                    ((orect.left() + orect.right()) - (srect.left() + srect.right())) / 2 + offset,
94                    0,
95                ));
96            }
97            AlignMode::CenterVertical => {
98                self.translate_mut(Point::new(
99                    0,
100                    ((orect.bot() + orect.top()) - (srect.bot() + srect.top())) / 2 + offset,
101                ));
102            }
103            AlignMode::Beneath => {
104                self.translate_mut(Point::new(0, orect.bot() - srect.top() + offset));
105            }
106            AlignMode::Above => {
107                self.translate_mut(Point::new(0, orect.top() - srect.bot() + offset));
108            }
109        }
110    }
111}
112
113impl<T: Translate> AlignRectMut for T {}
114
115/// A geometric shape that can be aligned using the relationship between two [`Rect`]s.
116///
117/// Takes in an owned copy of the shape and returns the aligned version.
118///
119/// # Examples
120///
121/// ```
122/// # use geometry::prelude::*;
123/// let mut rect1 = Rect::from_sides(0, 0, 100, 200);
124/// let rect2 = Rect::from_sides(500, 600, 700, 700);
125/// let rect1 = rect1.align(AlignMode::Left, rect1, rect2, 0);
126/// assert_eq!(rect1.left(), rect2.left());
127/// assert_eq!(rect1, Rect::from_sides(500, 0, 600, 200));
128///
129/// // Alternate rectangle to align `rect1` with.
130/// // Conceptually, this represents that `rect1` has
131/// // 5 units of hangover space on all sides that should
132/// // not contribute to alignment.
133/// let rect1_alt = rect1.shrink_all(5).unwrap();
134/// assert_eq!(rect1.align(AlignMode::Left, rect1_alt, rect2, 0), Rect::from_sides(495, 0, 595, 200));
135/// ```
136pub trait AlignRect: AlignRectMut + Sized {
137    /// Align `self` based on the relationship between `srect` and `orect`.
138    ///
139    /// `offset` represents an offset from the base alignment in the positive direction
140    /// along the alignment axis.
141    ///
142    /// For center alignments, if the centers are a non-integer number of units apart,
143    /// the translation amount is rounded down to the nearest integer. This behavior is subject
144    /// to change and should not be relied upon.
145    ///
146    /// Creates a new shape at the aligned location of the original.
147    fn align(mut self, mode: AlignMode, srect: Rect, orect: Rect, offset: i64) -> Self {
148        self.align_mut(mode, srect, orect, offset);
149        self
150    }
151}
152
153impl<T: AlignRectMut + Sized> AlignRect for T {}
154
155/// A geometric shape that can be aligned with another shape using their bounding boxes.
156///
157/// # Examples
158///
159/// ```
160/// # use geometry::prelude::*;
161/// # use geometry::align::AlignBboxMut;
162/// let mut rect1 = Rect::from_sides(0, 0, 100, 200);
163/// let rect2 = Rect::from_sides(500, 600, 700, 700);
164/// rect1.align_bbox_mut(AlignMode::Left, rect2, 0);
165/// assert_eq!(rect1.left(), rect2.left());
166/// assert_eq!(rect1, Rect::from_sides(500, 0, 600, 200));
167/// ```
168pub trait AlignBboxMut: AlignRectMut + Bbox {
169    /// Align `self` using its bounding box and the bounding box of `other`.
170    ///
171    /// `offset` represents an offset from the base alignment in the positive direction
172    /// along the alignment axis.
173    ///
174    /// For center alignments, if the centers are a non-integer number of units apart,
175    /// the translation amount is rounded down to the nearest integer. This behavior is subject
176    /// to change and should not be relied upon.
177    fn align_bbox_mut(&mut self, mode: AlignMode, other: impl Bbox, offset: i64) {
178        self.align_mut(mode, self.bbox().unwrap(), other.bbox().unwrap(), offset);
179    }
180}
181
182impl<T: AlignRectMut + Bbox> AlignBboxMut for T {}
183
184/// A geometric shape that can be aligned with another shape using their bounding boxes.
185///
186/// Takes in an owned copy of the shape and returns the aligned version.
187///
188/// # Examples
189///
190/// ```
191/// # use geometry::prelude::*;
192/// let mut rect1 = Rect::from_sides(0, 0, 100, 200);
193/// let rect2 = Rect::from_sides(500, 600, 700, 700);
194/// let rect1 = rect1.align_bbox(AlignMode::Left, rect2, 0);
195/// assert_eq!(rect1.left(), rect2.left());
196/// assert_eq!(rect1, Rect::from_sides(500, 0, 600, 200));
197/// ```
198pub trait AlignBbox: AlignBboxMut + Sized {
199    /// Align `self` using its bounding box and the bounding box of `other`.
200    ///
201    /// `offset` represents an offset from the base alignment in the positive direction
202    /// along the alignment axis.
203    ///
204    /// For center alignments, if the centers are a non-integer number of units apart,
205    /// the translation amount is rounded down to the nearest integer. This behavior is subject
206    /// to change and should not be relied upon.
207    ///
208    /// Creates a new shape at the aligned location of the original.
209    fn align_bbox(mut self, mode: AlignMode, other: impl Bbox, offset: i64) -> Self {
210        self.align_bbox_mut(mode, other, offset);
211        self
212    }
213}
214
215impl<T: AlignBboxMut + Sized> AlignBbox for T {}
216
217#[cfg(test)]
218mod tests {
219    use crate::{
220        align::{AlignBboxMut, AlignMode, AlignRectMut},
221        rect::Rect,
222    };
223
224    #[test]
225    fn align_and_align_bbox_work() {
226        let mut rect1 = Rect::from_sides(0, 0, 100, 200);
227        let mut rect1_bbox = Rect::from_sides(0, 0, 100, 200);
228        let rect2 = Rect::from_sides(500, 600, 700, 700);
229        rect1.align_mut(AlignMode::Left, rect1, rect2, 0);
230        rect1_bbox.align_bbox_mut(AlignMode::Left, rect2, 0);
231        assert_eq!(rect1, Rect::from_sides(500, 0, 600, 200));
232        assert_eq!(rect1_bbox, Rect::from_sides(500, 0, 600, 200));
233
234        rect1.align_mut(AlignMode::Right, rect1, rect2, 0);
235        rect1_bbox.align_bbox_mut(AlignMode::Right, rect2, 0);
236        assert_eq!(rect1, Rect::from_sides(600, 0, 700, 200));
237        assert_eq!(rect1_bbox, Rect::from_sides(600, 0, 700, 200));
238
239        rect1.align_mut(AlignMode::Bottom, rect1, rect2, 0);
240        rect1_bbox.align_bbox_mut(AlignMode::Bottom, rect2, 0);
241        assert_eq!(rect1, Rect::from_sides(600, 600, 700, 800));
242        assert_eq!(rect1_bbox, Rect::from_sides(600, 600, 700, 800));
243
244        rect1.align_mut(AlignMode::Top, rect1, rect2, 0);
245        rect1_bbox.align_bbox_mut(AlignMode::Top, rect2, 0);
246        assert_eq!(rect1, Rect::from_sides(600, 500, 700, 700));
247        assert_eq!(rect1_bbox, Rect::from_sides(600, 500, 700, 700));
248
249        rect1.align_mut(AlignMode::ToTheLeft, rect1, rect2, 0);
250        rect1_bbox.align_bbox_mut(AlignMode::ToTheLeft, rect2, 0);
251        assert_eq!(rect1, Rect::from_sides(400, 500, 500, 700));
252        assert_eq!(rect1_bbox, Rect::from_sides(400, 500, 500, 700));
253
254        rect1.align_mut(AlignMode::ToTheRight, rect1, rect2, 0);
255        rect1_bbox.align_bbox_mut(AlignMode::ToTheRight, rect2, 0);
256        assert_eq!(rect1, Rect::from_sides(700, 500, 800, 700));
257        assert_eq!(rect1_bbox, Rect::from_sides(700, 500, 800, 700));
258
259        rect1.align_mut(AlignMode::Beneath, rect1, rect2, 0);
260        rect1_bbox.align_bbox_mut(AlignMode::Beneath, rect2, 0);
261        assert_eq!(rect1, Rect::from_sides(700, 400, 800, 600));
262        assert_eq!(rect1_bbox, Rect::from_sides(700, 400, 800, 600));
263
264        rect1.align_mut(AlignMode::Above, rect1, rect2, 0);
265        rect1_bbox.align_bbox_mut(AlignMode::Above, rect2, 0);
266        assert_eq!(rect1, Rect::from_sides(700, 700, 800, 900));
267        assert_eq!(rect1_bbox, Rect::from_sides(700, 700, 800, 900));
268
269        rect1.align_mut(AlignMode::CenterHorizontal, rect1, rect2, 0);
270        rect1_bbox.align_bbox_mut(AlignMode::CenterHorizontal, rect2, 0);
271        assert_eq!(rect1, Rect::from_sides(550, 700, 650, 900));
272        assert_eq!(rect1_bbox, Rect::from_sides(550, 700, 650, 900));
273
274        rect1.align_mut(AlignMode::CenterVertical, rect1, rect2, 0);
275        rect1_bbox.align_bbox_mut(AlignMode::CenterVertical, rect2, 0);
276        assert_eq!(rect1, Rect::from_sides(550, 550, 650, 750));
277        assert_eq!(rect1_bbox, Rect::from_sides(550, 550, 650, 750));
278    }
279}