/// <summary>
        ///   Optimizes the rectangle's placement by moving it either left or up to fill
        ///   any gaps resulting from rectangles blocking the anchors of the most optimal
        ///   placements.
        /// </summary>
        /// <param name="placement">Placement to be optimized</param>
        /// <param name="rectangleWidth">Width of the rectangle to be optimized</param>
        /// <param name="rectangleHeight">Height of the rectangle to be optimized</param>
        private void OptimizePlacement(ref Pointi placement, int rectangleWidth, int rectangleHeight)
        {
            var rectangle = Recti.FromSize(placement.X, placement.Y, rectangleWidth, rectangleHeight);

            // Try to move the rectangle to the left as far as possible
            int leftMost = placement.X;

            while (IsFree(ref rectangle, PackingAreaWidth, PackingAreaHeight))
            {
                leftMost = rectangle.X;
                --rectangle.X;
            }

            // Reset rectangle to original position
            rectangle.X = placement.X;

            // Try to move the rectangle upwards as far as possible
            int topMost = placement.Y;

            while (IsFree(ref rectangle, PackingAreaWidth, PackingAreaHeight))
            {
                topMost = rectangle.Y;
                --rectangle.Y;
            }

            // Use the dimension in which the rectangle could be moved farther
            if ((placement.X - leftMost) > (placement.Y - topMost))
            {
                placement.X = leftMost;
            }
            else
            {
                placement.Y = topMost;
            }
        }
        /// <summary>Tries to allocate space for a rectangle in the packing area</summary>
        /// <param name="rectangleWidth">Width of the rectangle to allocate</param>
        /// <param name="rectangleHeight">Height of the rectangle to allocate</param>
        /// <param name="placement">Output parameter receiving the rectangle's placement</param>
        /// <returns>True if space for the rectangle could be allocated</returns>
        public override bool TryPack(int rectangleWidth, int rectangleHeight, out Pointi placement)
        {
            // Try to find an anchor where the rectangle fits in, enlarging the packing
            // area and repeating the search recursively until it fits or the
            // maximum allowed size is exceeded.
            int anchorIndex = SelectAnchorRecursive(rectangleWidth, rectangleHeight, actualPackingAreaWidth, actualPackingAreaHeight);

            // No anchor could be found at which the rectangle did fit in
            if (anchorIndex == -1)
            {
                placement = new Pointi();
                return(false);
            }

            placement = anchors[anchorIndex];

            // Move the rectangle either to the left or to the top until it collides with
            // a neightbouring rectangle. This is done to combat the effect of lining up
            // rectangles with gaps to the left or top of them because the anchor that
            // would allow placement there has been blocked by another rectangle
            OptimizePlacement(ref placement, rectangleWidth, rectangleHeight);

            // Remove the used anchor and add new anchors at the upper right and lower left
            // positions of the new rectangle
            // The anchor is only removed if the placement optimization didn't
            // move the rectangle so far that the anchor isn't blocked anymore
            bool blocksAnchor =
                ((placement.X + rectangleWidth) > anchors[anchorIndex].X) &&
                ((placement.Y + rectangleHeight) > anchors[anchorIndex].Y);

            if (blocksAnchor)
            {
                anchors.RemoveAt(anchorIndex);
            }

            // Add new anchors at the upper right and lower left coordinates of the rectangle
            InsertAnchor(new Pointi(placement.X + rectangleWidth, placement.Y));
            InsertAnchor(new Pointi(placement.X, placement.Y + rectangleHeight));

            // Finally, we can add the rectangle to our packed rectangles list
            packedRectangles.Add(Recti.FromSize(placement.X, placement.Y, rectangleWidth, rectangleHeight));

            return(true);
        }
        /// <summary>Inserts a new anchor point into the anchor list</summary>
        /// <param name="anchor">Anchor point that will be inserted</param>
        /// <remarks>
        ///   This method tries to keep the anchor list ordered by ranking the anchors
        ///   depending on the distance from the top left corner in the packing area.
        /// </remarks>
        private void InsertAnchor(Pointi anchor)
        {
            // Find out where to insert the new anchor based on its rank (which is
            // calculated based on the anchor's distance to the top left corner of
            // the packing area).
            //
            // From MSDN on BinarySearch():
            //   "If the List does not contain the specified value, the method returns
            //    a negative integer. You can apply the bitwise complement operation (~) to
            //    this negative integer to get the index of the first element that is
            //    larger than the search value."
            int insertIndex = anchors.BinarySearch(anchor, AnchorRankComparer.Default);

            if (insertIndex < 0)
            {
                insertIndex = ~insertIndex;
            }

            // Insert the anchor at the index matching its rank
            anchors.Insert(insertIndex, anchor);
        }
Example #4
0
 /// <summary>Tries to allocate space for a rectangle in the packing area</summary>
 /// <param name="rectangleWidth">Width of the rectangle to allocate</param>
 /// <param name="rectangleHeight">Height of the rectangle to allocate</param>
 /// <param name="placement">Output parameter receiving the rectangle's placement</param>
 /// <returns>True if space for the rectangle could be allocated</returns>
 public abstract bool TryPack(int rectangleWidth, int rectangleHeight, out Pointi placement);