Skip to main content

tex_packer_core/packer/
maxrects.rs

1use super::Packer;
2use crate::config::{MaxRectsHeuristic, PackerConfig};
3use crate::free_space::{prune_contained, subtract_intersections};
4use crate::geometry::{
5    PackingContext, PlacementGeometry, area_fit_score, bottom_ex_u32, contains_rect, intersects,
6    overlap_1d, right_ex_u32,
7};
8use crate::model::{Frame, Rect};
9
10pub struct MaxRectsPacker {
11    config: PackerConfig,
12    border: Rect,
13    free: Vec<Rect>,
14    used: Vec<Rect>,
15    heuristic: MaxRectsHeuristic,
16}
17
18impl MaxRectsPacker {
19    pub fn new(config: PackerConfig, heuristic: MaxRectsHeuristic) -> Self {
20        let border = PackingContext::new(&config).usable_area();
21        Self {
22            config,
23            border,
24            free: vec![border],
25            used: Vec::new(),
26            heuristic,
27        }
28    }
29
30    fn place_rect(&mut self, node: &Rect) {
31        if self.config.mr_reference {
32            return self.place_rect_ref(node);
33        }
34        self.free = subtract_intersections(self.free.iter().copied(), node);
35        self.prune_free_list();
36        self.used.push(*node);
37    }
38
39    fn place_rect_ref(&mut self, node: &Rect) {
40        let mut new_free: Vec<Rect> = Vec::new();
41        let mut i = 0usize;
42        while i < self.free.len() {
43            let fr = self.free[i];
44            if intersects(&fr, node) {
45                // remove this free rect; split into parts added to new_free
46                self.free.swap_remove(i);
47                self.split_free_node_ref(fr, node, &mut new_free);
48            } else {
49                i += 1;
50            }
51        }
52        // Prune new_free against existing free; and remove dominated among new_free
53        self.prune_new_vs_old(&mut new_free);
54        self.prune_within(&mut new_free);
55        // Merge new into free, then final prune pass
56        self.free.extend(new_free);
57        self.prune_free_list();
58        self.used.push(*node);
59    }
60
61    fn split_free_node_ref(&self, fr: Rect, node: &Rect, out: &mut Vec<Rect>) {
62        let fr_x2 = right_ex_u32(&fr);
63        let fr_y2 = bottom_ex_u32(&fr);
64        let n_x2 = right_ex_u32(node);
65        let n_y2 = bottom_ex_u32(node);
66
67        // Left
68        if node.x > fr.x && node.x < fr_x2 {
69            let w = node.x - fr.x;
70            out.push(Rect::new(fr.x, fr.y, w, fr.h));
71        }
72        // Right
73        if n_x2 < fr_x2 {
74            let x = n_x2;
75            let w = fr_x2 - n_x2;
76            out.push(Rect::new(x, fr.y, w, fr.h));
77        }
78        // Top
79        if node.y > fr.y && node.y < fr_y2 {
80            let h = node.y - fr.y;
81            out.push(Rect::new(fr.x, fr.y, fr.w, h));
82        }
83        // Bottom
84        if n_y2 < fr_y2 {
85            let y = n_y2;
86            let h = fr_y2 - n_y2;
87            out.push(Rect::new(fr.x, y, fr.w, h));
88        }
89        // filter zero areas handled by prune later
90    }
91
92    fn prune_new_vs_old(&mut self, new_free: &mut Vec<Rect>) {
93        // Remove any new rect fully contained in any existing free rect
94        new_free
95            .retain(|nr| !self.free.iter().any(|of| contains_rect(of, nr)) && nr.w > 0 && nr.h > 0);
96        // Remove any existing free rect fully contained in any remaining new rect
97        let mut i = 0;
98        while i < self.free.len() {
99            if new_free.iter().any(|nr| contains_rect(nr, &self.free[i])) {
100                self.free.swap_remove(i);
101            } else {
102                i += 1;
103            }
104        }
105    }
106
107    fn prune_within(&self, v: &mut Vec<Rect>) {
108        let mut i = 0;
109        while i < v.len() {
110            let a = v[i];
111            let a_x2 = right_ex_u32(&a);
112            let a_y2 = bottom_ex_u32(&a);
113            let mut remove_i = false;
114            let mut j = 0;
115            while j < v.len() {
116                if i == j {
117                    j += 1;
118                    continue;
119                }
120                let b = v[j];
121                let b_x2 = right_ex_u32(&b);
122                let b_y2 = bottom_ex_u32(&b);
123                if a.x >= b.x && a.y >= b.y && a_x2 <= b_x2 && a_y2 <= b_y2 {
124                    remove_i = true;
125                    break;
126                }
127                j += 1;
128            }
129            if remove_i {
130                v.swap_remove(i);
131            } else {
132                i += 1;
133            }
134        }
135    }
136
137    fn prune_free_list(&mut self) {
138        prune_contained(&mut self.free);
139    }
140
141    fn score(&self, fr: &Rect, w: u32, h: u32) -> (i128, i128) {
142        let leftover_h = fr.w as i128 - w as i128;
143        let leftover_v = fr.h as i128 - h as i128;
144        let short_fit = leftover_h.abs().min(leftover_v.abs());
145        let long_fit = leftover_h.abs().max(leftover_v.abs());
146        let area_fit = area_fit_score(fr, w, h);
147        match self.heuristic {
148            MaxRectsHeuristic::BestAreaFit => (area_fit, short_fit),
149            MaxRectsHeuristic::BestShortSideFit => (short_fit, long_fit),
150            MaxRectsHeuristic::BestLongSideFit => (long_fit, short_fit),
151            MaxRectsHeuristic::BottomLeft => (fr.y as i128, fr.x as i128),
152            MaxRectsHeuristic::ContactPoint => {
153                // maximize contact score: use negative for minimization
154                let contact = self.contact_point_score(fr.x, fr.y, w, h);
155                (-(contact as i128), area_fit)
156            }
157        }
158    }
159
160    fn find_position(&self, w: u32, h: u32) -> Option<(Rect, bool)> {
161        let mut best_score1 = i128::MAX;
162        let mut best_score2 = i128::MAX;
163        let mut best_rect = Rect::new(0, 0, 0, 0);
164        let mut best_rot = false;
165        let mut best_top = u32::MAX; // tie-break: prefer smaller top side (y + h)
166        let mut best_left = u32::MAX; // then prefer smaller x
167
168        for fr in &self.free {
169            // normal
170            if fr.w >= w && fr.h >= h {
171                let (s1, s2) = self.score(fr, w, h);
172                let top = fr.y.saturating_add(h);
173                if s1 < best_score1
174                    || (s1 == best_score1
175                        && (s2 < best_score2
176                            || (s2 == best_score2
177                                && (top < best_top || (top == best_top && fr.x < best_left)))))
178                {
179                    best_score1 = s1;
180                    best_score2 = s2;
181                    best_top = top;
182                    best_left = fr.x;
183                    best_rect = Rect::new(fr.x, fr.y, w, h);
184                    best_rot = false;
185                }
186                // perfect fit early-out
187                if fr.w == w && fr.h == h {
188                    return Some((Rect::new(fr.x, fr.y, w, h), false));
189                }
190            }
191            // rotated
192            if self.config.allow_rotation && fr.w >= h && fr.h >= w {
193                let (s1, s2) = self.score(fr, h, w);
194                let top = fr.y.saturating_add(w);
195                if s1 < best_score1
196                    || (s1 == best_score1
197                        && (s2 < best_score2
198                            || (s2 == best_score2
199                                && (top < best_top || (top == best_top && fr.x < best_left)))))
200                {
201                    best_score1 = s1;
202                    best_score2 = s2;
203                    best_top = top;
204                    best_left = fr.x;
205                    best_rect = Rect::new(fr.x, fr.y, h, w);
206                    best_rot = true;
207                }
208                // perfect fit early-out (rotated)
209                if fr.w == h && fr.h == w {
210                    return Some((Rect::new(fr.x, fr.y, h, w), true));
211                }
212            }
213        }
214
215        if best_rect.w == 0 || best_rect.h == 0 {
216            None
217        } else {
218            Some((best_rect, best_rot))
219        }
220    }
221
222    fn contact_point_score(&self, x: u32, y: u32, w: u32, h: u32) -> u64 {
223        let node = Rect::new(x, y, w, h);
224        let mut score = 0u64;
225        // contact with borders
226        let border_right = right_ex_u32(&self.border);
227        let border_bottom = bottom_ex_u32(&self.border);
228        if node.x == self.border.x {
229            score += node.h as u64;
230        }
231        if node.y == self.border.y {
232            score += node.w as u64;
233        }
234        if right_ex_u32(&node) == border_right {
235            score += node.h as u64;
236        }
237        if bottom_ex_u32(&node) == border_bottom {
238            score += node.w as u64;
239        }
240
241        // contact with used rectangles
242        for u in &self.used {
243            // vertical contact (left/right edges)
244            if node.x == right_ex_u32(u) || u.x == right_ex_u32(&node) {
245                let overlap = overlap_1d(node.y, bottom_ex_u32(&node), u.y, bottom_ex_u32(u));
246                score += overlap as u64;
247            }
248            // horizontal contact (top/bottom edges)
249            if node.y == bottom_ex_u32(u) || u.y == bottom_ex_u32(&node) {
250                let overlap = overlap_1d(node.x, right_ex_u32(&node), u.x, right_ex_u32(u));
251                score += overlap as u64;
252            }
253        }
254        score
255    }
256
257    pub fn free_list_len(&self) -> usize {
258        self.free.len()
259    }
260}
261
262impl<K: Clone> Packer<K> for MaxRectsPacker {
263    fn can_pack(&self, rect: &Rect) -> bool {
264        let geometry = PlacementGeometry::new(rect, &self.config);
265        self.find_position(geometry.reserved_w, geometry.reserved_h)
266            .is_some()
267    }
268
269    fn pack(&mut self, key: K, rect: &Rect) -> Option<Frame<K>> {
270        let geometry = PlacementGeometry::new(rect, &self.config);
271        if let Some((place, rotated)) = self.find_position(geometry.reserved_w, geometry.reserved_h)
272        {
273            self.place_rect(&place);
274            Some(geometry.frame(key, *rect, &place, rotated))
275        } else {
276            None
277        }
278    }
279}