/// <summary> /// Pack a list of supplied rectangles into the supplied bin. /// </summary> /// <param name="items">The rectangles to pack.</param> /// <param name="container">The container to pack into, clearing any previously initialised bin.</param> /// <param name="packingStrategy">The method to use when packing.</param> public void PackOneContainer( List <Rectangle> items, Rectangle container, RectanglePackingStrategy packingStrategy) { this.InitialiseBinFromRectangle(container); if (this.FreeRectangles.Count == 0) { throw new InvalidOperationException("Bin has not been initialised"); } if (items.Count == 0) { throw new ArgumentNullException(nameof(items)); } for (int i = 0; i < items.Count; i++) { // skip already placed rectangles var rect = items[i]; if (rect == null) { continue; } var placed = this.PlaceItem(rect, packingStrategy, i); if (placed) { items[i] = null; } } }
public static Dictionary <string, object> PackRectangles( List <Rectangle> items, List <Rectangle> containers, [DefaultArgument("RectanglePackingStrategy.BestAreaFits")] RectanglePackingStrategy strategy) { var packer = new RectanglePacker(); var results = packer.PackMultipleContainers(items, containers, strategy); return(results.ToDictionary()); }
/// <summary> /// Chooses the best free rectangle for an item based on the placement method. /// Rectangles are formed from the leftover free space in the bin. /// </summary> /// <param name="item">The rectangle to place</param> /// <param name="placementMethod">The placement method</param> /// <param name="this">The bin packing result to investigate.</param> /// <returns>The free rectangle with best score</returns> private FreeRectangle GetBestFreeRectangle( Rectangle item, RectanglePackingStrategy placementMethod) { var freeRectangles = new List <FreeRectangle>(); for (int i = 0; i < this.FreeRectangles.Count; i++) { var fRect = this.FreeRectangles[i]; FreeRectangle chosenFreeRect; var fitsItem = new List <FreeRectangle> { ScoreRectanglePlacementInBin(item, placementMethod, fRect, true), ScoreRectanglePlacementInBin(item, placementMethod, fRect, false) }; fitsItem.RemoveAll(x => x == null); if (fitsItem.Count == 1) { chosenFreeRect = fitsItem[0]; fRect.score = chosenFreeRect.score; fRect.rotate = chosenFreeRect.rotate; freeRectangles.Add(fRect); } else if (fitsItem.Count == 2) { // Choose free rect with smallest score chosenFreeRect = fitsItem.Aggregate((f1, f2) => f1.score < f2.score ? f1 : f2); fRect.score = chosenFreeRect.score; fRect.rotate = chosenFreeRect.rotate; freeRectangles.Add(fRect); } } if (freeRectangles.Count > 0) { // Choose free rect with smallest score return(freeRectangles.Aggregate((f1, f2) => f1.score < f2.score ? f1 : f2)); } else { return(null); } }
private static double Score( FreeRectangle freeRect, Rectangle item, RectanglePackingStrategy placementMethod) { if (placementMethod == RectanglePackingStrategy.BestShortSideFits) { return(BSSF_Score(freeRect, item)); } else if (placementMethod == RectanglePackingStrategy.BestLongSideFits) { return(BLSF_Score(freeRect, item)); } else if (placementMethod == RectanglePackingStrategy.BestAreaFits) { return(BAF_Score(freeRect, item)); } return(0); }
/// <summary> /// Pack a list of supplied rectangles into the supplied containers. /// </summary> /// <param name="items">The rectangles to pack.</param> /// <param name="containers">The containers to pack into, clearing any previously initialised bin.</param> /// <param name="packingStrategy">The method to use when packing.</param> /// <returns>The list of packing results for each container.</returns> public List <IPacker <Rectangle, Rectangle> > PackMultipleContainers( List <Rectangle> items, List <Rectangle> containers, RectanglePackingStrategy packingStrategy) { // we need to keep track of packed items across containers // and then aggregate results, hence the lists external to BinPacker object var remainingRects = new List <Rectangle>(items); var packers = new List <IPacker <Rectangle, Rectangle> >(); for (var i = 0; i < containers.Count; i++) { var packer = new RectanglePacker(); packer.PackOneContainer(remainingRects, containers[i], packingStrategy); packers.Add(packer as IPacker <Rectangle, Rectangle>); } return(packers); }
private static FreeRectangle ScoreRectanglePlacementInBin(Rectangle item, RectanglePackingStrategy placementMethod, FreeRectangle fRect, bool rotate) { if (!ItemFits(fRect, item, rotate)) { return(null); } // TODO : we're doing un-necessary allocations in some cases, duplicating the fRect var newFree = new FreeRectangle { xPos = fRect.xPos, yPos = fRect.yPos, height = fRect.height, width = fRect.width, rotate = rotate, }; newFree.score = Score(newFree, item, placementMethod); return(newFree); }
/// <summary> /// Find best free rectangle and place next rectangle /// </summary> /// <param name="item"></param> /// <param name="placementMethod"></param> /// <param name="itemIndex"></param> private bool PlaceItem( Rectangle item, RectanglePackingStrategy placementMethod, int itemIndex) { FreeRectangle freeRect = this.GetBestFreeRectangle(item, placementMethod); if (freeRect == null) { this.RemainingItems.Add(item); return(false); } // translate rectangle to correct orientation and position if (freeRect.rotate) { item = Rotate(item); } var newCS = CoordinateSystem.ByOrigin(freeRect.xPos, freeRect.yPos); var originCS = CoordinateSystem.ByOrigin(item.StartPoint.X - item.Width, item.StartPoint.Y); var placedRect = (Rectangle)item.Transform(originCS, newCS); // place rectangle and update this.PackedItems.Add(placedRect); this.PackedIndices.Add(itemIndex); this.FreeRectangles.Remove(freeRect); // update remaining free space this.SplitFreeRectangle(freeRect, placedRect); List <double> itemBounds = RectBounds(placedRect); this.RemoveOverlaps(itemBounds); // Dispose Dynamo geometry newCS.Dispose(); originCS.Dispose(); return(true); }