Exemplo n.º 1
0
        /// 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);
        }
Exemplo n.º 2
0
        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);
        }
Exemplo n.º 3
0
        /// 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);
        }
Exemplo n.º 4
0
        /// 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);
            }
        }
Exemplo n.º 5
0
        /// 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);
        }
Exemplo n.º 6
0
 public abstract Rect Insert(RectSize rectSize, GenericOption option);