texture_packer/
rect.rs

1use crate::texture::Texture;
2
3/// Defines a rectangle in pixels with the origin at the top-left of the texture atlas.
4#[derive(Copy, Clone, Debug)]
5pub struct Rect {
6    /// Horizontal position the rectangle begins at.
7    pub x: u32,
8    /// Vertical position the rectangle begins at.
9    pub y: u32,
10    /// Width of the rectangle.
11    pub w: u32,
12    /// Height of the rectangle.
13    pub h: u32,
14}
15
16impl Rect {
17    /// Create a new [Rect] based on a position and its width and height.
18    pub fn new(x: u32, y: u32, w: u32, h: u32) -> Rect {
19        Rect { x, y, w, h }
20    }
21
22    /// Create a new [Rect] based on two points spanning the rectangle.
23    pub fn new_with_points(x1: u32, y1: u32, x2: u32, y2: u32) -> Rect {
24        Rect {
25            x: x1,
26            y: y1,
27            w: x2 - x1 + 1,
28            h: y2 - y1 + 1,
29        }
30    }
31
32    /// Get the top coordinate of the rectangle.
33    #[inline(always)]
34    pub fn top(&self) -> u32 {
35        self.y
36    }
37
38    /// Get the bottom coordinate of the rectangle.
39    #[inline(always)]
40    pub fn bottom(&self) -> u32 {
41        self.y + self.h - 1
42    }
43
44    /// Get the left coordinate of the rectangle.
45    #[inline(always)]
46    pub fn left(&self) -> u32 {
47        self.x
48    }
49
50    /// Get the right coordinate of the rectangle.
51    #[inline(always)]
52    pub fn right(&self) -> u32 {
53        self.x + self.w - 1
54    }
55
56    /// Get the area of the rectangle.
57    #[inline(always)]
58    pub fn area(&self) -> u32 {
59        self.w * self.h
60    }
61
62    /// Check if this rectangle intersects with another.
63    pub fn intersects(&self, other: &Rect) -> bool {
64        self.left() < other.right()
65            && self.right() > other.left()
66            && self.top() < other.bottom()
67            && self.bottom() > other.top()
68    }
69
70    /// Check if this rectangle contains another.
71    pub fn contains(&self, other: &Rect) -> bool {
72        self.left() <= other.left()
73            && self.right() >= other.right()
74            && self.top() <= other.top()
75            && self.bottom() >= other.bottom()
76    }
77
78    /// Check if this rectangle contains a point. Includes the edges of the rectangle.
79    pub fn contains_point(&self, x: u32, y: u32) -> bool {
80        self.left() <= x && self.right() >= x && self.top() <= y && self.bottom() >= y
81    }
82
83    /// Check if a point falls on the rectangle's boundaries.
84    pub fn is_outline(&self, x: u32, y: u32) -> bool {
85        x == self.left() || x == self.right() || y == self.top() || y == self.bottom()
86    }
87
88    /// Split two rectangles into non-overlapping regions.
89    pub fn crop(&self, other: &Rect) -> Vec<Rect> {
90        if !self.intersects(other) {
91            return vec![*self];
92        }
93
94        let inside_x1 = if other.left() < self.left() {
95            self.left()
96        } else {
97            other.left()
98        };
99
100        let inside_y1 = if other.top() < self.top() {
101            self.top()
102        } else {
103            other.top()
104        };
105
106        let inside_x2 = if other.right() > self.right() {
107            self.right()
108        } else {
109            other.right()
110        };
111
112        let inside_y2 = if other.bottom() > self.bottom() {
113            self.bottom()
114        } else {
115            other.bottom()
116        };
117
118        //
119        // *******************
120        // *    | r3  |      *
121        // *    |     |      *
122        // *    +++++++      *
123        // * r1 +     +      *
124        // *    +     +  r2  *
125        // *    +++++++      *
126        // *    |     |      *
127        // *    | r4  |      *
128        // *******************
129        //
130        let mut result = Vec::new();
131
132        let r1 = Rect::new_with_points(self.left(), self.top(), inside_x1, self.bottom());
133        if r1.area() > 0 {
134            result.push(r1);
135        }
136
137        let r2 = Rect::new_with_points(inside_x2, self.top(), self.right(), self.bottom());
138        if r2.area() > 0 {
139            result.push(r2);
140        }
141
142        let r3 = Rect::new_with_points(inside_x1, self.top(), inside_x2, inside_y1);
143        if r3.area() > 0 {
144            result.push(r3);
145        }
146
147        let r4 = Rect::new_with_points(inside_x1, inside_y2, inside_x2, self.bottom());
148        if r4.area() > 0 {
149            result.push(r4);
150        }
151
152        result
153    }
154}
155
156impl<T: Texture> From<&T> for Rect {
157    fn from(item: &T) -> Self {
158        Rect {
159            x: 0,
160            y: 0,
161            w: item.width(),
162            h: item.height(),
163        }
164    }
165}