rectangle-pack Actions Status docs

A general purpose, deterministic bin packer designed to conform to any two or three dimensional use case.

rectangle-pack is a library focused on laying out any number of smaller rectangles (both 2d rectangles and 3d rectangular prisms) inside any number of larger rectangles.

rectangle-pack exposes an API that gives the consumer control over how rectangles are packed - allowing them to tailor the packing to their specific use case.

While rectangle-pack was originally designed with texture atlas related use cases in mind - the library itself has no notions of images and can be used in any rectangle packing context.

Quickstart

```

In your Cargo.toml

rectangle-pack = "0.1" ```

```rust //! A basic example of packing rectangles into target bins

use rectanglepack::{ GroupedRectsToPlace, RectToInsert, packrects, TargetBin, volumeheuristic, containssmallest_box }; use std::collections::BTreeMap;

// A rectangle ID just needs to meet these trait bounds (ideally also Copy). // So you could use a String, PathBuf, or any other type that meets these // trat bounds. You do not have to use a custom enum.

[derive(Debug, Hash, PartialEq, Eq, Clone, Ord, PartialOrd)]

enum MyCustomRectId { RectOne, RectTwo, RectThree, }

// A target bin ID just needs to meet these trait bounds (ideally also Copy) // So you could use a u32, &str, or any other type that meets these // trat bounds. You do not have to use a custom enum.

[derive(Debug, Hash, PartialEq, Eq, Clone, Ord, PartialOrd)]

enum MyCustomBinId { DestinationBinOne, DestinationBinTwo, }

// A placement group just needs to meet these trait bounds (ideally also Copy). // // Groups allow you to ensure that a set of rectangles will be placed // into the same bin. If this isn't possible an error is returned. // // Groups are optional. // // You could use an i32, &'static str, or any other type that meets these // trat bounds. You do not have to use a custom enum.

[derive(Debug, Hash, PartialEq, Eq, Clone, Ord, PartialOrd)]

enum MyCustomGroupId { GroupIdOne }

let mut rectstoplace = GroupedRectsToPlace::new(); rectstoplace.pushrect( MyCustomRectId::RectOne, Some(vec![MyCustomGroupId::GroupIdOne]), RectToInsert::new(10, 20, 255) ); rectstoplace.pushrect( MyCustomRectId::RectTwo, Some(vec![MyCustomGroupId::GroupIdOne]), RectToInsert::new(5, 50, 255) ); rectstoplace.push_rect( MyCustomRectId::RectThree, None, RectToInsert::new(30, 30, 255) );

let mut targetbins = BTreeMap::new(); targetbins.insert(MyCustomBinId::DestinationBinOne, TargetBin::new(2048, 2048, 255)); target_bins.insert(MyCustomBinId::DestinationBinTwo, TargetBin::new(4096, 4096, 1020));

// Information about where each MyCustomRectId was placed let rectangleplacements = packrects( &rectstoplace, &mut targetbins, &volumeheuristic, &containssmallestbox ).unwrap(); ```

Full API Documentation

Background / Initial Motivation

Click to show the initial motivation for the library.

In my application I've switched to dynamically placing textures into atlases at runtime instead of in how I previously used an asset compilation step, so some of the problems explained here are now moot.

I still use rectangle-pack to power my runtime texture allocation, though, along with a handful of other strategies depending on the nature of the textures that need to be placed into the atlas.

rectangle-pack knows nothing about textures, so you can use it for any form of bin packing, whether at runtime, during an offline step or any other time you like.

I'm working on a game with some of the following texture atlas requirements (as of March 2020):

These requirements are the initial guiding pillars to design the rectangle-pack API.

The API shouldn't know about the specifics of any of these requirements - it should just provide the bare minimum required to make them possible. We're trying to push as much into user-land as possible and leave rectangle-packs responsibility to not much more than answering:

Given these rectangles that need to be placed, the maximum sizes of the target bins to place them in and some criteria about how to place and how not to place them, where can I put all of these rectangles?

Features

Future Work

The first version of rectangle-pack was designed to meet my own needs.

As such there is functionality that could be useful that was not explored since I did not need it.

Here are some things that could be useful in the future.

Three-Dimensional Incoming Rectangle Rotation

When attempting to place a Rectangle into the smallest available bin section we might want to rotate the rectangle in order to see which orientation produces the best fit.

This could be accomplished by:

  1. The API exposes three booleans for every incoming rectangles, allow_global_x_axis_rotation, allow_global_y_axis_rotation, allow_global_z_axis_rotation.

  2. Let's say all three are enabled. When attempting to place the rectangle/box we should attempt it in all 6 possible orientations and then select the best placement (based on the ComparePotentialContainersFn heuristic).

  3. Return information to the caller about which axis ended up being rotated.

Mutually exclusive groups

An example of this is the ability to ensure that certain rectqngle groups are not placed in the same bins.

Perhaps you have two plates (bins) and two groups of cheese (rectangles), one for Alice and one for Bob.

When packing you want to ensure that these groups of cheese each end up in a different bin since Alice and Bob don't like to share.

Stats on how the bins were packed

Things such as the amount of wasted space - or anything else that would allow the caller to compare the results of different combinations of target bin sizes and heuristics to see which packed the most efficiently.


If you have a use case that isn't supported, go right ahead and open an issue or submit a pull request.

Packing Algorithm

We started with the algorithm described in [rectpack2D] and then made some adjustments in order to support our goal of flexibly supporting all use cases.

In The Wild

Here are some known production users of rectangle-pack.

Contributing

If you have a use case that isn't supported, a question, a patch, or anything else, go right ahead and open an issue or submit a pull request.

To Test

To run the test suite.

```sh

Clone the repository

git clone git@github.com:chinedufn/rectangle-pack.git cd rectangle-pack

Run tests

cargo test ```

See Also