/// <summary> /// Frees the specified old rectangle. /// </summary> /// <param name="oldRectangle">The old rectangle.</param> public void Free(ref Rectangle oldRectangle) { freeRectangles.Add(oldRectangle); }
/// <summary> /// Tries to fit a single rectangle with the specified width and height. /// </summary> /// <param name="width">Width requested.</param> /// <param name="height">Height requested</param> /// <param name="bestRectangle">Fill with the rectangle if it was successfully inserted.</param> /// <returns><c>true</c> if it was successfully inserted.</returns> public bool Insert(int width, int height, ref Rectangle bestRectangle) { return(Insert(width, height, freeRectangles, ref bestRectangle)); }
private static bool Insert(int width, int height, List <Rectangle> freeRectanglesList, ref Rectangle bestRectangle) { // Info on algorithm: http://clb.demon.fi/files/RectangleBinPack.pdf int bestScore = int.MaxValue; int freeRectangleIndex = -1; // Find space for new rectangle for (int i = 0; i < freeRectanglesList.Count; ++i) { var currentFreeRectangle = freeRectanglesList[i]; if (width == currentFreeRectangle.Width && height == currentFreeRectangle.Height) { // Perfect fit bestRectangle.X = currentFreeRectangle.X; bestRectangle.Y = currentFreeRectangle.Y; bestRectangle.Width = width; bestRectangle.Height = height; freeRectangleIndex = i; break; } if (width <= currentFreeRectangle.Width && height <= currentFreeRectangle.Height) { // Can fit inside // Use "BAF" heuristic (best area fit) var score = currentFreeRectangle.Width * currentFreeRectangle.Height - width * height; if (score < bestScore) { bestRectangle.X = currentFreeRectangle.X; bestRectangle.Y = currentFreeRectangle.Y; bestRectangle.Width = width; bestRectangle.Height = height; bestScore = score; freeRectangleIndex = i; } } } // No space could be found if (freeRectangleIndex == -1) { return(false); } var freeRectangle = freeRectanglesList[freeRectangleIndex]; // Choose an axis to split (trying to minimize the smaller area "MINAS") int w = freeRectangle.Width - bestRectangle.Width; int h = freeRectangle.Height - bestRectangle.Height; var splitHorizontal = (bestRectangle.Width * h > w * bestRectangle.Height); // Form the two new rectangles. var bottom = new Rectangle { X = freeRectangle.X, Y = freeRectangle.Y + bestRectangle.Height, Width = splitHorizontal ? freeRectangle.Width : bestRectangle.Width, Height = h }; var right = new Rectangle { X = freeRectangle.X + bestRectangle.Width, Y = freeRectangle.Y, Width = w, Height = splitHorizontal ? bestRectangle.Height : freeRectangle.Height }; if (bottom.Width > 0 && bottom.Height > 0) { freeRectanglesList.Add(bottom); } if (right.Width > 0 && right.Height > 0) { freeRectanglesList.Add(right); } // Remove previously selected freeRectangle if (freeRectangleIndex != freeRectanglesList.Count - 1) { freeRectanglesList[freeRectangleIndex] = freeRectanglesList[freeRectanglesList.Count - 1]; } freeRectanglesList.RemoveAt(freeRectanglesList.Count - 1); return(true); }