1use crate::dimen::UVec2;
18use crate::shape::Rectangle;
19use crate::texture_packer::TexturePackerError::NotEnoughSpace;
20
21#[derive(Debug)]
22struct FreeRegion
23{
24 rect: Rectangle<u32>
25}
26
27impl FreeRegion
28{
29 #[inline]
30 fn from_rectangle(rect: Rectangle<u32>) -> Self
31 {
32 FreeRegion { rect }
33 }
34
35 #[inline]
36 fn new(width: u32, height: u32) -> Self
37 {
38 FreeRegion::from_rectangle(Rectangle::new(UVec2::ZERO, UVec2::new(width, height)))
39 }
40}
41
42#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
43pub(crate) enum TexturePackerError
44{
45 NotEnoughSpace
46}
47
48#[derive(Debug)]
49pub(crate) struct TexturePacker
50{
51 areas: Vec<FreeRegion>
52}
53
54impl TexturePacker
55{
56 pub(crate) fn new(width: u32, height: u32) -> Self
57 {
58 TexturePacker {
59 areas: vec![FreeRegion::new(width, height)]
60 }
61 }
62
63 pub(crate) fn try_allocate(
64 &mut self,
65 size: UVec2
66 ) -> Result<Rectangle<u32>, TexturePackerError>
67 {
68 if size.x == 0 || size.y == 0 {
69 return Ok(Rectangle::new(UVec2::ZERO, size));
70 }
71
72 let size = size + UVec2::new(2, 2);
73
74 let width = size.x;
76 let height = size.y;
77
78 let mut best_area: Option<&mut FreeRegion> = None;
79
80 for area in &mut self.areas {
81 let area_width = area.rect.width();
82 let area_height = area.rect.height();
83
84 if width > area.rect.width() || height > area.rect.height() {
85 continue;
86 }
87
88 let update_best = if let Some(current_best) = &best_area {
89 current_best.rect.width() >= area_width
90 && current_best.rect.height() >= area_height
91 } else {
92 true
93 };
94
95 if update_best {
96 best_area = Some(area);
97 }
98 }
99
100 let best_area = best_area.ok_or(NotEnoughSpace)?;
101
102 let alloc_area_with_border =
103 Rectangle::new(*best_area.rect.top_left(), best_area.rect.top_left() + size);
104
105 let space_underneath = Rectangle::new(
106 UVec2::new(
107 best_area.rect.top_left().x,
108 alloc_area_with_border.bottom_right().y
109 ),
110 *best_area.rect.bottom_right()
111 );
112
113 let space_right = Rectangle::new(
114 UVec2::new(
115 alloc_area_with_border.bottom_right().x,
116 best_area.rect.top_left().y
117 ),
118 space_underneath.top_right()
119 );
120
121 if space_right.is_zero_area() {
122 best_area.rect = space_underneath
123 } else {
124 best_area.rect = space_right;
125
126 if !space_underneath.is_zero_area() {
127 self.areas
128 .push(FreeRegion::from_rectangle(space_underneath));
129 }
130 }
131
132 Ok(Rectangle::new(
133 alloc_area_with_border.top_left() + UVec2::new(1, 1),
134 alloc_area_with_border.bottom_right() - UVec2::new(1, 1)
135 ))
136 }
137}
138
139#[cfg(test)]
140mod test
141{
142
143 use super::*;
144
145 #[test]
146 fn pack_test_fill_four_squares()
147 {
148 let mut packer = TexturePacker::new(64, 64);
149
150 assert_eq!(
151 Ok(Rectangle::from_tuples((1, 1), (31, 31))),
152 packer.try_allocate(UVec2::new(30, 30))
153 );
154
155 assert_eq!(
156 Ok(Rectangle::from_tuples((33, 1), (63, 31))),
157 packer.try_allocate(UVec2::new(30, 30))
158 );
159
160 assert_eq!(
161 Ok(Rectangle::from_tuples((1, 33), (31, 63))),
162 packer.try_allocate(UVec2::new(30, 30))
163 );
164
165 assert_eq!(
166 Ok(Rectangle::from_tuples((33, 33), (63, 63))),
167 packer.try_allocate(UVec2::new(30, 30))
168 );
169
170 assert_eq!(Err(NotEnoughSpace), packer.try_allocate(UVec2::new(30, 30)));
171 }
172
173 #[test]
174 fn pack_test_nonfill_four_squares()
175 {
176 let mut packer = TexturePacker::new(64, 64);
177
178 assert_eq!(
179 Ok(Rectangle::from_tuples((1, 1), (29, 29))),
180 packer.try_allocate(UVec2::new(28, 28))
181 );
182
183 assert_eq!(
184 Ok(Rectangle::from_tuples((31, 1), (59, 29))),
185 packer.try_allocate(UVec2::new(28, 28))
186 );
187
188 assert_eq!(
189 Ok(Rectangle::from_tuples((1, 31), (29, 59))),
190 packer.try_allocate(UVec2::new(28, 28))
191 );
192
193 assert_eq!(
194 Ok(Rectangle::from_tuples((31, 31), (59, 59))),
195 packer.try_allocate(UVec2::new(28, 28))
196 );
197
198 assert_eq!(Err(NotEnoughSpace), packer.try_allocate(UVec2::new(30, 30)));
199 }
200
201 #[test]
202 fn pack_test_uneven_squares()
203 {
204 let mut packer = TexturePacker::new(64, 64);
205
206 assert_eq!(
207 Ok(Rectangle::from_tuples((1, 1), (15, 15))),
208 packer.try_allocate(UVec2::new(14, 14))
209 );
210
211 assert_eq!(
212 Ok(Rectangle::from_tuples((1, 17), (15, 47))),
213 packer.try_allocate(UVec2::new(14, 30))
214 );
215
216 assert_eq!(
217 Ok(Rectangle::from_tuples((17, 17), (47, 47))),
218 packer.try_allocate(UVec2::new(30, 30))
219 );
220
221 assert_eq!(
222 Ok(Rectangle::from_tuples((17, 1), (31, 15))),
223 packer.try_allocate(UVec2::new(14, 14))
224 );
225 }
226}