geometry/place.rs
1//! Traits for placing a geometric object at a point.
2
3use serde::{Deserialize, Serialize};
4
5use crate::{
6 bbox::Bbox, corner::Corner, point::Point, rect::Rect, side::Side, transform::TranslateMut,
7};
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
10/// An enumeration of possible ways to place a geometric shape at a point.
11pub enum PlaceMode {
12 /// Place the corner of a geometric shape at a point.
13 Corner(Corner),
14 /// Place the side of a geometric shape at a point's coordinate along the same axis.
15 Side(Side),
16 /// Place the center of a side of a geometric shape at a point.
17 SideCenter(Side),
18 /// Place the center of a geometric shape at a point.
19 Center,
20 /// Place the x-coordinate of a geometric shape's center at the x-coordinate of a point.
21 CenterX,
22 /// Place the y-coordinate of a geometric shape's center at the y-coordinate of a point.
23 CenterY,
24}
25
26/// A geometric shape that can be placed at a point.
27///
28/// # Examples
29///
30/// ```
31/// # use geometry::prelude::*;
32/// # use geometry::place::PlaceRectMut;
33/// let mut rect1 = Rect::from_sides(0, 0, 100, 200);
34/// rect1.place_mut(PlaceMode::Center, rect1, Point::new(25, 25));
35/// assert_eq!(rect1, Rect::from_sides(-25, -75, 75, 125));
36///
37/// // Alternate rectangle to align `rect1` with.
38/// // Conceptually, this represents that `rect1` has
39/// // 5 units of hangover space on all sides that should
40/// // not contribute to placement.
41/// let rect1_alt = rect1.shrink_all(5).unwrap();
42/// rect1.place_mut(PlaceMode::Corner(Corner::UpperRight), rect1_alt, Point::new(25, 25));
43/// assert_eq!(rect1, Rect::from_sides(-70, -170, 30, 30));
44/// ```
45pub trait PlaceRectMut: TranslateMut {
46 /// Places an object at the given point.
47 ///
48 /// For center alignments, the center's non-integer coordinates are rounded down to the nearest integer.
49 /// This behavior is subject to change and should not be relied upon.
50 fn place_mut(&mut self, mode: PlaceMode, srect: Rect, pt: Point) {
51 match mode {
52 PlaceMode::Corner(corner) => {
53 let ofs = pt - srect.corner(corner);
54 self.translate_mut(ofs);
55 }
56 PlaceMode::Side(side) => {
57 let dir_ofs = side.coord_dir();
58 let ofs = pt.coord(dir_ofs) - srect.side(side);
59 self.translate_mut(Point::from_dir_coords(dir_ofs, ofs, 0));
60 }
61 PlaceMode::SideCenter(side) => {
62 let edge = srect.edge(side);
63 let center =
64 Point::from_dir_coords(side.coord_dir(), edge.coord(), edge.span().center());
65 let ofs = pt - center;
66 self.translate_mut(ofs);
67 }
68 PlaceMode::Center => {
69 let ofs = pt - srect.center();
70 self.translate_mut(ofs);
71 }
72 PlaceMode::CenterX => {
73 let ofs = pt.x - srect.center().x;
74 self.translate_mut(Point::new(ofs, 0));
75 }
76 PlaceMode::CenterY => {
77 let ofs = pt.y - srect.center().y;
78 self.translate_mut(Point::new(0, ofs));
79 }
80 }
81 }
82}
83
84impl<T: TranslateMut> PlaceRectMut for T {}
85
86/// A geometric shape that can be placed at a point.
87///
88/// # Examples
89///
90/// ```
91/// # use geometry::prelude::*;
92/// let mut rect1 = Rect::from_sides(0, 0, 100, 200);
93/// assert_eq!(
94/// rect1.place(PlaceMode::Center, rect1, Point::new(25, 25)),
95/// Rect::from_sides(-25, -75, 75, 125),
96/// );
97///
98/// // Alternate rectangle to align `rect1` with.
99/// // Conceptually, this represents that `rect1` has
100/// // 5 units of hangover space on all sides that should
101/// // not contribute to placement.
102/// let rect1_alt = rect1.shrink_all(5).unwrap();
103///
104/// assert_eq!(
105/// rect1.place(
106/// PlaceMode::Corner(Corner::UpperRight),
107/// rect1_alt,
108/// Point::new(25, 25)
109/// ),
110/// Rect::from_sides(-70, -170, 30, 30),
111/// );
112/// ```
113pub trait PlaceRect: PlaceRectMut + Sized {
114 /// Places an object at the given point.
115 ///
116 /// For center alignments, the center's non-integer coordinates are rounded down to the nearest integer.
117 /// This behavior is subject to change and should not be relied upon.
118 ///
119 /// Creates a new shape at the placed location.
120 fn place(mut self, mode: PlaceMode, srect: Rect, pt: Point) -> Self {
121 self.place_mut(mode, srect, pt);
122 self
123 }
124}
125
126impl<T: PlaceRectMut + Sized> PlaceRect for T {}
127
128/// A geometric shape that can be placed at a point using its bounding box.
129///
130/// # Examples
131///
132/// ```
133/// # use geometry::prelude::*;
134/// # use geometry::place::PlaceBboxMut;
135/// let mut rect1 = Rect::from_sides(0, 0, 100, 200);
136/// rect1.place_bbox_mut(PlaceMode::Center, Point::new(25, 25));
137/// assert_eq!(rect1, Rect::from_sides(-25, -75, 75, 125));
138/// ```
139pub trait PlaceBboxMut: PlaceRectMut + Bbox {
140 /// Places an object at the given point using its bounding box.
141 ///
142 /// For center alignments, the center's non-integer coordinates are rounded down to the nearest integer.
143 /// This behavior is subject to change and should not be relied upon.
144 fn place_bbox_mut(&mut self, mode: PlaceMode, pt: Point) {
145 self.place_mut(mode, self.bbox().unwrap(), pt)
146 }
147}
148
149impl<T: PlaceRectMut + Bbox> PlaceBboxMut for T {}
150
151/// A geometric shape that can be placed at a point using its bounding box.
152///
153/// # Examples
154///
155/// ```
156/// # use geometry::prelude::*;
157/// let mut rect1 = Rect::from_sides(0, 0, 100, 200);
158/// assert_eq!(
159/// rect1.place_bbox(PlaceMode::Center, Point::new(25, 25)),
160/// Rect::from_sides(-25, -75, 75, 125),
161/// );
162/// ```
163pub trait PlaceBbox: PlaceBboxMut + Sized {
164 /// Places an object at the given point using its boudning box.
165 ///
166 /// For center alignments, the center's non-integer coordinates are rounded down to the nearest integer.
167 /// This behavior is subject to change and should not be relied upon.
168 ///
169 /// Creates a new shape at the placed location.
170 fn place_bbox(mut self, mode: PlaceMode, pt: Point) -> Self {
171 self.place_bbox_mut(mode, pt);
172 self
173 }
174}
175
176impl<T: PlaceBboxMut + Sized> PlaceBbox for T {}
177
178#[cfg(test)]
179mod tests {
180 use crate::{
181 corner::Corner,
182 place::{PlaceBbox, PlaceMode, PlaceRect},
183 point::Point,
184 rect::Rect,
185 side::Side,
186 };
187
188 #[test]
189 fn place_and_place_bbox_work() {
190 let rect1 = Rect::from_sides(0, 0, 100, 200);
191 let pt = Point::new(75, 75);
192
193 assert_eq!(
194 rect1.place(PlaceMode::Corner(Corner::UpperLeft), rect1, pt),
195 Rect::from_sides(75, -125, 175, 75)
196 );
197 assert_eq!(
198 rect1.place_bbox(PlaceMode::Corner(Corner::UpperLeft), pt),
199 Rect::from_sides(75, -125, 175, 75)
200 );
201
202 assert_eq!(
203 rect1.place(PlaceMode::Side(Side::Right), rect1, pt),
204 Rect::from_sides(-25, 0, 75, 200)
205 );
206 assert_eq!(
207 rect1.place_bbox(PlaceMode::Side(Side::Right), pt),
208 Rect::from_sides(-25, 0, 75, 200)
209 );
210
211 assert_eq!(
212 rect1.place(PlaceMode::SideCenter(Side::Left), rect1, pt),
213 Rect::from_sides(75, -25, 175, 175)
214 );
215 assert_eq!(
216 rect1.place_bbox(PlaceMode::SideCenter(Side::Left), pt),
217 Rect::from_sides(75, -25, 175, 175)
218 );
219
220 assert_eq!(
221 rect1.place(PlaceMode::Center, rect1, pt),
222 Rect::from_sides(25, -25, 125, 175)
223 );
224 assert_eq!(
225 rect1.place_bbox(PlaceMode::Center, pt),
226 Rect::from_sides(25, -25, 125, 175)
227 );
228
229 assert_eq!(
230 rect1.place(PlaceMode::CenterX, rect1, pt),
231 Rect::from_sides(25, 0, 125, 200)
232 );
233 assert_eq!(
234 rect1.place_bbox(PlaceMode::CenterX, pt),
235 Rect::from_sides(25, 0, 125, 200)
236 );
237
238 assert_eq!(
239 rect1.place(PlaceMode::CenterY, rect1, pt),
240 Rect::from_sides(0, -25, 100, 175)
241 );
242 assert_eq!(
243 rect1.place_bbox(PlaceMode::CenterY, pt),
244 Rect::from_sides(0, -25, 100, 175)
245 );
246 }
247}