public MaxRectsBinPack(int width, int height, FreeRectChoiceHeuristic method, IComparer<Rect> comparer) { binWidth = width; binHeight = height; Method = method; var n = new Rect(0, 0, width, height); FreeBoxes.Add(n); this.comparer = comparer; }
public int CompareRectShortSide(Rect a, Rect b) { int smallerSideA = Math.Min(a.width, a.height); int smallerSideB = Math.Min(b.width, b.height); if (smallerSideA != smallerSideB) return smallerSideA.CompareTo(smallerSideB); // Tie-break on the larger side. int largerSideA = Math.Max(a.width, a.height); int largerSideB = Math.Max(b.width, b.height); return largerSideA.CompareTo(largerSideB); }
public Rect FindPositionForNewNodeBestAreaFit(int width, int height, ref int bestAreaFit, ref int bestShortSideFit) { Rect bestNode = new Rect(); bestAreaFit = int.MaxValue; foreach (Rect rect in FreeBoxes) { int areaFit = rect.width*rect.height - width*height; // Try to place the rectangle in upright (non-flipped) orientation. if (rect.width >= width && rect.height >= height) { int leftoverHoriz = Math.Abs(rect.width - width); int leftoverVert = Math.Abs(rect.height - height); int shortSideFit = Math.Min(leftoverHoriz, leftoverVert); if (areaFit < bestAreaFit || (areaFit == bestAreaFit && shortSideFit < bestShortSideFit)) { bestNode.x = rect.x; bestNode.y = rect.y; bestNode.width = width; bestNode.height = height; bestShortSideFit = shortSideFit; bestAreaFit = areaFit; } } if (rect.width >= height && rect.height >= width) { int leftoverHoriz = Math.Abs(rect.width - height); int leftoverVert = Math.Abs(rect.height - width); int shortSideFit = Math.Min(leftoverHoriz, leftoverVert); if (areaFit < bestAreaFit || (areaFit == bestAreaFit && shortSideFit < bestShortSideFit)) { bestNode.x = rect.x; bestNode.y = rect.y; bestNode.width = height; bestNode.height = width; bestShortSideFit = shortSideFit; bestAreaFit = areaFit; } } } return bestNode; }
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; }
public Rect(Rect b) : this(b.x, b.y, b.width, b.height) { }
private Rect ScoreRect(int width, int height, FreeRectChoiceHeuristic method, out int score1, out int score2) { Rect newNode = new Rect(); score1 = int.MaxValue; score2 = int.MaxValue; switch (method) { case FreeRectChoiceHeuristic.RectBestShortSideFit: newNode = FindPositionForNewNodeBestShortSideFit(width, height, ref score1, ref score2); 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; } // Cannot fit the current rectangle. if (newNode == null) { score1 = int.MaxValue; score2 = int.MaxValue; } return newNode; }
public 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 = new Rect(freeNode); newNode.height = usedNode.y - newNode.y; FreeBoxes.Add(newNode); } // New node at the bottom side of the used node. if (usedNode.y + usedNode.height < freeNode.y + freeNode.height) { Rect newNode = new Rect(freeNode); newNode.y = usedNode.y + usedNode.height; newNode.height = freeNode.y + freeNode.height - (usedNode.y + usedNode.height); FreeBoxes.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 = new Rect(freeNode); newNode.width = usedNode.x - newNode.x; FreeBoxes.Add(newNode); } // New node at the right side of the used node. if (usedNode.x + usedNode.width < freeNode.x + freeNode.width) { Rect newNode = new Rect(freeNode); newNode.x = usedNode.x + usedNode.width; newNode.width = freeNode.x + freeNode.width - (usedNode.x + usedNode.width); FreeBoxes.Add(newNode); } } return true; }
public void PlaceRect(Rect node) { int numRectanglesToProcess = FreeBoxes.Count; for (int i = 0; i < numRectanglesToProcess; ++i) { if (SplitFreeNode(FreeBoxes[i], node)) { FreeBoxes.RemoveAt(i); --i; --numRectanglesToProcess; } } PruneFreeList(); UsedBoxes.Add(node); }
public bool Insert(List<Rect> rects) { UsedBoxes.Clear(); rects.Sort(comparer); while (rects.Count > 0) { int bestScore1 = int.MaxValue; int bestScore2 = int.MaxValue; int bestRectIndex = -1; Rect bestNode = new Rect(); for (int i = 0; i < rects.Count; ++i) { int score1 = 0; int score2 = 0; Rect newNode = new Rect(ScoreRect(rects[i].width, rects[i].height, Method, out score1, out score2)); if (score1 < bestScore1 || (score1 == bestScore1 && score2 < bestScore2)) { bestScore1 = score1; bestScore2 = score2; bestNode = newNode; bestRectIndex = i; } } if (bestRectIndex == -1) return false; PlaceRect(bestNode); rects.RemoveAt(bestRectIndex); } return true; }
public Rect FindPositionForNewNodeContactPoint(int width, int height, ref int bestContactScore) { Rect bestNode = new Rect(); bestContactScore = -1; for (int i = 0; i < FreeBoxes.Count; ++i) { // Try to place the rectangle in upright (non-flipped) orientation. if (FreeBoxes[i].width >= width && FreeBoxes[i].height >= height) { int score = ContactPointScoreNode(FreeBoxes[i].x, FreeBoxes[i].y, width, height); if (score > bestContactScore) { bestNode.x = FreeBoxes[i].x; bestNode.y = FreeBoxes[i].y; bestNode.width = width; bestNode.height = height; bestContactScore = score; } } if (FreeBoxes[i].width >= height && FreeBoxes[i].height >= width) { int score = ContactPointScoreNode(FreeBoxes[i].x, FreeBoxes[i].y, width, height); if (score > bestContactScore) { bestNode.x = FreeBoxes[i].x; bestNode.y = FreeBoxes[i].y; bestNode.width = height; bestNode.height = width; bestContactScore = score; } } } return bestNode; }
public Rect FindPositionForNewNodeBottomLeft(int width, int height, ref int bestY, ref int bestX) { Rect bestNode = new Rect(); bestY = int.MaxValue; foreach (Rect t in FreeBoxes) { // Try to place the rectangle in upright (non-flipped) orientation. if (t.width >= width && t.height >= height) { int topSideY = t.y + height; if (topSideY < bestY || (topSideY == bestY && t.x < bestX)) { bestNode.x = t.x; bestNode.y = t.y; bestNode.width = width; bestNode.height = height; bestY = topSideY; bestX = t.x; } } if (t.width >= height && t.height >= width) { int topSideY = t.y + width; if (topSideY < bestY || (topSideY == bestY && t.x < bestX)) { bestNode.x = t.x; bestNode.y = t.y; bestNode.width = height; bestNode.height = width; bestY = topSideY; bestX = t.x; } } } return bestNode; }