* The type of items to be held */ public class RectanglePacker
{
/**
* Determines the outcome of a rectangle-fitting test
*
* @author ryanm
*/
private static enum Fit {
/**
* Indicates that the rectangle did not fit
*/
FAIL,
/**
* Indicates that the rectangle fitted perfectly
*/
PERFECT,
/**
* Indicates that the rectangle fitted with room to spare
*/
FIT
};
private Node root;
/**
* The border to leave around rectangles
*/
private int border = 0;
/**
* Builds a new {@link RectanglePacker}
*
* @param width
* The width of the space available to pack into
* @param height
* The height of the space available to pack into
* @param border
* The border to preserve between packed items
*/
public RectanglePacker(int width, int height, int border) {
root = new Node(new Rectangle(0, 0, width, height));
this.border = border;
}
/**
* Builds a list of all {@link Rectangle}s in the tree, for debugging
* purposes
*
* @param rectangles
* The list to add the tree's {@link Rectangle}s to
*/
public void inspectRectangles(Listtrue
if the item was found, false otherwise
*/
public boolean remove(P o) {
return root.remove(o);
}
/**
* Gets the width of this packer
*
* @return the width of this packer
*/
public int getWidth() {
return root.rect.width;
}
/**
* Gets the height of this packer
*
* @return The height of this packer
*/
public int getHeight() {
return root.rect.height;
}
private class Node {
private Rectangle rect;
private P occupier = null;
private Node left = null;
private Node right = null;
private Node(Rectangle r) {
this.rect = r;
}
private Rectangle findRectange(P item) {
if (isLeaf()) {
if (item == occupier) {
return rect;
} else {
return null;
}
} else {
Rectangle l = left.findRectange(item);
if (l != null) {
return l;
} else {
return right.findRectange(item);
}
}
}
private Node insert(int width, int height, P o) {
if (!isLeaf()) {
Node r = left.insert(width, height, o);
if (r == null) {
r = right.insert(width, height, o);
}
return r;
} else {
if (occupier != null) {
return null;
}
Fit fit = fits(width, height);
switch (fit) {
case FAIL:
return null;
case PERFECT:
occupier = o;
return this;
case FIT:
split(width, height);
break;
}
return left.insert(width, height, o);
}
}
private boolean isLeaf() {
return left == null;
}
/**
* Determines if this node contains an item, even many levels below
*
* @return true
if this node or any of it's descendants
* holds an item
*/
private boolean isOccupied() {
return occupier != null || !isLeaf();
}
/**
* Removes an item, and consolidates the tree if possible
*
* @param o
* the item to remove
* @return true
if the item was found, false
* otherwise
*/
private boolean remove(P o) {
if (isLeaf()) {
if (occupier == o) {
occupier = null;
return true;
}
return false;
} else {
boolean found = left.remove(o);
if (!found) {
found = right.remove(o);
}
if (found) {
if (!left.isOccupied() && !right.isOccupied()) {
left = null;
right = null;
}
}
return found;
}
}
private void split(int width, int height) {
int dw = rect.width - width;
int dh = rect.height - height;
assert dw >= 0;
assert dh >= 0;
Rectangle r, l;
if (dw > dh) {
l = new Rectangle(rect.x, rect.y, width, rect.height);
r = new Rectangle(l.x + width, rect.y, rect.width - width,
rect.height);
} else {
l = new Rectangle(rect.x, rect.y, rect.width, height);
r = new Rectangle(rect.x, l.y + height, rect.width, rect.height
- height);
}
left = new Node(l);
right = new Node(r);
}
private Fit fits(int width, int height) {
if (width <= rect.width && height <= rect.height) {
if (width == rect.width && height == rect.height) {
return Fit.PERFECT;
} else {
return Fit.FIT;
}
}
return Fit.FAIL;
}
private void getRectangles(List