1type Dim = u32;
30
31#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
33pub struct ItemId {
34 idx: u32,
35 generation: u32,
36}
37
38#[derive(Debug)]
40struct ItemSlot {
41 generation: u32,
42 item: Option<Item>,
43}
44
45#[derive(Copy, Clone, Debug)]
46struct Item {
47 x: Dim,
48 y: Dim,
49 width: Dim,
50 height: Dim,
51 shelf_index: usize,
52}
53
54#[derive(Copy, Clone, Debug)]
55struct Span {
56 x: Dim,
57 width: Dim,
58}
59
60#[derive(Debug)]
61struct Shelf {
62 y: Dim,
63 height: Dim,
64 index: usize,
65 free_spans: Vec<Span>,
66}
67
68fn add_dim(a: Dim, b: Dim) -> Dim {
69 a.checked_add(b).expect("dimension overflow")
70}
71
72impl Shelf {
73 fn new(y: Dim, height: Dim, width: Dim, index: usize) -> Self {
74 Shelf {
75 y,
76 height,
77 index,
78 free_spans: vec![Span { x: 0, width }],
79 }
80 }
81
82 fn has_space_for(&self, width: Dim) -> bool {
83 self.free_spans.iter().any(|span| span.width >= width)
84 }
85
86 fn alloc_item(&mut self, width: Dim, height: Dim) -> Option<Item> {
87 if height > self.height {
88 return None;
89 }
90 for i in 0..self.free_spans.len() {
91 if self.free_spans[i].width >= width {
92 let x = self.free_spans[i].x;
93 let rest = self.free_spans[i].width - width;
94 if rest > 0 {
95 self.free_spans[i].x = add_dim(self.free_spans[i].x, width);
96 self.free_spans[i].width = rest;
97 } else {
98 self.free_spans.remove(i);
99 }
100 return Some(Item {
101 x,
102 y: self.y,
103 width,
104 height,
105 shelf_index: self.index,
106 });
107 }
108 }
109 None
110 }
111
112 fn free_item(&mut self, item: Item) {
113 self.add_free_span(item.x, item.width);
114 }
115
116 fn add_free_span(&mut self, x: Dim, width: Dim) {
117 if width == 0 {
118 return;
119 }
120 let pos = self.free_spans.partition_point(|span| span.x < x);
121 self.free_spans.insert(pos, Span { x, width });
122 self.merge_free_spans(pos);
123 }
124
125 fn merge_free_spans(&mut self, idx: usize) {
126 if idx + 1 < self.free_spans.len() {
127 let (left, right) = self.free_spans.split_at_mut(idx + 1);
128 let cur = &mut left[idx];
129 let next = &right[0];
130 if add_dim(cur.x, cur.width) == next.x {
131 cur.width = add_dim(cur.width, next.width);
132 self.free_spans.remove(idx + 1);
133 }
134 }
135 if idx > 0 && idx < self.free_spans.len() {
136 let prev_idx = idx - 1;
137 if add_dim(self.free_spans[prev_idx].x, self.free_spans[prev_idx].width)
138 == self.free_spans[idx].x
139 {
140 let width = self.free_spans[idx].width;
141 self.free_spans[prev_idx].width = add_dim(self.free_spans[prev_idx].width, width);
142 self.free_spans.remove(idx);
143 }
144 }
145 }
146}
147
148#[derive(Debug)]
149pub struct Atlas {
151 width: Dim,
152 height: Dim,
153 shelves: Vec<Shelf>,
154 items: Vec<ItemSlot>,
155 free_list: Vec<usize>,
156}
157
158impl Atlas {
159 pub fn new(width: Dim, height: Dim) -> Self {
161 let width = if width > 0 { width } else { 64 };
162 let height = if height > 0 { height } else { 64 };
163 Atlas {
164 width,
165 height,
166 shelves: Vec::with_capacity(8),
167 items: Vec::new(),
168 free_list: Vec::new(),
169 }
170 }
171
172 pub fn width(&self) -> Dim {
174 self.width
175 }
176
177 pub fn height(&self) -> Dim {
179 self.height
180 }
181
182 pub fn add(&mut self, width: Dim, height: Dim) -> Option<ItemId> {
186 if width == 0 || height == 0 {
187 return None;
188 }
189
190 let mut best_shelf: Option<usize> = None;
191 let mut best_score = Dim::MAX;
192 let mut top_y: Dim = 0;
193
194 for i in 0..self.shelves.len() {
195 let shelf_height = self.shelves[i].height;
196 top_y = top_y.max(add_dim(self.shelves[i].y, shelf_height));
197 if shelf_height < height {
198 continue;
199 }
200 if shelf_height == height {
201 if let Some(id) = self.alloc_on_shelf(i, width, height) {
202 return Some(id);
203 }
204 } else {
205 let score = shelf_height - height;
206 if score < best_score && self.shelves[i].has_space_for(width) {
207 best_score = score;
208 best_shelf = Some(i);
209 }
210 }
211 }
212
213 if let Some(idx) = best_shelf
214 && let Some(id) = self.alloc_on_shelf(idx, width, height) {
215 return Some(id);
216 }
217
218 if width <= self.width && add_dim(top_y, height) <= self.height {
219 let shelf_index = self.shelves.len();
220 self.shelves
221 .push(Shelf::new(top_y, height, self.width, shelf_index));
222 return self.alloc_on_shelf(shelf_index, width, height);
223 }
224
225 None
226 }
227
228 fn alloc_on_shelf(&mut self, shelf_index: usize, width: Dim, height: Dim) -> Option<ItemId> {
229 let item = {
230 let shelf = &mut self.shelves[shelf_index];
231 shelf.alloc_item(width, height)?
232 };
233 Some(self.store_item(item))
234 }
235
236 fn store_item(&mut self, item: Item) -> ItemId {
237 if let Some(idx) = self.free_list.pop() {
238 let slot = &mut self.items[idx];
239 slot.item = Some(item);
240 slot.generation = slot.generation.wrapping_add(1);
241 ItemId {
242 idx: idx as u32,
243 generation: slot.generation,
244 }
245 } else {
246 let generation = 1;
247 self.items.push(ItemSlot {
248 generation,
249 item: Some(item),
250 });
251 ItemId {
252 idx: (self.items.len() - 1) as u32,
253 generation,
254 }
255 }
256 }
257
258 pub fn remove(&mut self, id: ItemId) -> bool {
260 let idx = id.idx as usize;
261 if let Some(slot) = self.items.get_mut(idx)
262 && slot.generation == id.generation
263 && let Some(item) = slot.item.take() {
264 self.shelves[item.shelf_index].free_item(item);
265 slot.generation = slot.generation.wrapping_add(1);
266 self.free_list.push(idx);
267 return true;
268 }
269 false
270 }
271
272 pub fn clear(&mut self, new_width: Option<Dim>, new_height: Option<Dim>) {
274 if let Some(w) = new_width
275 && w > 0 {
276 self.width = w;
277 }
278 if let Some(h) = new_height
279 && h > 0 {
280 self.height = h;
281 }
282 self.shelves.clear();
283 self.free_list.clear();
284 for (i, slot) in self.items.iter_mut().enumerate() {
285 slot.item = None;
286 slot.generation = slot.generation.wrapping_add(1);
287 self.free_list.push(i);
288 }
289 }
290
291 pub fn item_x(&self, id: ItemId) -> Option<Dim> {
293 self.item(id).map(|item| item.x)
294 }
295
296 pub fn item_y(&self, id: ItemId) -> Option<Dim> {
298 self.item(id).map(|item| item.y)
299 }
300
301 pub fn item_width(&self, id: ItemId) -> Option<Dim> {
303 self.item(id).map(|item| item.width)
304 }
305
306 pub fn item_height(&self, id: ItemId) -> Option<Dim> {
308 self.item(id).map(|item| item.height)
309 }
310
311 fn item(&self, id: ItemId) -> Option<&Item> {
312 let idx = id.idx as usize;
313 let slot = self.items.get(idx)?;
314 if slot.generation == id.generation {
315 slot.item.as_ref()
316 } else {
317 None
318 }
319 }
320}
321
322#[cfg(test)]
323mod tests {
324 use super::*;
325
326 fn check_item(atlas: &Atlas, id: ItemId, x: Dim, y: Dim, w: Dim, h: Dim) {
327 assert_eq!(Some(x), atlas.item_x(id));
328 assert_eq!(Some(y), atlas.item_y(id));
329 assert_eq!(Some(w), atlas.item_width(id));
330 assert_eq!(Some(h), atlas.item_height(id));
331 }
332
333 #[test]
334 fn invalid_item_when_out_of_space() {
335 let mut atlas = Atlas::new(64, 64);
336 let e1 = atlas.add(40, 16).unwrap();
337 let e2 = atlas.add(24, 16).unwrap();
338 check_item(&atlas, e1, 0, 0, 40, 16);
339 check_item(&atlas, e2, 40, 0, 24, 16);
340
341 let e3 = atlas.add(32, 48).unwrap();
342 let e4 = atlas.add(32, 48).unwrap();
343 check_item(&atlas, e3, 0, 16, 32, 48);
344 check_item(&atlas, e4, 32, 16, 32, 48);
345
346 let e5 = atlas.add(1, 1);
347 assert!(e5.is_none());
348 }
349
350 #[test]
351 fn same_height_on_same_shelf() {
352 let mut atlas = Atlas::new(64, 64);
353 let e1 = atlas.add(10, 10).unwrap();
354 let e2 = atlas.add(10, 10).unwrap();
355 let e3 = atlas.add(10, 10).unwrap();
356 check_item(&atlas, e1, 0, 0, 10, 10);
357 check_item(&atlas, e2, 10, 0, 10, 10);
358 check_item(&atlas, e3, 20, 0, 10, 10);
359 }
360
361 #[test]
362 fn larger_height_new_shelf() {
363 let mut atlas = Atlas::new(64, 64);
364 let e1 = atlas.add(10, 10).unwrap();
365 let e2 = atlas.add(10, 15).unwrap();
366 let e3 = atlas.add(10, 20).unwrap();
367 check_item(&atlas, e1, 0, 0, 10, 10);
368 check_item(&atlas, e2, 0, 10, 10, 15);
369 check_item(&atlas, e3, 0, 25, 10, 20);
370 }
371
372 #[test]
373 fn shorter_height_existing_best_shelf() {
374 let mut atlas = Atlas::new(64, 64);
375 let e1 = atlas.add(10, 10).unwrap();
376 let e2 = atlas.add(10, 15).unwrap();
377 let e3 = atlas.add(10, 20).unwrap();
378 let e4 = atlas.add(10, 9).unwrap();
379 check_item(&atlas, e1, 0, 0, 10, 10);
380 check_item(&atlas, e2, 0, 10, 10, 15);
381 check_item(&atlas, e3, 0, 25, 10, 20);
382 check_item(&atlas, e4, 10, 0, 10, 9);
383 }
384
385 #[test]
386 fn pack_uses_free_space() {
387 let mut atlas = Atlas::new(64, 64);
388 let _e1 = atlas.add(10, 10).unwrap();
389 let e2 = atlas.add(10, 10).unwrap();
390 let _e3 = atlas.add(10, 10).unwrap();
391
392 assert!(atlas.remove(e2));
393 let e4 = atlas.add(10, 10).unwrap();
394 check_item(&atlas, e4, 10, 0, 10, 10);
395 }
396
397 #[test]
398 fn pack_uses_least_wasteful_free_space() {
399 let mut atlas = Atlas::new(64, 64);
400 let e1 = atlas.add(10, 10).unwrap();
401 let e2 = atlas.add(10, 15).unwrap();
402 let e3 = atlas.add(10, 20).unwrap();
403
404 atlas.remove(e3);
405 atlas.remove(e2);
406 atlas.remove(e1);
407
408 let e4 = atlas.add(10, 13).unwrap();
409 check_item(&atlas, e4, 0, 10, 10, 13);
410 }
411
412 #[test]
413 fn pack_makes_new_shelf_if_free_entries_more_wasteful() {
414 let mut atlas = Atlas::new(64, 64);
415 let e1 = atlas.add(10, 10).unwrap();
416 let e2 = atlas.add(10, 15).unwrap();
417 atlas.remove(e2);
418 let e3 = atlas.add(10, 10).unwrap();
419 check_item(&atlas, e1, 0, 0, 10, 10);
420 check_item(&atlas, e3, 10, 0, 10, 10);
421 }
422
423 #[test]
424 fn pack_considers_max_dimensions_for_space_reuse() {
425 let mut atlas = Atlas::new(64, 64);
426 let _ = atlas.add(10, 10).unwrap();
427 let e2 = atlas.add(10, 15).unwrap();
428
429 atlas.remove(e2);
430
431 let e3 = atlas.add(10, 13).unwrap();
432 check_item(&atlas, e3, 0, 10, 10, 13);
433
434 atlas.remove(e3);
435
436 let e4 = atlas.add(10, 14).unwrap();
437 check_item(&atlas, e4, 0, 10, 10, 14);
438 }
439
440 #[test]
441 fn pack_results_minimal_size() {
442 let mut atlas = Atlas::new(30, 45);
443
444 let res0 = atlas.add(10, 10).unwrap();
445 let res1 = atlas.add(5, 15).unwrap();
446 let res2 = atlas.add(25, 15).unwrap();
447 let res3 = atlas.add(10, 20).unwrap();
448
449 check_item(&atlas, res0, 0, 0, 10, 10);
450 check_item(&atlas, res1, 0, 10, 5, 15);
451 check_item(&atlas, res2, 5, 10, 25, 15);
452 check_item(&atlas, res3, 0, 25, 10, 20);
453
454 assert_eq!(30, atlas.width());
455 assert_eq!(45, atlas.height());
456 }
457
458 #[test]
459 fn pack_shelf_coalescing() {
460 let mut atlas = Atlas::new(100, 10);
461
462 let r_a = atlas.add(10, 10).unwrap();
463 let r_b = atlas.add(20, 10).unwrap();
464 let r_c = atlas.add(10, 10).unwrap();
465 let r_d = atlas.add(40, 10).unwrap();
466 check_item(&atlas, r_a, 0, 0, 10, 10);
467 check_item(&atlas, r_b, 10, 0, 20, 10);
468 check_item(&atlas, r_c, 30, 0, 10, 10);
469 check_item(&atlas, r_d, 40, 0, 40, 10);
470
471 atlas.remove(r_a);
472 atlas.remove(r_c);
473 atlas.remove(r_b);
474
475 let r_e = atlas.add(30, 10).unwrap();
476 check_item(&atlas, r_e, 0, 0, 30, 10);
477
478 atlas.remove(r_d);
479 atlas.remove(r_e);
480
481 let r_f = atlas.add(90, 10).unwrap();
482 check_item(&atlas, r_f, 0, 0, 90, 10);
483
484 assert_eq!(100, atlas.width());
485 assert_eq!(10, atlas.height());
486 }
487
488 #[test]
489 fn clear_keeps_size_or_resizes() {
490 let mut atlas = Atlas::new(10, 10);
491 let e1 = atlas.add(10, 10).unwrap();
492 check_item(&atlas, e1, 0, 0, 10, 10);
493
494 atlas.clear(None, None);
495
496 let e2 = atlas.add(10, 5).unwrap();
497 let e3 = atlas.add(5, 5).unwrap();
498 let e4 = atlas.add(5, 5).unwrap();
499 check_item(&atlas, e2, 0, 0, 10, 5);
500 check_item(&atlas, e3, 0, 5, 5, 5);
501 check_item(&atlas, e4, 5, 5, 5, 5);
502 assert!(atlas.add(1, 1).is_none());
503
504 atlas.clear(Some(10), Some(11));
505
506 let e2 = atlas.add(10, 5).unwrap();
507 let e3 = atlas.add(5, 5).unwrap();
508 let e4 = atlas.add(5, 5).unwrap();
509 check_item(&atlas, e2, 0, 0, 10, 5);
510 check_item(&atlas, e3, 0, 5, 5, 5);
511 check_item(&atlas, e4, 5, 5, 5, 5);
512 let e5 = atlas.add(1, 1).unwrap();
513 check_item(&atlas, e5, 0, 10, 1, 1);
514 }
515}