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::HashMap;

// A rectangle ID just needs to meet these trait bounds (ideally also Copy)

[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)

[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.

[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, targetbins, &volumeheuristic, &containssmallestbox ).unwrap(); ```

Full API Documentation

Background / Initial Motivation

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.

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