1use crate::binpack::BinError;
61use std::fmt::{Display, Formatter};
62use std::slice::Iter;
63
64use super::{visualize_bin, BinPacker};
65use crate::dimension::Dimension;
66use crate::rectangle::Rectangle;
67
68#[derive(Copy, Clone, Debug, PartialEq)]
71pub enum Heuristic {
72 BestShortSideFit,
74 BestLongSideFit,
76 BestAreaFit,
78 BottomLeftRule,
80 ContactPointRule,
84}
85
86#[derive(Clone, Debug, PartialEq)]
92pub struct MaxRectsBin {
93 bin_width: i32,
95 bin_height: i32,
97 rects_used: Vec<Rectangle>,
99 rects_free: Vec<Rectangle>,
101
102 new_rects_free_size: usize,
104 new_rects_free: Vec<Rectangle>,
106
107 default_heuristic: Heuristic,
109}
110
111impl BinPacker for MaxRectsBin {
112 fn width(&self) -> i32 {
113 self.bin_width
114 }
115
116 fn height(&self) -> i32 {
117 self.bin_height
118 }
119
120 fn clear_with(&mut self, capacity: usize) {
121 self.rects_used.clear();
122 self.rects_used.shrink_to(capacity.max(4));
123 self.rects_free.clear();
124 self.rects_free.shrink_to((capacity * 4).max(16));
125 self.rects_free.push(Rectangle::new(
126 0,
127 0,
128 Dimension::with_id(0, self.bin_width, self.bin_height, 0),
129 ));
130 }
131
132 fn grow(&mut self, dw: u32, dh: u32) {
133 if dw > 0 {
134 self.rects_free.push(Rectangle::new(
135 self.bin_width,
136 0,
137 Dimension::with_id(0, dw as i32, self.bin_height, 0)
138 ));
139 self.bin_width += dw as i32;
140 }
141
142 if dh > 0 {
143 self.rects_free.push(Rectangle::new(
144 0,
145 self.bin_height,
146 Dimension::with_id(0, self.bin_width, dh as i32, 0)
147 ));
148 self.bin_height += dh as i32;
149 }
150 }
151
152 fn shrink(&mut self, binary: bool) {
153 if self.rects_used.is_empty() {
154 return;
155 }
156
157 let mut min_x = i32::MAX;
158 let mut min_y = i32::MAX;
159 let mut max_x = i32::MIN;
160 let mut max_y = i32::MIN;
161
162 for rect in &self.rects_used {
164 min_x = min_x.min(rect.x_total());
165 min_y = min_y.min(rect.y_total());
166 max_x = max_x.max(rect.x_total() + rect.width_total());
167 max_y = max_y.max(rect.y_total() + rect.height_total());
168 }
169
170 let mut new_width = max_x - min_x;
171 let mut new_height = max_y - min_y;
172
173 if binary {
174 let mut cur_width = self.bin_width;
176 while new_width <= (cur_width >> 1) {
177 cur_width >>= 1;
178 }
179 new_width = cur_width;
180
181 let mut cur_height = self.bin_height;
182 while new_height <= (cur_height >> 1) {
183 cur_height >>= 1;
184 }
185 new_height = cur_height;
186 }
187
188 if new_width != self.bin_width || new_height != self.bin_height {
190 if min_x > 0 || min_y > 0 {
191 for rect in &mut self.rects_used {
192 rect.set_x_total(rect.x_total() - min_x);
193 rect.set_y_total(rect.y_total() - min_y);
194 }
195 for rect in &mut self.rects_free {
196 rect.set_x_total(rect.x_total() - min_x);
197 rect.set_y_total(rect.y_total() - min_y);
198 }
199 for rect in &mut self.new_rects_free {
200 rect.set_x_total(rect.x_total() - min_x);
201 rect.set_y_total(rect.y_total() - min_y);
202 }
203 }
204
205 self.bin_width = new_width;
206 self.bin_height = new_height;
207 }
208 }
209
210 fn insert(&mut self, dim: &Dimension) -> Option<Rectangle> {
211 self.insert(dim, self.default_heuristic)
212 }
213
214 fn insert_list(&mut self, nodes: &[Dimension]) -> (Vec<Rectangle>, Vec<Dimension>) {
215 self.insert_list(nodes, self.default_heuristic)
216 }
217
218 fn occupancy(&self) -> f32 {
219 if self.bin_width == 0 || self.bin_height == 0 {
220 return 0.0;
221 }
222
223 let area: i64 = self.rects_used.iter().map(|r| r.dim().area()).sum();
224
225 area as f32 / (self.bin_width * self.bin_height) as f32
226 }
227
228 fn as_slice(&self) -> &[Rectangle] {
229 &self.rects_used
230 }
231
232 fn is_empty(&self) -> bool {
233 self.rects_used.is_empty()
234 }
235
236 fn len(&self) -> usize {
237 self.rects_used.len()
238 }
239
240 fn iter(&self) -> Iter<'_, Rectangle> {
241 self.rects_used.iter()
242 }
243
244 fn find_by_id(&self, id: isize) -> Option<Rectangle> {
245 self.rects_used
246 .iter()
247 .find(|&n| n.dim().id() == id)
248 .map(|r| r.to_owned())
249 }
250
251 fn visualize(&self) -> String {
252 if let Some(output) = visualize_bin(self.bin_width, self.bin_height, &self.rects_used) {
253 output
254 } else {
255 format!("{self}")
256 }
257 }
258}
259
260impl MaxRectsBin {
261 pub fn new(width: i32, height: i32) -> Self {
265 Self::with_capacity(width, height, 4)
266 }
267
268 pub fn with_capacity(width: i32, height: i32, capacity: usize) -> Self {
273 let mut result = Self {
274 bin_width: width.max(1),
275 bin_height: height.max(1),
276 rects_used: Vec::with_capacity(capacity.max(4)),
277 rects_free: Vec::with_capacity((capacity * 4).max(4 * 4)),
278 new_rects_free_size: 0,
279 new_rects_free: Vec::new(),
280 default_heuristic: Heuristic::BestShortSideFit,
281 };
282 result.rects_free.push(Rectangle::new(
283 0,
284 0,
285 Dimension::with_id(0, result.bin_width, result.bin_height, 0),
286 ));
287
288 result
289 }
290
291 pub fn default_rule(&self) -> Heuristic {
297 self.default_heuristic
298 }
299
300 pub fn set_default_rule(&mut self, rule: Heuristic) {
306 self.default_heuristic = rule;
307 }
308
309 pub fn insert(&mut self, dim: &Dimension, rule: Heuristic) -> Option<Rectangle> {
318 if dim.is_empty()
320 || dim.width_total() > self.bin_width
321 || dim.height_total() > self.bin_height
322 {
323 return None;
324 }
325
326 let (_, _, result) = match rule {
327 Heuristic::BestShortSideFit => self.find_bssf(dim),
328 Heuristic::BestLongSideFit => self.find_blsf(dim),
329 Heuristic::BestAreaFit => self.find_baf(dim),
330 Heuristic::BottomLeftRule => self.find_blr(dim),
331 Heuristic::ContactPointRule => self.find_cpr(dim),
332 };
333
334 if let Some(new_node) = &result {
335 self.place_rect(new_node);
336
337 Some(*new_node)
338 } else {
339 None
340 }
341 }
342
343 pub fn insert_list(
357 &mut self,
358 nodes: &[Dimension],
359 rule: Heuristic,
360 ) -> (Vec<Rectangle>, Vec<Dimension>) {
361 let mut inserted = Vec::with_capacity(nodes.len());
362 let mut rejected = nodes.to_vec();
363
364 while !rejected.is_empty() {
365 let mut best_score1 = i32::MAX;
366 let mut best_score2 = i32::MAX;
367 let mut best_index = None;
368 let mut best_node = None;
369
370 for (i, dim) in rejected.iter().enumerate() {
371 let (score1, score2, new_node) = self.score_rect(dim, rule);
372
373 if score1 < best_score1 || (score1 == best_score1 && score2 < best_score2) {
374 best_score1 = score1;
375 best_score2 = score2;
376 best_index = Some(i);
377 best_node = new_node;
378 }
379 }
380
381 if best_index.is_none() {
382 break;
383 }
384
385 debug_assert!(best_node.is_some());
386
387 self.place_rect(&best_node.unwrap());
388 inserted.push(best_node.unwrap());
389 rejected.swap_remove(best_index.unwrap());
390 }
391
392 (inserted, rejected)
393 }
394
395 fn score_rect(&self, dim: &Dimension, rule: Heuristic) -> (i32, i32, Option<Rectangle>) {
400 let (mut score1, mut score2, new_node) = match rule {
401 Heuristic::BestShortSideFit => self.find_bssf(dim),
402 Heuristic::BestLongSideFit => self.find_blsf(dim),
403 Heuristic::BestAreaFit => self.find_baf(dim),
404 Heuristic::BottomLeftRule => self.find_blr(dim),
405 Heuristic::ContactPointRule => self.find_cpr(dim),
406 };
407
408 if new_node.is_none() {
410 score1 = i32::MAX;
411 score2 = i32::MAX;
412 }
413
414 (score1, score2, new_node)
415 }
416
417 fn place_rect(&mut self, rect: &Rectangle) {
419 let mut idx = 0usize;
420 while idx < self.rects_free.len() {
421 let node = self.rects_free[idx];
422 if self.split_free_node(&node, rect) {
423 self.rects_free.swap_remove(idx);
424 continue;
425 }
426 idx += 1;
427 }
428
429 self.prune_free_list();
430
431 self.rects_used.push(rect.to_owned());
432 }
433
434 fn find_blr(&self, dim: &Dimension) -> (i32, i32, Option<Rectangle>) {
436 let mut result = None;
437
438 let mut best_x = i32::MAX;
439 let mut best_y = i32::MAX;
440 for rect in &self.rects_free {
441 if rect.width_total() >= dim.width_total() && rect.height_total() >= dim.height_total()
442 {
443 let top_y = rect.y_total() + dim.height_total();
444
445 if top_y < best_y || (top_y == best_y && rect.x_total() < best_x) {
446 let best_node = result.get_or_insert_with(|| Rectangle::new(0, 0, *dim));
447 best_node.set_location_total(rect.x_total(), rect.y_total());
448 best_node.dim_mut().set_dimension(dim.width(), dim.height());
449 best_x = rect.x_total();
450 best_y = top_y;
451 }
452 }
453 }
454
455 (best_y, best_x, result)
456 }
457
458 fn find_bssf(&self, dim: &Dimension) -> (i32, i32, Option<Rectangle>) {
460 let mut result = None;
461
462 let mut best_short_side_fit = i32::MAX;
463 let mut best_long_size_fit = i32::MAX;
464 for rect in &self.rects_free {
465 if rect.width_total() >= dim.width_total() && rect.height_total() >= dim.height_total()
466 {
467 let leftover_h = rect.width_total().abs_diff(dim.width_total()) as i32;
468 let leftover_v = rect.height_total().abs_diff(dim.height_total()) as i32;
469 let short_side_fit = leftover_h.min(leftover_v);
470 let long_side_fit = leftover_h.max(leftover_v);
471
472 if short_side_fit < best_short_side_fit
473 || (short_side_fit == best_short_side_fit && long_side_fit < best_long_size_fit)
474 {
475 let best_node = result.get_or_insert_with(|| Rectangle::new(0, 0, *dim));
476 best_node.set_location_total(rect.x_total(), rect.y_total());
477 best_node.dim_mut().set_dimension(dim.width(), dim.height());
478 best_short_side_fit = short_side_fit;
479 best_long_size_fit = long_side_fit;
480 }
481 }
482 }
483
484 (best_short_side_fit, best_long_size_fit, result)
485 }
486
487 fn find_blsf(&self, dim: &Dimension) -> (i32, i32, Option<Rectangle>) {
489 let mut result = None;
490
491 let mut best_short_side_fit = i32::MAX;
492 let mut best_long_size_fit = i32::MAX;
493 for rect in &self.rects_free {
494 if rect.width_total() >= dim.width_total() && rect.height_total() >= dim.height_total()
495 {
496 let leftover_h = rect.width_total().abs_diff(dim.width_total()) as i32;
497 let leftover_v = rect.height_total().abs_diff(dim.height_total()) as i32;
498 let short_side_fit = leftover_h.min(leftover_v);
499 let long_side_fit = leftover_h.max(leftover_v);
500
501 if long_side_fit < best_long_size_fit
502 || (long_side_fit == best_long_size_fit && short_side_fit < best_short_side_fit)
503 {
504 let best_node = result.get_or_insert_with(|| Rectangle::new(0, 0, *dim));
505 best_node.set_location_total(rect.x_total(), rect.y_total());
506 best_node.dim_mut().set_dimension(dim.width(), dim.height());
507 best_short_side_fit = short_side_fit;
508 best_long_size_fit = long_side_fit;
509 }
510 }
511 }
512
513 (best_long_size_fit, best_short_side_fit, result)
514 }
515
516 fn find_baf(&self, dim: &Dimension) -> (i32, i32, Option<Rectangle>) {
518 let mut result = None;
519
520 let mut best_area_fit = i64::MAX;
521 let mut best_short_side_fit = i64::MAX;
522 for rect in &self.rects_free {
524 if rect.width_total() >= dim.width_total() && rect.height_total() >= dim.height_total()
525 {
526 let leftover_h = rect.width_total().abs_diff(dim.width_total());
527 let leftover_v = rect.height_total().abs_diff(dim.height_total());
528 let short_side_fit = leftover_h.min(leftover_v) as i64;
529
530 let area_fit = rect.dim().area_total() - dim.area_total();
531 if area_fit < best_area_fit
532 || (area_fit == best_area_fit && short_side_fit < best_short_side_fit)
533 {
534 let best_node = result.get_or_insert_with(|| Rectangle::new(0, 0, *dim));
535 best_node.set_location_total(rect.x_total(), rect.y_total());
536 best_node.dim_mut().set_dimension(dim.width(), dim.height());
537 best_area_fit = area_fit;
538 best_short_side_fit = short_side_fit;
539 }
540 }
541 }
542
543 (best_area_fit as i32, best_short_side_fit as i32, result)
544 }
545
546 fn find_cpr(&self, dim: &Dimension) -> (i32, i32, Option<Rectangle>) {
548 let mut result = None;
549
550 let mut best_score = -1;
551 for rect in &self.rects_free {
552 if rect.width_total() >= dim.width_total() && rect.height_total() >= dim.height_total()
553 {
554 let score = self.contact_point_score_node(
555 rect.x_total(),
556 rect.y_total(),
557 dim.width_total(),
558 dim.height_total(),
559 );
560 if score > best_score {
561 let best_node = result.get_or_insert_with(|| Rectangle::new(0, 0, *dim));
562 best_node.set_location_total(rect.x_total(), rect.y_total());
563 best_node.dim_mut().set_dimension(dim.width(), dim.height());
564 best_score = score;
565 }
566 }
567 }
568
569 best_score = -best_score;
571
572 (best_score, 0, result)
574 }
575
576 fn contact_point_score_node(&self, x: i32, y: i32, width: i32, height: i32) -> i32 {
578 let mut score = 0;
579
580 if x == 0 || x + width == self.bin_width {
581 score += height;
582 }
583 if y == 0 || y + height == self.bin_height {
584 score += width;
585 }
586
587 for rect in &self.rects_used {
588 if rect.x_total() == x + width || rect.x_total() + rect.width_total() == x {
589 score += Self::common_interval_length(
590 rect.y_total(),
591 rect.y_total() + rect.height_total(),
592 y,
593 y + height,
594 );
595 }
596 if rect.y_total() == y + height || rect.y_total() + rect.height_total() == y {
597 score += Self::common_interval_length(
598 rect.x_total(),
599 rect.x_total() + rect.width_total(),
600 x,
601 x + width,
602 );
603 }
604 }
605
606 score
607 }
608
609 fn split_free_node(&mut self, free: &Rectangle, used: &Rectangle) -> bool {
611 if used.x_total() >= free.x_total() + free.width_total()
613 || used.x_total() + used.width_total() <= free.x_total()
614 || used.y_total() >= free.y_total() + free.height_total()
615 || used.y_total() + used.height_total() <= free.y_total()
616 {
617 return false;
618 }
619
620 self.new_rects_free_size = self.new_rects_free.len();
624
625 if used.x_total() < free.x_total() + free.width_total()
626 && used.x_total() + used.width_total() > free.x_total()
627 {
628 if used.y_total() > free.y_total()
630 && used.y_total() < free.y_total() + free.height_total()
631 {
632 let mut new_node = free.to_owned();
633 let new_y = new_node.y_total();
634 new_node.dim_mut().set_height(used.y_total() - new_y);
635 self.insert_new_free_rect(&new_node);
636 }
637
638 if used.y_total() + used.height_total() < free.y_total() + free.height_total() {
640 let mut new_node = free.to_owned();
641 new_node.set_y_total(used.y_total() + used.height_total());
642 new_node.dim_mut().set_height(
643 free.y_total() + free.height_total() - (used.y_total() + used.height_total()),
644 );
645 self.insert_new_free_rect(&new_node);
646 }
647 }
648
649 if used.y_total() < free.y_total() + free.height_total()
650 && used.y_total() + used.height_total() > free.y_total()
651 {
652 if used.x_total() > free.x_total()
654 && used.x_total() < free.x_total() + free.width_total()
655 {
656 let mut new_node = free.to_owned();
657 let new_x = new_node.x_total();
658 new_node.dim_mut().set_width(used.x_total() - new_x);
659 self.insert_new_free_rect(&new_node);
660 }
661
662 if used.x_total() + used.width_total() < free.x_total() + free.width_total() {
664 let mut new_node = free.to_owned();
665 new_node.set_x_total(used.x_total() + used.width_total());
666 new_node.dim_mut().set_width(
667 free.x_total() + free.width_total() - (used.x_total() + used.width_total()),
668 );
669 self.insert_new_free_rect(&new_node);
670 }
671 }
672
673 true
674 }
675
676 fn insert_new_free_rect(&mut self, new_node: &Rectangle) {
677 debug_assert!(new_node.width_total() > 0);
678 debug_assert!(new_node.height_total() > 0);
679
680 let mut i = 0usize;
681 while i < self.new_rects_free_size {
682 let cur_node = &self.new_rects_free[i];
683
684 if cur_node.contains_total(new_node) {
686 return;
687 }
688
689 if new_node.contains_total(cur_node) {
691 self.new_rects_free_size -= 1;
695 self.new_rects_free[i] = self.new_rects_free[self.new_rects_free_size];
696 self.new_rects_free.swap_remove(self.new_rects_free_size);
697 } else {
698 i += 1;
699 }
700 }
701 self.new_rects_free.push(new_node.to_owned());
702 }
703
704 fn prune_free_list(&mut self) {
706 for rect in &self.rects_free {
707 self.new_rects_free.retain(|r| !rect.contains_total(r));
708 }
709
710 self.rects_free.append(&mut self.new_rects_free);
730
731 #[cfg(debug_assertions)]
732 for (i, rect1) in self.rects_free.iter().enumerate() {
733 for rect2 in self.rects_free.iter().skip(i + 1) {
734 debug_assert!(!rect1.contains_total(rect2));
735 debug_assert!(!rect2.contains_total(rect1));
736 }
737 }
738 }
739
740 fn common_interval_length(i1start: i32, i1end: i32, i2start: i32, i2end: i32) -> i32 {
742 if i1end < i2start || i2end < i1start {
743 0
744 } else {
745 i1end.min(i2end) - i1start.max(i2start)
746 }
747 }
748}
749
750impl<Idx> std::ops::Index<Idx> for MaxRectsBin
751where
752 Idx: std::slice::SliceIndex<[Rectangle]>,
753{
754 type Output = Idx::Output;
755
756 fn index(&self, index: Idx) -> &Self::Output {
757 &self.rects_used[index]
758 }
759}
760
761impl Display for MaxRectsBin {
762 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
763 write!(
764 f,
765 "Bin(width: {}, height: {}, rectangles: {})",
766 self.bin_width,
767 self.bin_height,
768 self.rects_used.len()
769 )
770 }
771}
772
773pub fn pack_bins(
806 nodes: &[Dimension],
807 bin_width: i32,
808 bin_height: i32,
809 rule: Heuristic,
810 optimized: bool,
811) -> Result<Vec<MaxRectsBin>, BinError> {
812 if optimized {
813 pack_bins_list(nodes, bin_width, bin_height, rule)
814 } else {
815 pack_bins_single(nodes, bin_width, bin_height, rule)
816 }
817}
818
819fn pack_bins_list(
821 nodes: &[Dimension],
822 bin_width: i32,
823 bin_height: i32,
824 rule: Heuristic,
825) -> Result<Vec<MaxRectsBin>, BinError> {
826 let mut bins = Vec::new();
827 if nodes.is_empty() || bin_width == 0 || bin_height == 0 {
828 return Ok(bins);
829 }
830
831 let mut bin = MaxRectsBin::new(bin_width, bin_height);
833 let (inserted, mut rejected) = bin.insert_list(nodes, rule);
834
835 if inserted.is_empty() && !rejected.is_empty() {
836 rejected.clear();
838 }
839
840 if !inserted.is_empty() {
841 bins.push(bin);
842 }
843
844 let mut nodes_left = rejected;
846 while !nodes_left.is_empty() {
847 let mut bin = MaxRectsBin::new(bin_width, bin_height);
848 let (inserted, mut rejected) = bin.insert_list(&nodes_left, rule);
849
850 if inserted.is_empty() && !rejected.is_empty() {
851 let result = rejected
853 .iter()
854 .map(|r| {
855 if r.width_total() == 0 || r.height_total() == 0 {
856 BinError::ItemTooSmall
857 } else if r.width_total() > bin_width || r.height_total() > bin_height {
858 BinError::ItemTooBig
859 } else {
860 BinError::Unspecified
861 }
862 })
863 .next();
864 if let Some(result) = result {
865 return Err(result);
866 } else {
867 eprintln!("pack_bins(): Could not insert remaining items");
869 rejected.clear();
870 }
871 }
872
873 if !inserted.is_empty() {
874 bins.push(bin);
875 }
876
877 nodes_left.clear();
879 nodes_left.append(&mut rejected);
880 }
881
882 Ok(bins)
883}
884
885fn pack_bins_single(
887 nodes: &[Dimension],
888 bin_width: i32,
889 bin_height: i32,
890 rule: Heuristic,
891) -> Result<Vec<MaxRectsBin>, BinError> {
892 let mut bins = Vec::new();
893 if nodes.is_empty() || bin_width == 0 || bin_height == 0 {
894 return Ok(bins);
895 }
896
897 for node in nodes {
898 if node.is_empty() {
899 return Err(BinError::ItemTooSmall);
900 } else if node.width_total() > bin_width || node.height_total() > bin_height {
901 return Err(BinError::ItemTooBig);
902 }
903
904 let mut inserted = false;
906 for bin in &mut bins {
907 if bin.insert(node, rule).is_some() {
908 inserted = true;
909 break;
910 }
911 }
912
913 if !inserted {
915 bins.push(MaxRectsBin::new(bin_width, bin_height));
916 if let Some(bin) = bins.last_mut() {
917 bin.insert(node, rule)
918 .expect("Object should fit into the bin");
919 }
920 }
921 }
922
923 Ok(bins)
924}
925
926#[cfg(test)]
927mod tests;