public void Insert(List <LORect> rects, List <LORect> dst, FreeRectChoiceHeuristic method) { dst.Clear(); while (rects.Count > 0) { int bestScore1 = int.MaxValue; int bestScore2 = int.MaxValue; int bestRectIndex = -1; LORect bestNode = new LORect(); for (int i = 0; i < rects.Count; ++i) { int score1 = 0; int score2 = 0; LORect newNode = ScoreRect((int)rects[i].width, (int)rects[i].height, method, ref score1, ref score2); if (score1 < bestScore1 || (score1 == bestScore1 && score2 < bestScore2)) { bestScore1 = score1; bestScore2 = score2; bestNode = newNode; bestRectIndex = i; } } if (bestRectIndex == -1) { return; } PlaceRect(bestNode); rects.RemoveAt(bestRectIndex); } }
LORect FindPositionForNewNodeBestAreaFit(int width, int height, ref int bestAreaFit, ref int bestShortSideFit) { LORect bestNode = new LORect(); bestAreaFit = int.MaxValue; for (int i = 0; i < freeRectangles.Count; ++i) { int areaFit = (int)freeRectangles[i].width * (int)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 = Mathf.Abs((int)freeRectangles[i].width - width); int leftoverVert = Mathf.Abs((int)freeRectangles[i].height - height); int shortSideFit = Mathf.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; } } } return(bestNode); }
LORect FindPositionForNewNodeBottomLeft(int width, int height, ref int bestY, ref int bestX) { LORect bestNode = new LORect(); bestY = int.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 = (int)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 = (int)freeRectangles[i].x; } } } return(bestNode); }
LORect ScoreRect(int width, int height, FreeRectChoiceHeuristic method, ref int score1, ref int score2) { LORect newNode = new LORect(); score1 = int.MaxValue; score2 = int.MaxValue; 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); score1 = -score1; // Reverse since we are minimizing, but for contact point score bigger is better. 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.height == 0) { score1 = int.MaxValue; score2 = int.MaxValue; } return(newNode); }
bool Intersects(LORect r1, LORect r2) { return(!( r2.x >= r1.x + r1.width || r2.x + r2.width <= r1.x || r2.y + r2.height <= r1.y || r2.y >= r1.y + r1.height )); }
bool SplitFreeNode(LORect freeNode, ref LORect 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) { LORect newNode = freeNode; 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) { LORect newNode = freeNode; 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) { LORect newNode = freeNode; 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) { LORect newNode = freeNode; newNode.x = usedNode.x + usedNode.width; newNode.width = freeNode.x + freeNode.width - (usedNode.x + usedNode.width); freeRectangles.Add(newNode); } } return(true); }
public void Init(int width, int height, bool rotations = true) { binWidth = width; binHeight = height; LORect n = new LORect(); n.x = 0; n.y = 0; n.width = width; n.height = height; usedRectangles.Clear(); freeRectangles.Clear(); freeRectangles.Add(n); }
public bool Insert(int width, int height, FreeRectChoiceHeuristic method, RectTransform rt, out LORect packedRect) { LORect newNode = new LORect(); 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; } newNode.rectTransform = rt; if (newNode.height == 0) { packedRect = newNode; return(false); } int numRectanglesToProcess = freeRectangles.Count; for (int i = 0; i < numRectanglesToProcess; ++i) { if (SplitFreeNode(freeRectangles[i], ref newNode)) { freeRectangles.RemoveAt(i); --i; --numRectanglesToProcess; } } PruneFreeList(); usedRectangles.Add(newNode); packedRect = newNode; return(true); }
void PlaceRect(LORect node) { int numRectanglesToProcess = freeRectangles.Count; for (int i = 0; i < numRectanglesToProcess; ++i) { if (SplitFreeNode(freeRectangles[i], ref node)) { freeRectangles.RemoveAt(i); --i; --numRectanglesToProcess; } } PruneFreeList(); usedRectangles.Add(node); }
LORect FindPositionForNewNodeContactPoint(int width, int height, ref int bestContactScore) { LORect bestNode = new LORect(); 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((int)freeRectangles[i].x, (int)freeRectangles[i].y, width, height); if (score > bestContactScore) { bestNode.x = (int)freeRectangles[i].x; bestNode.y = (int)freeRectangles[i].y; bestNode.width = width; bestNode.height = height; bestContactScore = score; } } } return(bestNode); }
public void ExpandRectsToFill(float w, float h) { // Run through all rects; make them the full width // find any other intersecting rects, mind our min X and max X for (int i = 0; i < usedRectangles.Count; i++) { LORect a = usedRectangles [i]; float centerX = a.x + a.width * 0.5f; float minX = 0; float maxX = w; a.x = 0; a.width = w; for (int j = 0; j < usedRectangles.Count; j++) { if (i == j) { continue; } LORect b = usedRectangles [j]; if (Intersects(a, b)) { // find a left edge which is > centerX && < maxX if (b.x > centerX && b.x < maxX) { maxX = b.x; } // find a right edge which is < centerX && > minX if ((b.x + b.width) < centerX && (b.x + b.width) > minX) { minX = (b.x + b.width); } } } a.x = minX; a.width = maxX - minX; a.rectTransform.sizeDelta = new Vector2(a.width, a.rectTransform.sizeDelta.y); usedRectangles [i] = a; } // Same thing as above, but do it for the Y axis for (int i = 0; i < usedRectangles.Count; i++) { LORect a = usedRectangles [i]; float centerY = a.y + a.height * 0.5f; float minY = 0; float maxY = h; a.y = 0; a.height = h; for (int j = 0; j < usedRectangles.Count; j++) { if (i == j) { continue; } LORect b = usedRectangles [j]; if (Intersects(a, b)) { // find a top edge which is > centerY && < maxY if (b.y > centerY && b.y < maxY) { maxY = b.y; } // find a bottom edge which is < centerY && > minY if ((b.y + b.height) < centerY && (b.y + b.height) > minY) { minY = (b.y + b.height); } } } a.y = minY; a.height = maxY - minY; a.rectTransform.sizeDelta = new Vector2(a.rectTransform.sizeDelta.x, a.height); usedRectangles [i] = a; } }
bool IsContainedIn(LORect a, LORect 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); }