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}