Skip to main content

altui_core/layout/
rect.rs

1use std::cmp::{max, min};
2
3use crate::layout::{Direction, Margin};
4
5/// A simple rectangle used in the computation of the layout and to give widgets a hint about the
6/// area they are supposed to render to.
7#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default)]
8pub struct Rect {
9    pub x: u16,
10    pub y: u16,
11    pub width: u16,
12    pub height: u16,
13}
14
15impl Rect {
16    /// Creates a new rect, with width and height limited to keep the area under max u16.
17    /// If clipped, aspect ratio will be preserved.
18    pub fn new(x: u16, y: u16, width: u16, height: u16) -> Rect {
19        let max_area = u16::max_value();
20        let (clipped_width, clipped_height) =
21            if u32::from(width) * u32::from(height) > u32::from(max_area) {
22                let aspect_ratio = f64::from(width) / f64::from(height);
23                let max_area_f = f64::from(max_area);
24                let height_f = (max_area_f / aspect_ratio).sqrt();
25                let width_f = height_f * aspect_ratio;
26                (width_f as u16, height_f as u16)
27            } else {
28                (width, height)
29            };
30        Rect {
31            x,
32            y,
33            width: clipped_width,
34            height: clipped_height,
35        }
36    }
37
38    /// Computes the area of the rectangle.
39    ///
40    /// **Returns**: The rectangle's area as `width × height`.
41    /// **Note**: Consumes `self` by value.
42    pub fn area(self) -> u16 {
43        self.width * self.height
44    }
45
46    /// Returns the x-coordinate of the rectangle's left edge.
47    pub fn left(self) -> u16 {
48        self.x
49    }
50
51    /// Returns the x-coordinate of the rectangle's right edge.
52    pub fn right(self) -> u16 {
53        self.x.saturating_add(self.width)
54    }
55
56    /// Returns the y-coordinate of the rectangle's top edge.
57    pub fn top(self) -> u16 {
58        self.y
59    }
60
61    /// Returns the y-coordinate of the rectangle's bottom edge.
62    pub fn bottom(self) -> u16 {
63        self.y.saturating_add(self.height)
64    }
65
66    pub fn inner(self, margin: &Margin) -> Rect {
67        if self.width < 2 * margin.horizontal || self.height < 2 * margin.vertical {
68            Rect::default()
69        } else {
70            Rect {
71                x: self.x + margin.horizontal,
72                y: self.y + margin.vertical,
73                width: self.width - 2 * margin.horizontal,
74                height: self.height - 2 * margin.vertical,
75            }
76        }
77    }
78
79    pub fn union(self, other: Rect) -> Rect {
80        let x1 = min(self.x, other.x);
81        let y1 = min(self.y, other.y);
82        let x2 = max(self.x + self.width, other.x + other.width);
83        let y2 = max(self.y + self.height, other.y + other.height);
84        Rect {
85            x: x1,
86            y: y1,
87            width: x2 - x1,
88            height: y2 - y1,
89        }
90    }
91
92    pub fn intersection(self, other: Rect) -> Rect {
93        let x1 = max(self.x, other.x);
94        let y1 = max(self.y, other.y);
95        let x2 = min(self.x + self.width, other.x + other.width);
96        let y2 = min(self.y + self.height, other.y + other.height);
97        Rect {
98            x: x1,
99            y: y1,
100            width: x2 - x1,
101            height: y2 - y1,
102        }
103    }
104
105    pub fn intersects(self, other: Rect) -> bool {
106        self.x < other.x + other.width
107            && self.x + self.width > other.x
108            && self.y < other.y + other.height
109            && self.y + self.height > other.y
110    }
111
112    pub fn inhere(&self, x: u16, y: u16) -> bool {
113        x >= self.x && x < self.x + self.width && y >= self.y && y < self.y + self.height
114    }
115
116    #[inline(always)]
117    pub(crate) fn start(&self, direction: Direction) -> f64 {
118        match direction {
119            Direction::Horizontal => self.x as f64,
120            Direction::Vertical => self.y as f64,
121        }
122    }
123
124    #[inline(always)]
125    pub(crate) fn end(&self, direction: Direction) -> f64 {
126        match direction {
127            Direction::Horizontal => (self.x + self.width) as f64,
128            Direction::Vertical => (self.y + self.height) as f64,
129        }
130    }
131
132    #[inline(always)]
133    pub(crate) fn size(&self, direction: Direction) -> f64 {
134        match direction {
135            Direction::Horizontal => self.width as f64,
136            Direction::Vertical => self.height as f64,
137        }
138    }
139
140    #[inline(always)]
141    pub(crate) fn cross_start(&self, direction: Direction) -> f64 {
142        match direction {
143            Direction::Horizontal => self.y as f64,
144            Direction::Vertical => self.x as f64,
145        }
146    }
147
148    #[inline(always)]
149    pub(crate) fn cross_end(&self, direction: Direction) -> f64 {
150        match direction {
151            Direction::Horizontal => (self.y + self.height) as f64,
152            Direction::Vertical => (self.x + self.width) as f64,
153        }
154    }
155
156    #[inline(always)]
157    pub(crate) fn cross_size(&self, direction: Direction) -> f64 {
158        match direction {
159            Direction::Horizontal => self.height as f64,
160            Direction::Vertical => self.width as f64,
161        }
162    }
163}