/// <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); }
/// <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);