Skip to content

Commit

Permalink
Add (unimplemented) coalesce_available_sections
Browse files Browse the repository at this point in the history
This method will allow you to combine free sections within the bin.

When working under a tight performance budget you have the ability
to distribute this coalescing over multiple calculations.

I don't need it quite yet, so I'll implement it later. But exposing
it now in case someone else needs it and wants to implement it.
  • Loading branch information
chinedufn committed Apr 28, 2021
1 parent a6ffe1a commit bab4860
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ The API shouldn't know about the specifics of any of these requirements - it sho
- Packing of the same inputs using the same heuristics and the same sized target bins will always lead to the same layout.
- This is useful anywhere that reproducible builds are useful, such as when generating a texture atlas that is meant to be cached based on the hash of the contents.

- Ability to remove placed rectangles and coalesce neighboring free space.

## Future Work

The first version of `rectangle-pack` was designed to meet my own needs.
Expand Down
6 changes: 6 additions & 0 deletions src/target_bin.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::bin_section::BinSection;
use crate::width_height_depth::WidthHeightDepth;

mod coalesce;
mod push_available_bin_section;

/// A bin that we'd like to play our incoming rectangles into
Expand Down Expand Up @@ -34,6 +35,11 @@ impl TargetBin {
}
}

/// The free [`BinSection`]s within the [`TargetBin`] that rectangles can still be placed into.
pub fn available_bin_sections(&self) -> &Vec<BinSection> {
&self.available_bin_sections
}

/// Remove the section that was just split by a placed rectangle.
pub fn remove_filled_section(&mut self, idx: usize) {
self.available_bin_sections.remove(idx);
Expand Down
84 changes: 84 additions & 0 deletions src/target_bin/coalesce.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use crate::TargetBin;
use std::ops::Range;

impl TargetBin {
/// Over time as you use [`TargetBin.push_available_bin_section`] to return remove packed
/// rectangles from the [`TargetBin`], you may end up with neighboring bin sections that can
/// be combined into a larger bin section.
///
/// Combining bin sections in this was is desirable because a larger bin section allows you to
/// place larger rectangles that might not fit into the smaller bin sections.
///
/// In order to coalesce, or combine a bin section with other bin sections, we need to check
/// every other available bin section to see if they are neighbors.
///
/// This means that fully coalescing the entire list of available bin sections is O(n^2) time
/// complexity, where n is the number of available empty sections.
///
/// # Basic Usage
///
/// ```ignore
/// # use rectangle_pack::TargetBin;
/// let target_bin = my_target_bin();
///
/// for idx in 0..target_bin.available_bin_sections().len() {
/// let len = target_bin.available_bin_sections().len();
/// target_bin.coalesce_available_sections(idx, 0..len);
/// }
///
/// # fn my_target_bin () -> TargetBin {
/// # TargetBin::new(1, 2, 3)
/// # }
/// ```
///
/// # Distributing the Workload
///
/// It is possible that you are developing an application that can in some cases have a lot of
/// heavily fragmented bins that need to be coalesced. If your application has a tight
/// performance budget, such as a real time simulation, you may not want to do all of your
/// coalescing at once.
///
/// This method allows you to split the work over many frames by giving you fine grained control
/// over which bin sections is getting coalesced and which other bin sections it gets tested
/// against.
///
/// So, for example, say you have an application where you want to fully coalesce the entire
/// bin every ten seconds, and you are running at 60 frames per second. You would then
/// distribute the coalescing work such that it would take 600 calls to compare every bin
/// section.
///
/// Here's a basic eample of splitting the work.
///
/// ```ignore
/// # use rectangle_pack::TargetBin;
/// let target_bin = my_target_bin();
///
/// let current_frame: usize = get_current_frame() % 600;
///
/// for idx in 0..target_bin.available_bin_sections().len() {
/// let len = target_bin.available_bin_sections().len();
///
/// let start = len / 600 * current_frame;
/// let end = start + len / 600;
///
/// target_bin.coalesce_available_sections(idx, start..end);
/// }
///
/// # fn my_target_bin () -> TargetBin {
/// # TargetBin::new(1, 2, 3)
/// # }
/// #
/// # fn get_current_frame () -> usize {
/// # 0
/// # }
/// ```
///
/// [`TargetBin.push_available_bin_section`]: #method.push_available_bin_section
// TODO: Write tests, implement then remove the "ignore" from the examples above.
// Tests cases should have a rectangle and then a neighbor (above, below, left, right) and
// verify that they get combined, but only if the comparison indices are correct and only if
// the neighbor has the same width (uf above/below) or height (if left/right).
pub fn coalesce_available_sections(bin_section_index: usize, compare_to_indices: Range<usize>) {
unimplemented!()
}
}

0 comments on commit bab4860

Please sign in to comment.