initial commit.
This commit is contained in:
147
public/javascripts/packer.growing.js
Normal file
147
public/javascripts/packer.growing.js
Normal file
@@ -0,0 +1,147 @@
|
||||
/******************************************************************************
|
||||
|
||||
This is a binary tree based bin packing algorithm that is more complex than
|
||||
the simple Packer (packer.js). Instead of starting off with a fixed width and
|
||||
height, it starts with the width and height of the first block passed and then
|
||||
grows as necessary to accomodate each subsequent block. As it grows it attempts
|
||||
to maintain a roughly square ratio by making 'smart' choices about whether to
|
||||
grow right or down.
|
||||
|
||||
When growing, the algorithm can only grow to the right OR down. Therefore, if
|
||||
the new block is BOTH wider and taller than the current target then it will be
|
||||
rejected. This makes it very important to initialize with a sensible starting
|
||||
width and height. If you are providing sorted input (largest first) then this
|
||||
will not be an issue.
|
||||
|
||||
A potential way to solve this limitation would be to allow growth in BOTH
|
||||
directions at once, but this requires maintaining a more complex tree
|
||||
with 3 children (down, right and center) and that complexity can be avoided
|
||||
by simply chosing a sensible starting block.
|
||||
|
||||
Best results occur when the input blocks are sorted by height, or even better
|
||||
when sorted by max(width,height).
|
||||
|
||||
Inputs:
|
||||
------
|
||||
|
||||
blocks: array of any objects that have .w and .h attributes
|
||||
|
||||
Outputs:
|
||||
-------
|
||||
|
||||
marks each block that fits with a .fit attribute pointing to a
|
||||
node with .x and .y coordinates
|
||||
|
||||
Example:
|
||||
-------
|
||||
|
||||
var blocks = [
|
||||
{ w: 100, h: 100 },
|
||||
{ w: 100, h: 100 },
|
||||
{ w: 80, h: 80 },
|
||||
{ w: 80, h: 80 },
|
||||
etc
|
||||
etc
|
||||
];
|
||||
|
||||
var packer = new GrowingPacker();
|
||||
packer.fit(blocks);
|
||||
|
||||
for(var n = 0 ; n < blocks.length ; n++) {
|
||||
var block = blocks[n];
|
||||
if (block.fit) {
|
||||
Draw(block.fit.x, block.fit.y, block.w, block.h);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
GrowingPacker = function() { };
|
||||
|
||||
GrowingPacker.prototype = {
|
||||
|
||||
fit: function(blocks) {
|
||||
var n, node, block, len = blocks.length;
|
||||
var w = len > 0 ? blocks[0].w : 0;
|
||||
var h = len > 0 ? blocks[0].h : 0;
|
||||
this.root = { x: 0, y: 0, w: w, h: h };
|
||||
for (n = 0; n < len ; n++) {
|
||||
block = blocks[n];
|
||||
if (node = this.findNode(this.root, block.w, block.h))
|
||||
block.fit = this.splitNode(node, block.w, block.h);
|
||||
else
|
||||
block.fit = this.growNode(block.w, block.h);
|
||||
}
|
||||
},
|
||||
|
||||
findNode: function(root, w, h) {
|
||||
if (root.used)
|
||||
return this.findNode(root.right, w, h) || this.findNode(root.down, w, h);
|
||||
else if ((w <= root.w) && (h <= root.h))
|
||||
return root;
|
||||
else
|
||||
return null;
|
||||
},
|
||||
|
||||
splitNode: function(node, w, h) {
|
||||
node.used = true;
|
||||
node.down = { x: node.x, y: node.y + h, w: node.w, h: node.h - h };
|
||||
node.right = { x: node.x + w, y: node.y, w: node.w - w, h: h };
|
||||
return node;
|
||||
},
|
||||
|
||||
growNode: function(w, h) {
|
||||
var canGrowDown = (w <= this.root.w);
|
||||
var canGrowRight = (h <= this.root.h);
|
||||
|
||||
var shouldGrowRight = canGrowRight && (this.root.h >= (this.root.w + w)); // attempt to keep square-ish by growing right when height is much greater than width
|
||||
var shouldGrowDown = canGrowDown && (this.root.w >= (this.root.h + h)); // attempt to keep square-ish by growing down when width is much greater than height
|
||||
|
||||
if (shouldGrowRight)
|
||||
return this.growRight(w, h);
|
||||
else if (shouldGrowDown)
|
||||
return this.growDown(w, h);
|
||||
else if (canGrowRight)
|
||||
return this.growRight(w, h);
|
||||
else if (canGrowDown)
|
||||
return this.growDown(w, h);
|
||||
else
|
||||
return null; // need to ensure sensible root starting size to avoid this happening
|
||||
},
|
||||
|
||||
growRight: function(w, h) {
|
||||
this.root = {
|
||||
used: true,
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: this.root.w + w,
|
||||
h: this.root.h,
|
||||
down: this.root,
|
||||
right: { x: this.root.w, y: 0, w: w, h: this.root.h }
|
||||
};
|
||||
if (node = this.findNode(this.root, w, h))
|
||||
return this.splitNode(node, w, h);
|
||||
else
|
||||
return null;
|
||||
},
|
||||
|
||||
growDown: function(w, h) {
|
||||
this.root = {
|
||||
used: true,
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: this.root.w,
|
||||
h: this.root.h + h,
|
||||
down: { x: 0, y: this.root.h, w: this.root.w, h: h },
|
||||
right: this.root
|
||||
};
|
||||
if (node = this.findNode(this.root, w, h))
|
||||
return this.splitNode(node, w, h);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user