static bool IsDisjoint(Rect a, Rect b) { if ((a.x + a.width <= b.x) || (b.x + b.width <= a.x) || (a.y + a.height <= b.y) || (b.y + b.height <= a.y)) return true; return false; }
public Rect Copy() { Rect r = new Rect(); r.x = x; r.y = y; r.width = width; r.height = height; return r; }
bool Disjoint(Rect r) { // Degenerate rectangles are ignored. if (r.width == 0 || r.height == 0) return true; for (int i = 0; i < rects.Count; ++i) if (!IsDisjoint(rects[i], r)) return false; return true; }
public bool Add(Rect r) { // Degenerate rectangles are ignored. if (r.width == 0 || r.height == 0) return true; if (!Disjoint(r)) return false; rects.Add(r); return true; }
/// (Re)initializes the packer to an empty bin of width x height units. Call whenever /// you need to restart with a new bin. public void Init(int width, int height) { binWidth = width; binHeight = height; Rect n = new Rect(); n.x = 0; n.y = 0; n.width = width; n.height = height; usedRectangles.Clear(); freeRectangles.Clear(); freeRectangles.Add(n); }
/// (Re)initializes the packer to an empty bin of width x height units. Call whenever /// you need to restart with a new bin. private void Init(int width, int height, bool allowRotation) { binWidth = width; binHeight = height; this.allowRotation = allowRotation; Rect n = new Rect(); n.x = 0; n.y = 0; n.width = width; n.height = height; usedRectangles.Clear(); freeRectangles.Clear(); freeRectangles.Add(n); }
/// Performs a lexicographic compare on (rect short side, rect long side). /// @return -1 if the smaller side of a is shorter than the smaller side of b, 1 if the other way around. /// If they are equal, the larger side length is used as a tie-breaker. /// If the rectangles are of same size, returns 0. // public static int CompareRectShortSide(Rect a, Rect b); /// Performs a lexicographic compare on (x, y, width, height). // public static int NodeSortCmp(Rect a, Rect b); /// Returns true if a is contained in b. public static bool IsContainedIn(Rect a, Rect b) { return (a.x >= b.x) && (a.y >= b.y) && (a.x + a.width <= b.x + b.width) && (a.y + a.height <= b.y + b.height); }
/// @return True if the free node was split. bool SplitFreeNode(Rect freeNode, Rect usedNode) { // Test with SAT if the rectangles even intersect. if (usedNode.x >= freeNode.x + freeNode.width || usedNode.x + usedNode.width <= freeNode.x || usedNode.y >= freeNode.y + freeNode.height || usedNode.y + usedNode.height <= freeNode.y) return false; if (usedNode.x < freeNode.x + freeNode.width && usedNode.x + usedNode.width > freeNode.x) { // New node at the top side of the used node. if (usedNode.y > freeNode.y && usedNode.y < freeNode.y + freeNode.height) { Rect newNode = freeNode.Copy(); newNode.height = usedNode.y - newNode.y; freeRectangles.Add(newNode); } // New node at the bottom side of the used node. if (usedNode.y + usedNode.height < freeNode.y + freeNode.height) { Rect newNode = freeNode.Copy(); newNode.y = usedNode.y + usedNode.height; newNode.height = freeNode.y + freeNode.height - (usedNode.y + usedNode.height); freeRectangles.Add(newNode); } } if (usedNode.y < freeNode.y + freeNode.height && usedNode.y + usedNode.height > freeNode.y) { // New node at the left side of the used node. if (usedNode.x > freeNode.x && usedNode.x < freeNode.x + freeNode.width) { Rect newNode = freeNode.Copy(); newNode.width = usedNode.x - newNode.x; freeRectangles.Add(newNode); } // New node at the right side of the used node. if (usedNode.x + usedNode.width < freeNode.x + freeNode.width) { Rect newNode = freeNode.Copy(); newNode.x = usedNode.x + usedNode.width; newNode.width = freeNode.x + freeNode.width - (usedNode.x + usedNode.width); freeRectangles.Add(newNode); } } return true; }
/// Places the given rectangle into the bin. void PlaceRect(Rect node) { int numRectanglesToProcess = freeRectangles.Count; for (int i = 0; i < numRectanglesToProcess; ++i) { if (SplitFreeNode(freeRectangles[i], node)) { freeRectangles.RemoveAt(i); --i; --numRectanglesToProcess; } } PruneFreeList(); usedRectangles.Add(node); // dst.push_back(bestNode); ///\todo Refactor so that this compiles. }
Rect FindPositionForNewNodeContactPoint(int width, int height, ref int bestContactScore) { Rect bestNode = new Rect(); bestContactScore = -1; for (int i = 0; i < freeRectangles.Count; ++i) { // Try to place the rectangle in upright (non-flipped) orientation. if (freeRectangles[i].width >= width && freeRectangles[i].height >= height) { int score = ContactPointScoreNode(freeRectangles[i].x, freeRectangles[i].y, width, height); if (score > bestContactScore) { bestNode.x = freeRectangles[i].x; bestNode.y = freeRectangles[i].y; bestNode.width = width; bestNode.height = height; bestContactScore = score; } } if (freeRectangles[i].width >= height && freeRectangles[i].height >= width) { int score = ContactPointScoreNode(freeRectangles[i].x, freeRectangles[i].y, width, height); if (score > bestContactScore) { bestNode.x = freeRectangles[i].x; bestNode.y = freeRectangles[i].y; bestNode.width = height; bestNode.height = width; bestContactScore = score; } } } return bestNode; }
Rect FindPositionForNewNodeBottomLeft(int width, int height, ref int bestY, ref int bestX) { Rect bestNode = new Rect(); // memset(&bestNode, 0, sizeof(Rect)); // done in constructor bestY = Int32.MaxValue; for (int i = 0; i < freeRectangles.Count; ++i) { // Try to place the rectangle in upright (non-flipped) orientation. if (freeRectangles[i].width >= width && freeRectangles[i].height >= height) { int topSideY = freeRectangles[i].y + height; if (topSideY < bestY || (topSideY == bestY && freeRectangles[i].x < bestX)) { bestNode.x = freeRectangles[i].x; bestNode.y = freeRectangles[i].y; bestNode.width = width; bestNode.height = height; bestY = topSideY; bestX = freeRectangles[i].x; } } if (freeRectangles[i].width >= height && freeRectangles[i].height >= width) { int topSideY = freeRectangles[i].y + width; if (topSideY < bestY || (topSideY == bestY && freeRectangles[i].x < bestX)) { bestNode.x = freeRectangles[i].x; bestNode.y = freeRectangles[i].y; bestNode.width = height; bestNode.height = width; bestY = topSideY; bestX = freeRectangles[i].x; } } } return bestNode; }
Rect FindPositionForNewNodeBestShortSideFit(int width, int height, ref int bestShortSideFit, ref int bestLongSideFit) { Rect bestNode = new Rect(); //memset(&bestNode, 0, sizeof(Rect)); bestShortSideFit = Int32.MaxValue; for (int i = 0; i < freeRectangles.Count; ++i) { // Try to place the rectangle in upright (non-flipped) orientation. if (freeRectangles[i].width >= width && freeRectangles[i].height >= height) { int leftoverHoriz = Math.Abs(freeRectangles[i].width - width); int leftoverVert = Math.Abs(freeRectangles[i].height - height); int shortSideFit = Math.Min(leftoverHoriz, leftoverVert); int longSideFit = Math.Max(leftoverHoriz, leftoverVert); if (shortSideFit < bestShortSideFit || (shortSideFit == bestShortSideFit && longSideFit < bestLongSideFit)) { bestNode.x = freeRectangles[i].x; bestNode.y = freeRectangles[i].y; bestNode.width = width; bestNode.height = height; bestShortSideFit = shortSideFit; bestLongSideFit = longSideFit; } } if (freeRectangles[i].width >= height && freeRectangles[i].height >= width) { int flippedLeftoverHoriz = Math.Abs(freeRectangles[i].width - height); int flippedLeftoverVert = Math.Abs(freeRectangles[i].height - width); int flippedShortSideFit = Math.Min(flippedLeftoverHoriz, flippedLeftoverVert); int flippedLongSideFit = Math.Max(flippedLeftoverHoriz, flippedLeftoverVert); if (flippedShortSideFit < bestShortSideFit || (flippedShortSideFit == bestShortSideFit && flippedLongSideFit < bestLongSideFit)) { bestNode.x = freeRectangles[i].x; bestNode.y = freeRectangles[i].y; bestNode.width = height; bestNode.height = width; bestShortSideFit = flippedShortSideFit; bestLongSideFit = flippedLongSideFit; } } } return bestNode; }
/// Inserts a single rectangle into the bin, possibly rotated. public Rect Insert(int width, int height, FreeRectChoiceHeuristic method) { Rect newNode = new Rect(); int score1 = 0; // Unused in this function. We don't need to know the score after finding the position. int score2 = 0; switch (method) { case FreeRectChoiceHeuristic.RectBestShortSideFit: newNode = FindPositionForNewNodeBestShortSideFit(width, height, ref score1, ref score2); break; case FreeRectChoiceHeuristic.RectBottomLeftRule: newNode = FindPositionForNewNodeBottomLeft(width, height, ref score1, ref score2); break; case FreeRectChoiceHeuristic.RectContactPointRule: newNode = FindPositionForNewNodeContactPoint(width, height, ref score1); break; case FreeRectChoiceHeuristic.RectBestLongSideFit: newNode = FindPositionForNewNodeBestLongSideFit(width, height, ref score2, ref score1); break; case FreeRectChoiceHeuristic.RectBestAreaFit: newNode = FindPositionForNewNodeBestAreaFit(width, height, ref score1, ref score2); break; } if (newNode.height == 0) return newNode; int numRectanglesToProcess = freeRectangles.Count; for (int i = 0; i < numRectanglesToProcess; ++i) { if (SplitFreeNode(freeRectangles[i], newNode)) { freeRectangles.RemoveAt(i); --i; --numRectanglesToProcess; } } PruneFreeList(); usedRectangles.Add(newNode); return newNode; }
Rect FindPositionForNewNodeBestAreaFit(int width, int height, ref int bestAreaFit, ref int bestShortSideFit) { Rect bestNode = new Rect(); bestAreaFit = Int32.MaxValue; for (int i = 0; i < freeRectangles.Count; ++i) { int areaFit = freeRectangles[i].width * freeRectangles[i].height - width * height; // Try to place the rectangle in upright (non-flipped) orientation. if (freeRectangles[i].width >= width && freeRectangles[i].height >= height) { int leftoverHoriz = Math.Abs(freeRectangles[i].width - width); int leftoverVert = Math.Abs(freeRectangles[i].height - height); int shortSideFit = Math.Min(leftoverHoriz, leftoverVert); if (areaFit < bestAreaFit || (areaFit == bestAreaFit && shortSideFit < bestShortSideFit)) { bestNode.x = freeRectangles[i].x; bestNode.y = freeRectangles[i].y; bestNode.width = width; bestNode.height = height; bestShortSideFit = shortSideFit; bestAreaFit = areaFit; } } if (allowRotation && freeRectangles[i].width >= height && freeRectangles[i].height >= width) { int leftoverHoriz = Math.Abs(freeRectangles[i].width - height); int leftoverVert = Math.Abs(freeRectangles[i].height - width); int shortSideFit = Math.Min(leftoverHoriz, leftoverVert); if (areaFit < bestAreaFit || (areaFit == bestAreaFit && shortSideFit < bestShortSideFit)) { bestNode.x = freeRectangles[i].x; bestNode.y = freeRectangles[i].y; bestNode.width = height; bestNode.height = width; bestShortSideFit = shortSideFit; bestAreaFit = areaFit; } } } return bestNode; }