shelf-pack
A 2D rectangular bin packing
data structure that uses the Shelf Best Height Fit heuristic.
What is it?
shelf-pack
is a library for packing little rectangles into a big rectangle. This sounds simple enough,
but finding an optimal packing is a problem with NP-Complete
complexity. One useful application of bin packing is to assemble icons or glyphs into a sprite texture.
There are many ways to approach the bin packing problem, but shelf-pack
uses the Shelf Best
Height Fit heuristic. It works by dividing the total space into "shelves", each with a certain height.
The allocator packs rectangles onto whichever shelf minimizes the amount of wasted vertical space.
shelf-pack
is simple, fast, and works best when the rectangles have similar heights (icons and glyphs
are like this). It is not a generalized bin packer, and can potentially waste a lot of space if the
rectangles vary significantly in height.
How fast is it?
Really fast! shelf-pack
is several orders of magnitude faster than the more general
bin-pack
library.
> npm run bench
ShelfPack single allocate fixed size bins x 1,610 ops/sec ±1.21% (90 runs sampled)
ShelfPack single allocate random width bins x 1,475 ops/sec ±1.00% (89 runs sampled)
ShelfPack single allocate random height bins x 1,458 ops/sec ±1.00% (90 runs sampled)
ShelfPack single allocate random height and width bins x 1,346 ops/sec ±0.96% (89 runs sampled)
ShelfPack batch allocate fixed size bins x 1,522 ops/sec ±1.06% (86 runs sampled)
ShelfPack batch allocate random width bins x 1,427 ops/sec ±1.06% (89 runs sampled)
ShelfPack batch allocate random height bins x 1,350 ops/sec ±1.63% (90 runs sampled)
ShelfPack batch allocate random height and width bins x 1,257 ops/sec ±1.02% (89 runs sampled)
BinPack batch allocate fixed size bins x 2.21 ops/sec ±6.60% (10 runs sampled)
BinPack batch allocate random width bins x 0.50 ops/sec ±2.25% (6 runs sampled)
BinPack batch allocate random height bins x 0.51 ops/sec ±1.97% (6 runs sampled)
BinPack batch allocate random height and width bins x 0.51 ops/sec ±1.37% (6 runs sampled)
Usage
Basic Usage
var ShelfPack = require('@mapbox/shelf-pack');
var sprite = new ShelfPack(64, 64);
for (var i = 0; i < 5; i++) {
var bin = sprite.packOne(32, 32);
console.log(bin || 'out of space');
}
sprite.clear();
sprite.resize(128, 128);
Batch packing
var ShelfPack = require('@mapbox/shelf-pack');
var sprite = new ShelfPack(10, 10, { autoResize: true });
var requests = [
{ id: 'a', width: 10, height: 10 },
{ id: 'b', width: 10, height: 12 },
{ id: 'c', w: 10, h: 12 },
{ id: 'd', w: 10, h: 10 }
];
var results = sprite.pack(requests);
results.forEach(function(bin) {
console.log(bin);
});
var myBins = [
{ id: 'e', width: 12, height: 24 },
{ id: 'f', width: 12, height: 12 },
{ id: 'g', w: 10, h: 10 }
];
sprite.pack(myBins, { inPlace: true });
myBins.forEach(function(bin) {
console.log(bin);
});
Reference Counting
var ShelfPack = require('@mapbox/shelf-pack');
var sprite = new ShelfPack(64, 64);
[100, 101, 102].forEach(function(id) {
var bin = sprite.packOne(16, 16, id);
console.log(bin);
});
var bin102 = sprite.packOne(16, 16, 102);
console.log(bin102);
var bin101 = sprite.getBin(101);
sprite.ref(bin101);
console.log(bin101);
var bin100 = sprite.getBin(100);
sprite.unref(bin100);
console.log(bin100);
var bin103 = sprite.packOne(16, 15, 103);
console.log(bin103);
sprite.unref(bin103)
var bin104 = sprite.packOne(16, 16, 104);
console.log(bin104);
Documentation
Complete API documentation is here: http://mapbox.github.io/shelf-pack/
See also
J. Jylänky, "A Thousand Ways to Pack the Bin - A Practical
Approach to Two-Dimensional Rectangle Bin Packing,"
http://clb.demon.fi/files/RectangleBinPack.pdf, 2010