fastpack_core/algorithms/
grid.rs1use crate::{
2 error::CoreError,
3 types::rect::{Rect, Size},
4};
5
6use super::packer::{PackInput, PackOutput, Packer, PlacedSprite, Placement};
7
8#[derive(Default)]
19pub struct Grid {
20 pub cell_width: Option<u32>,
22 pub cell_height: Option<u32>,
24}
25
26impl Packer for Grid {
27 fn pack(&self, input: PackInput) -> Result<PackOutput, CoreError> {
28 if input.sprites.is_empty() {
29 return Err(CoreError::NoSprites);
30 }
31 Ok(pack_grid(input, self.cell_width, self.cell_height))
32 }
33
34 fn name(&self) -> &'static str {
35 "grid"
36 }
37}
38
39fn pack_grid(input: PackInput, fixed_cw: Option<u32>, fixed_ch: Option<u32>) -> PackOutput {
40 let cfg = &input.config;
41 let bp = cfg.border_padding;
42 let sp = cfg.shape_padding;
43
44 let cell_w = fixed_cw.unwrap_or_else(|| {
45 input
46 .sprites
47 .iter()
48 .map(|s| s.image.width())
49 .max()
50 .unwrap_or(1)
51 });
52 let cell_h = fixed_ch.unwrap_or_else(|| {
53 input
54 .sprites
55 .iter()
56 .map(|s| s.image.height())
57 .max()
58 .unwrap_or(1)
59 });
60
61 let step_x = cell_w + sp;
62 let step_y = cell_h + sp;
63
64 let usable_w = cfg.max_width.saturating_sub(bp * 2);
66 let cols = ((usable_w + sp) / step_x).max(1);
67
68 let mut placed = Vec::with_capacity(input.sprites.len());
69 let mut overflow = Vec::new();
70 let mut max_row = 0u32;
71
72 for (idx, sprite) in input.sprites.into_iter().enumerate() {
73 let col = idx as u32 % cols;
74 let row = idx as u32 / cols;
75 let x = bp + col * step_x;
76 let y = bp + row * step_y;
77
78 if y + cell_h + bp > cfg.max_height {
80 overflow.push(sprite);
81 continue;
82 }
83
84 let sw = sprite.image.width();
86 let sh = sprite.image.height();
87 let dest_x = x + (cell_w.saturating_sub(sw)) / 2;
88 let dest_y = y + (cell_h.saturating_sub(sh)) / 2;
89
90 max_row = max_row.max(row);
91
92 placed.push(PlacedSprite {
93 placement: Placement {
94 sprite_id: sprite.id.clone(),
95 dest: Rect::new(dest_x, dest_y, sw, sh),
96 rotated: false,
97 },
98 sprite,
99 });
100 }
101
102 let row_count = if placed.is_empty() { 0 } else { max_row + 1 };
103
104 let raw_w = if row_count == 0 {
106 0
107 } else {
108 bp + cols * step_x - sp + bp
109 };
110 let raw_h = if row_count == 0 {
111 0
112 } else {
113 bp + row_count * step_y - sp + bp
114 };
115
116 let mut w = cfg.size_constraint.apply(raw_w).min(cfg.max_width);
117 let mut h = cfg.size_constraint.apply(raw_h).min(cfg.max_height);
118
119 if cfg.force_square {
120 let side = w.max(h);
121 w = side;
122 h = side;
123 }
124
125 PackOutput {
126 placed,
127 atlas_size: Size { w, h },
128 overflow,
129 }
130}