fastpack_core/algorithms/
basic.rs1use crate::{
2 error::CoreError,
3 types::rect::{Rect, Size},
4};
5
6use super::packer::{PackInput, PackOutput, Packer, PlacedSprite, Placement};
7
8#[derive(Default)]
17pub struct Basic;
18
19impl Packer for Basic {
20 fn pack(&self, input: PackInput) -> Result<PackOutput, CoreError> {
21 if input.sprites.is_empty() {
22 return Err(CoreError::NoSprites);
23 }
24 Ok(pack_basic(input))
25 }
26
27 fn name(&self) -> &'static str {
28 "basic"
29 }
30}
31
32fn pack_basic(input: PackInput) -> PackOutput {
33 let cfg = &input.config;
34 let bp = cfg.border_padding;
35 let sp = cfg.shape_padding;
36
37 let mut sprites = input.sprites;
38 sprites.sort_unstable_by(|a, b| b.image.height().cmp(&a.image.height()));
40
41 let max_canvas_w = cfg.max_width.saturating_sub(bp * 2);
42 let max_canvas_h = cfg.max_height.saturating_sub(bp * 2);
43
44 let mut placed = Vec::with_capacity(sprites.len());
45 let mut overflow = Vec::new();
46
47 let mut cursor_x: u32 = 0;
48 let mut cursor_y: u32 = 0;
49 let mut row_h: u32 = 0;
50 let mut atlas_w: u32 = 0;
51 let mut atlas_h: u32 = 0;
52
53 for sprite in sprites {
54 let sw = sprite.image.width();
55 let sh = sprite.image.height();
56 let fw = sw + sp;
57 let fh = sh + sp;
58
59 if cursor_x > 0 && cursor_x + fw > max_canvas_w {
61 cursor_y += row_h;
62 cursor_x = 0;
63 row_h = 0;
64 }
65
66 if cursor_y + sh > max_canvas_h {
68 overflow.push(sprite);
69 continue;
70 }
71
72 let dest_x = bp + cursor_x;
73 let dest_y = bp + cursor_y;
74
75 atlas_w = atlas_w.max(cursor_x + sw);
76 atlas_h = atlas_h.max(cursor_y + sh);
77 row_h = row_h.max(fh);
78
79 placed.push(PlacedSprite {
80 placement: Placement {
81 sprite_id: sprite.id.clone(),
82 dest: Rect::new(dest_x, dest_y, sw, sh),
83 rotated: false,
84 },
85 sprite,
86 });
87
88 cursor_x += fw;
89 }
90
91 let raw_w = if placed.is_empty() {
92 0
93 } else {
94 atlas_w + bp * 2
95 };
96 let raw_h = if placed.is_empty() {
97 0
98 } else {
99 atlas_h + bp * 2
100 };
101
102 let mut w = cfg.size_constraint.apply(raw_w).min(cfg.max_width);
103 let mut h = cfg.size_constraint.apply(raw_h).min(cfg.max_height);
104
105 if cfg.force_square {
106 let side = w.max(h);
107 w = side;
108 h = side;
109 }
110
111 PackOutput {
112 placed,
113 atlas_size: Size { w, h },
114 overflow,
115 }
116}