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}