Skip to main content

blockworx_geom/
align.rs

1use crate::{Pos2, Rect, Vec2, pos2};
2
3/// Left/center/right, or top/center/bottom.
4#[derive(
5    Clone, Copy, Debug, Default, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize,
6)]
7pub enum Align {
8    /// Left or top.
9    #[default]
10    Min,
11
12    Center,
13
14    /// Right or bottom.
15    Max,
16}
17
18/// Two-dimensional alignment: `[x, y]`.
19#[derive(Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
20pub struct Align2(pub [Align; 2]);
21
22impl Align2 {
23    pub const LEFT_BOTTOM: Self = Self([Align::Min, Align::Max]);
24    pub const LEFT_CENTER: Self = Self([Align::Min, Align::Center]);
25    pub const LEFT_TOP: Self = Self([Align::Min, Align::Min]);
26    pub const CENTER_BOTTOM: Self = Self([Align::Center, Align::Max]);
27    pub const CENTER_CENTER: Self = Self([Align::Center, Align::Center]);
28    pub const CENTER_TOP: Self = Self([Align::Center, Align::Min]);
29    pub const RIGHT_BOTTOM: Self = Self([Align::Max, Align::Max]);
30    pub const RIGHT_CENTER: Self = Self([Align::Max, Align::Center]);
31    pub const RIGHT_TOP: Self = Self([Align::Max, Align::Min]);
32
33    #[inline]
34    pub fn x(self) -> Align {
35        self.0[0]
36    }
37
38    #[inline]
39    pub fn y(self) -> Align {
40        self.0[1]
41    }
42
43    /// Place a box of `size` so that the corner this alignment names lands on
44    /// `pos`: `RIGHT_TOP` puts the box's right-top at `pos`.
45    pub fn anchor_size(self, pos: Pos2, size: Vec2) -> Rect {
46        let x = match self.x() {
47            Align::Min => pos.x,
48            Align::Center => pos.x - 0.5 * size.x,
49            Align::Max => pos.x - size.x,
50        };
51        let y = match self.y() {
52            Align::Min => pos.y,
53            Align::Center => pos.y - 0.5 * size.y,
54            Align::Max => pos.y - size.y,
55        };
56        Rect::from_min_size(pos2(x, y), size)
57    }
58
59    /// The point of `rect` this alignment names: `RIGHT_TOP` is its right-top
60    /// corner. The inverse of [`Self::anchor_size`].
61    pub fn pos_in_rect(self, rect: Rect) -> Pos2 {
62        let along = |align: Align, min: f32, max: f32| match align {
63            Align::Min => min,
64            Align::Center => 0.5 * (min + max),
65            Align::Max => max,
66        };
67        pos2(
68            along(self.x(), rect.min.x, rect.max.x),
69            along(self.y(), rect.min.y, rect.max.y),
70        )
71    }
72
73    /// [`Self::anchor_size`] anchoring `rect` by its own left-top corner.
74    pub fn anchor_rect(self, rect: Rect) -> Rect {
75        self.anchor_size(rect.min, rect.size())
76    }
77}
78
79impl std::fmt::Debug for Align2 {
80    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81        write!(f, "Align2({:?}, {:?})", self.x(), self.y())
82    }
83}