private void Insert( Cuboid cuboid, FreeCuboidChoiceHeuristic cuboidChoice, GuillotineSplitHeuristic splitMethod) { // Check is overweight if (cuboid.Weight + _usedCuboids.Sum(x => x.Weight) > _parameter.BinWeight) { return; } // Find where to put the new cuboid var freeNodeIndex = 0; FindPositionForNewNode(cuboid, cuboidChoice, out freeNodeIndex); // Abort if we didn't have enough space in the bin if (!cuboid.IsPlaced) { return; } // Remove the space that was just consumed by the new cuboid if (freeNodeIndex < 0) { throw new ArithmeticException("freeNodeIndex < 0"); } SplitFreeCuboidByHeuristic(_freeCuboids[freeNodeIndex], cuboid, splitMethod); _freeCuboids.RemoveAt(freeNodeIndex); // Remember the new used cuboid _usedCuboids.Add(cuboid); }
/// Splits the given L-shaped free rectangle into two new free rectangles after placedRect has been placed into it. /// Determines the split axis by using the given heuristic. private void SplitFreeRectByHeuristic(Rect freeRect, Rect placedRect, GuillotineSplitHeuristic method) { // Compute the lengths of the leftover area. int w = freeRect.Width - placedRect.Width; int h = freeRect.Height - placedRect.Height; // Placing placedRect into freeRect results in an L-shaped free area, which must be split into // two disjoint rectangles. This can be achieved with by splitting the L-shape using a single line. // We have two choices: horizontal or vertical. // Use the given heuristic to decide which choice to make. bool splitHorizontal; switch (method) { case GuillotineSplitHeuristic.SplitShorterLeftoverAxis: // Split along the shorter leftover axis. splitHorizontal = (w <= h); break; case GuillotineSplitHeuristic.SplitLongerLeftoverAxis: // Split along the longer leftover axis. splitHorizontal = (w > h); break; case GuillotineSplitHeuristic.SplitMinimizeArea: // Maximize the larger area == minimize the smaller area. // Tries to make the single bigger rectangle. splitHorizontal = (placedRect.Width * h > w * placedRect.Height); break; case GuillotineSplitHeuristic.SplitMaximizeArea: // Maximize the smaller area == minimize the larger area. // Tries to make the rectangles more even-sized. splitHorizontal = (placedRect.Width * h <= w * placedRect.Height); break; case GuillotineSplitHeuristic.SplitShorterAxis: // Split along the shorter total axis. splitHorizontal = (freeRect.Width <= freeRect.Height); break; case GuillotineSplitHeuristic.SplitLongerAxis: // Split along the longer total axis. splitHorizontal = (freeRect.Width > freeRect.Height); break; default: splitHorizontal = true; break; } // Perform the actual split. SplitFreeRectAlongAxis(freeRect, placedRect, splitHorizontal); }
public BinPackGuillotineAlgorithm( BinPackParameter parameter, FreeCuboidChoiceHeuristic cuboidChoice, GuillotineSplitHeuristic splitMethod) { _parameter = parameter; _cuboidChoice = cuboidChoice; _splitMethod = splitMethod; _usedCuboids = new List <Cuboid>(); _freeCuboids = new List <Cuboid>(); AddFreeCuboid(new Cuboid(parameter.BinWidth, parameter.BinHeight, parameter.BinDepth)); }
public BinPackShelfAlgorithm( BinPackParameter parameter, FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod, ShelfChoiceHeuristic shelfChoice) { _parameter = parameter; _rectChoice = rectChoice; _splitMethod = splitMethod; _shelfChoice = shelfChoice; _currentY = 0; _shelves = new List <Shelf>(); _packedCuboids = new List <Cuboid>(); StartNewShelf(0); }
public void InsertOnPosition( Rectangle rect, GuillotineSplitHeuristic splitMethod, int freeRectIndex) { // Remove the space that was just consumed by the new rectangle. if (freeRectIndex < 0) { throw new ArithmeticException("freeRectIndex < 0"); } SplitFreeRectByHeuristic(_freeRectangles[freeRectIndex], rect, splitMethod); _freeRectangles.RemoveAt(freeRectIndex); // Remember the new used rectangle _usedRectangles.Add(rect); }
public BinPackGuillotineAlgorithm( decimal binWidth, decimal binHeight, decimal binDepth, FreeCuboidChoiceHeuristic cuboidChoice, GuillotineSplitHeuristic splitMethod) { _binWidth = binWidth; _binHeight = binHeight; _binDepth = binDepth; _cuboidChoice = cuboidChoice; _splitMethod = splitMethod; _usedCuboids = new List <Cuboid>(); _freeCuboids = new List <Cuboid>(); AddFreeCuboid(new Cuboid(_binWidth, _binHeight, _binDepth)); }
private void SplitFreeRectByHeuristic( Rectangle freeRect, Rectangle placedRect, GuillotineSplitHeuristic method) { // Compute the lengths of the leftover area. decimal w = freeRect.Width - placedRect.Width; decimal h = freeRect.Height - placedRect.Height; // Placing placedRect into freeRect results in an L-shaped free area, which // must be split into two disjoint rectangles. This can be achieved with by // splitting the L-shape using a single line. // We have two choices: horizontal or vertical. // Use the given heuristic to decide which choice to make. bool splitHorizontal; switch (method) { case GuillotineSplitHeuristic.SplitShorterLeftoverAxis: // Split along the shorter leftover axis. splitHorizontal = (w <= h); break; case GuillotineSplitHeuristic.SplitLongerLeftoverAxis: // Split along the longer leftover axis. splitHorizontal = (w > h); break; case GuillotineSplitHeuristic.SplitShorterAxis: // Split along the shorter total axis. splitHorizontal = (freeRect.Width <= freeRect.Height); break; case GuillotineSplitHeuristic.SplitLongerAxis: // Split along the longer total axis. splitHorizontal = (freeRect.Width > freeRect.Height); break; default: throw new NotSupportedException($"split method is unsupported: {method}"); } // Perform the actual split. SplitFreeRectAlongAxis(freeRect, placedRect, splitHorizontal); }
public BinPackShelfAlgorithm( decimal binWidth, decimal binHeight, decimal binDepth, FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod, ShelfChoiceHeuristic shelfChoice) { _binWidth = binWidth; _binHeight = binHeight; _binDepth = binDepth; _rectChoice = rectChoice; _splitMethod = splitMethod; _shelfChoice = shelfChoice; _currentY = 0; _shelves = new List <Shelf>(); StartNewShelf(0); }
private void SplitFreeCuboidByHeuristic( Cuboid freeCuboid, Cuboid placedCuboid, GuillotineSplitHeuristic method) { // Compute the lengths of the leftover area. var w = freeCuboid.Width - placedCuboid.Width; var d = freeCuboid.Depth - placedCuboid.Depth; // Use the given heuristic to decide which choice to make. bool splitHorizontal; switch (method) { case GuillotineSplitHeuristic.SplitShorterLeftoverAxis: // Split along the shorter leftover axis. splitHorizontal = (w <= d); break; case GuillotineSplitHeuristic.SplitLongerLeftoverAxis: // Split along the longer leftover axis. splitHorizontal = (w > d); break; case GuillotineSplitHeuristic.SplitShorterAxis: // Split along the shorter total axis. splitHorizontal = (freeCuboid.Width <= freeCuboid.Depth); break; case GuillotineSplitHeuristic.SplitLongerAxis: // Split along the longer total axis. splitHorizontal = (freeCuboid.Width > freeCuboid.Depth); break; default: throw new NotSupportedException($"split method is unsupported: {method}"); } // Perform the actual split. SplitFreeCuboidAlongAxis(freeCuboid, placedCuboid, splitHorizontal); }
/// 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 BinRect Insert(int width, int height, bool merge, FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod) { // Find where to put the new rectangle. int freeNodeIndex = 0; BinRect newRect = FindPositionForNewNode(width, height, rectChoice, 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. { var item = freeRectangles[freeNodeIndex]; SplitFreeRectByHeuristic(ref item, ref newRect, splitMethod); freeRectangles[freeNodeIndex] = item; freeRectangles.RemoveAt(freeNodeIndex); } // Perform a Rectangle Merge step if desired. if (merge) { MergeFreeList(); } // Remember the new used rectangle. usedRectangles.Add(newRect); return(newRect); }
/// Inserts a list of rectangles into the bin. /// @param rects The list of rectangles to add. This list will be destroyed in the packing process. /// @param merge If true, performs Rectangle Merge operations during the packing process. /// @param rectChoice The free rectangle choice heuristic rule to use. /// @param splitMethod The free rectangle split heuristic rule to use. public void Insert(List <RectSize> rects, bool merge, FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod) { // Remember variables about the best packing choice we have made so far during the iteration process. int bestFreeRect = 0; int bestRect = 0; bool bestFlipped = false; // Pack rectangles one at a time until we have cleared the rects array of all rectangles. // rects will get destroyed in the process. while (rects.Count > 0) { // Stores the penalty score of the best rectangle placement - bigger=worse, smaller=better. int bestScore = int.MaxValue; for (int i = 0; i < freeRectangles.Count; ++i) { for (int j = 0; j < rects.Count; ++j) { // If this rectangle is a perfect match, we pick it instantly. if (rects[j].width == freeRectangles[i].width && rects[j].height == freeRectangles[i].height) { bestFreeRect = i; bestRect = j; bestFlipped = false; bestScore = int.MinValue; i = freeRectangles.Count; // Force a jump out of the outer loop as well - we got an instant fit. break; } // If flipping this rectangle is a perfect match, pick that then. else if (rects[j].height == freeRectangles[i].width && rects[j].width == freeRectangles[i].height) { bestFreeRect = i; bestRect = j; bestFlipped = true; bestScore = int.MinValue; i = freeRectangles.Count; // Force a jump out of the outer loop as well - we got an instant fit. break; } // Try if we can fit the rectangle upright. else if (rects[j].width <= freeRectangles[i].width && rects[j].height <= freeRectangles[i].height) { var item = freeRectangles[i]; int score = ScoreByHeuristic(rects[j].width, rects[j].height, ref item, rectChoice); freeRectangles[i] = item; if (score < bestScore) { bestFreeRect = i; bestRect = j; bestFlipped = false; bestScore = score; } } // If not, then perhaps flipping sideways will make it fit? else if (rects[j].height <= freeRectangles[i].width && rects[j].width <= freeRectangles[i].height) { var item = freeRectangles[i]; int score = ScoreByHeuristic(rects[j].height, rects[j].width, ref item, rectChoice); freeRectangles[i] = item; if (score < bestScore) { bestFreeRect = i; bestRect = j; bestFlipped = true; bestScore = score; } } } } // If we didn't manage to find any rectangle to pack, abort. if (bestScore == int.MaxValue) { return; } // Otherwise, we're good to go and do the actual packing. BinRect newNode; newNode.x = freeRectangles[bestFreeRect].x; newNode.y = freeRectangles[bestFreeRect].y; newNode.width = rects[bestRect].width; newNode.height = rects[bestRect].height; if (bestFlipped) { int temp = newNode.width; newNode.width = newNode.height; newNode.height = temp; } // Remove the free space we lost in the bin. { var item = freeRectangles[bestFreeRect]; SplitFreeRectByHeuristic(ref item, ref newNode, splitMethod); freeRectangles[bestFreeRect] = item; freeRectangles.RemoveAt(bestFreeRect); } // Remove the rectangle we just packed from the input list. rects.RemoveAt(bestRect); // Perform a Rectangle Merge step if desired. if (merge) { MergeFreeList(); } // Remember the new used rectangle. usedRectangles.Add(newNode); } }