/// Inserts a single rectangle into the bin. The packer might rotate the rectangle, in which case the returned /// struct will have the width and height values swapped. /// @param merge If true, performs free Rectangle Merge procedure after packing the new rectangle. This procedure /// tries to defragment the list of disjoint free rectangles to improve packing performance, but also takes up /// some extra time. /// @param rectChoice The free rectangle choice heuristic rule to use. /// @param splitMethod The free rectangle split heuristic rule to use. public override Rect Insert(RectSize rectSize, GenericOption option) { var opt = option as Option; int width = rectSize.Width; int height = rectSize.Height; // Find where to put the new rectangle. int freeNodeIndex = 0; Rect newRect = FindPositionForNewNode(width, height, opt.FreeRectChoice, ref freeNodeIndex); // Abort if we didn't have enough space in the bin. if (newRect.Height == 0) { return(newRect); } // Remove the space that was just consumed by the new rectangle. SplitFreeRectByHeuristic(freeRectangles[freeNodeIndex], newRect, opt.GuillotineSplit); freeRectangles.RemoveAt(freeNodeIndex); // Perform a Rectangle Merge step if desired. if (opt.Merge) { MergeFreeList(); } // Remember the new used rectangle. UsedRectangles.Add(newRect); // Check that we're really producing correct packings here. disjointRects.Add(newRect); return(newRect); }
public override Rect Insert(RectSize rectSize, GenericOption option) { Rect rect = new Rect(); // There are three cases: // 1. short edge <= long edge <= shelf height. Then store the long edge vertically. // 2. short edge <= shelf height <= long edge. Then store the short edge vertically. // 3. shelf height <= short edge <= long edge. Then store the short edge vertically. // If the long edge of the new rectangle fits vertically onto the current shelf, // flip it. If the short edge is larger than the current shelf height, store // the short edge vertically. int width = rectSize.Width; int height = rectSize.Height; if (((width > height && width < shelfHeight) || (width < height && height > shelfHeight))) { Swap(ref width, ref height); } if (currentX + width > BinWidth) { currentX = 0; currentY += shelfHeight; shelfHeight = 0; // When starting a new shelf, store the new long edge of the new rectangle horizontally // to minimize the new shelf height. if (width < height) { Swap(ref width, ref height); } } // If the rectangle doesn't fit in this orientation, try flipping. if (width > BinWidth || currentY + height > BinHeight) { Swap(ref width, ref height); } // If flipping didn't help, return failure. if (width > BinWidth || currentY + height > BinHeight) { return(rect); } rect.Width = width; rect.Height = height; rect.X = currentX; rect.Y = currentY; currentX += width; shelfHeight = Math.Max(shelfHeight, height); IncrementUsedArea(width * height); return(rect); }
/// Inserts a single rectangle into the bin, possibly rotated. public override Rect Insert(RectSize rect, GenericOption option) { Rect newNode = new Rect(); // Unused in this function. We don't need to know the score after finding the position. int score1 = int.MaxValue; int score2 = int.MaxValue; int width = rect.Width, height = rect.Height; var optionMaxRectsBinPath = option as Option; switch (optionMaxRectsBinPath.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], ref newNode)) { freeRectangles.RemoveAt(i); --i; --numRectanglesToProcess; } } PruneFreeList(); UsedRectangles.Add(newNode); return(newNode); }
/// Inserts a single rectangle into the bin, possibly rotated. public override Rect Insert(RectSize rectSize, GenericOption option) { int width = rectSize.Width; int height = rectSize.Height; // First try to pack this rectangle into the waste map, if it fits. Rect node = wasteMap.Insert(rectSize, new GuillotineBinPack.Option() { Merge = true, FreeRectChoice = GuillotineBinPack.FreeRectChoiceHeuristic.RectBestShortSideFit, GuillotineSplit = GuillotineBinPack.GuillotineSplitHeuristic.SplitMaximizeArea }); Debug.Assert(disjointRects.Disjoint(node)); if (node.Height != 0) { Rect newNode = new Rect(); newNode.X = node.X; newNode.Y = node.Y; newNode.Width = node.Width; newNode.Height = node.Height; IncrementUsedArea(width * height); Debug.Assert(disjointRects.Disjoint(newNode)); disjointRects.Add(newNode); return(newNode); } Option opt = option as Option; switch (opt.Method) { case LevelChoiceHeuristic.LevelBottomLeft: return(InsertBottomLeft(width, height)); case LevelChoiceHeuristic.LevelMinWasteFit: return(InsertMinWaste(width, height)); default: Debug.Assert(false); return(node); } }
/// Inserts a single rectangle into the bin. The packer might rotate the rectangle, in which case the returned /// struct will have the width and height values swapped. /// @param method The heuristic rule to use for choosing a shelf if multiple ones are possible. public override Rect Insert(RectSize rectSize, GenericOption option) { Rect newNode = new Rect(); // First try to pack this rectangle into the waste map, if it fits. if (UseWasteMap) { newNode = wasteMap.Insert(rectSize, new GuillotineBinPack.Option() { Merge = true, FreeRectChoice = GuillotineBinPack.FreeRectChoiceHeuristic.RectBestShortSideFit, GuillotineSplit = GuillotineBinPack.GuillotineSplitHeuristic.SplitMaximizeArea }); if (newNode.Height != 0) { // Track the space we just used. IncrementUsedArea(rectSize.Area); return(newNode); } } int width = rectSize.Width, height = rectSize.Height; Option shelfBinPackOption = option as Option; switch (shelfBinPackOption.Method) { case ShelfChoiceHeuristic.ShelfNextFit: if (FitsOnShelf(shelves.Last(), width, height, true)) { AddToShelf(shelves.Last(), width, height, ref newNode); return(newNode); } break; case ShelfChoiceHeuristic.ShelfFirstFit: for (int i = 0; i < shelves.Count; ++i) { if (FitsOnShelf(shelves[i], width, height, i == shelves.Count - 1)) { AddToShelf(shelves[i], width, height, ref newNode); return(newNode); } } break; case ShelfChoiceHeuristic.ShelfBestAreaFit: { // Best Area Fit rule: Choose the shelf with smallest remaining shelf area. Shelf bestShelf = null; int bestShelfSurfaceArea = int.MaxValue; for (int i = 0; i < shelves.Count; ++i) { // Pre-rotate the rect onto the shelf here already so that the area fit computation // is done correctly. RotateToShelf(shelves[i], ref width, ref height); if (FitsOnShelf(shelves[i], width, height, i == shelves.Count - 1)) { int surfaceArea = (BinWidth - shelves[i].currentX) * shelves[i].height; if (surfaceArea < bestShelfSurfaceArea) { bestShelf = shelves[i]; bestShelfSurfaceArea = surfaceArea; } } } if (null != bestShelf) { AddToShelf(bestShelf, rectSize.Width, rectSize.Height, ref newNode); return(newNode); } } break; case ShelfChoiceHeuristic.ShelfWorstAreaFit: { // Worst Area Fit rule: Choose the shelf with smallest remaining shelf area. Shelf bestShelf = null; int bestShelfSurfaceArea = -1; for (int i = 0; i < shelves.Count; ++i) { // Pre-rotate the rect onto the shelf here already so that the area fit computation // is done correctly. RotateToShelf(shelves[i], ref width, ref height); if (FitsOnShelf(shelves[i], width, height, i == shelves.Count - 1)) { int surfaceArea = (BinWidth - shelves[i].currentX) * shelves[i].height; if (surfaceArea > bestShelfSurfaceArea) { bestShelf = shelves[i]; bestShelfSurfaceArea = surfaceArea; } } } if (null != bestShelf) { AddToShelf(bestShelf, width, height, ref newNode); return(newNode); } } break; case ShelfChoiceHeuristic.ShelfBestHeightFit: { // Best Height Fit rule: Choose the shelf with best-matching height. Shelf bestShelf = null; int bestShelfHeightDifference = int.MaxValue; for (int i = 0; i < shelves.Count; ++i) { // Pre-rotate the rect onto the shelf here already so that the height fit computation // is done correctly. RotateToShelf(shelves[i], ref width, ref height); if (FitsOnShelf(shelves[i], width, height, i == shelves.Count - 1)) { int heightDifference = Math.Max(shelves[i].height - height, 0); Debug.Assert(heightDifference >= 0); if (heightDifference < bestShelfHeightDifference) { bestShelf = shelves[i]; bestShelfHeightDifference = heightDifference; } } } if (null != bestShelf) { AddToShelf(bestShelf, width, height, ref newNode); return(newNode); } } break; case ShelfChoiceHeuristic.ShelfBestWidthFit: { // Best Width Fit rule: Choose the shelf with smallest remaining shelf width. Shelf bestShelf = null; int bestShelfWidthDifference = int.MaxValue; for (int i = 0; i < shelves.Count; ++i) { // Pre-rotate the rect onto the shelf here already so that the height fit computation // is done correctly. RotateToShelf(shelves[i], ref width, ref height); if (FitsOnShelf(shelves[i], width, height, i == shelves.Count - 1)) { int widthDifference = BinWidth - shelves[i].currentX - width; Debug.Assert(widthDifference >= 0); if (widthDifference < bestShelfWidthDifference) { bestShelf = shelves[i]; bestShelfWidthDifference = widthDifference; } } } if (null != bestShelf) { AddToShelf(bestShelf, width, height, ref newNode); return(newNode); } } break; case ShelfChoiceHeuristic.ShelfWorstWidthFit: { // Worst Width Fit rule: Choose the shelf with smallest remaining shelf width. Shelf bestShelf = null; int bestShelfWidthDifference = -1; for (int i = 0; i < shelves.Count; ++i) { // Pre-rotate the rect onto the shelf here already so that the height fit computation // is done correctly. RotateToShelf(shelves[i], ref width, ref height); if (FitsOnShelf(shelves[i], width, height, i == shelves.Count - 1)) { int widthDifference = BinWidth - shelves[i].currentX - width; Debug.Assert(widthDifference >= 0); if (widthDifference > bestShelfWidthDifference) { bestShelf = shelves[i]; bestShelfWidthDifference = widthDifference; } } } if (null != bestShelf) { AddToShelf(bestShelf, width, height, ref newNode); return(newNode); } } break; } // The rectangle did not fit on any of the shelves. Open a new shelf. // Flip the rectangle so that the long side is horizontal. if (width < height && height <= BinWidth) { Swap(ref width, ref height); } if (CanStartNewShelf(height)) { if (UseWasteMap) { MoveShelfToWasteMap(shelves.LastOrDefault()); } StartNewShelf(height); Debug.Assert(FitsOnShelf(shelves.LastOrDefault(), width, height, true)); AddToShelf(shelves.LastOrDefault(), width, height, ref newNode); return(newNode); } /* * ///\todo This is problematic: If we couldn't start a new shelf - should we give up * /// and move all the remaining space of the bin for the waste map to track, * /// or should we just wait if the next rectangle would fit better? For now, * /// don't add the leftover space to the waste map. * else if (useWasteMap) * { * assert(binHeight - shelves.back().startY >= shelves.back().height); * shelves.back().height = binHeight - shelves.back().startY; * if (shelves.back().height > 0) * MoveShelfToWasteMap(shelves.back()); * * // Try to pack the rectangle again to the waste map. * GuillotineBinPack::Node node = wasteMap.Insert(width, height, true, 1, 3); * if (node.height != 0) * { * newNode.x = node.x; * newNode.y = node.y; * newNode.width = node.width; * newNode.height = node.height; * return newNode; * } * } */ // The rectangle didn't fit. return(newNode); }
public abstract Rect Insert(RectSize rectSize, GenericOption option);