/// <summary> /// Split the free area into freeRectangles /// </summary> /// <param name="fRect"></param> /// <param name="item"></param> private static void SplitFreeRectangle( FreeRectangle fRect, Rectangle item) { if (item.Width < fRect.width) { double fW = fRect.width - item.Width; double fH = fRect.height; double fX = fRect.xPos + item.Width; double fY = fRect.yPos; freeRectangles.Add(new FreeRectangle { width = fW, height = fH, xPos = fX, yPos = fY }); } if (item.Height < fRect.height) { double fW = fRect.width; double fH = fRect.height - item.Height; double fX = fRect.xPos; double fY = fRect.yPos + item.Height; freeRectangles.Add(new FreeRectangle { width = fW, height = fH, xPos = fX, yPos = fY }); } }
/// <summary> /// Find best freerectangle and place next rectangle /// </summary> /// <param name="item"></param> /// <param name="placementMethod"></param> /// <param name="idx"></param> private static void PlaceItem( Rectangle item, PlacementMethods placementMethod, int idx) { FreeRectangle f = BestFreeRect(item, placementMethod); if (f != null) { if (f.rotate) { item = Rotate(item); } CoordinateSystem newCS = CoordinateSystem.ByOrigin(f.xPos, f.yPos); CoordinateSystem originCS = CoordinateSystem.ByOrigin(item.StartPoint.X - item.Width, item.StartPoint.Y); Rectangle placedRect = (Rectangle)item.Transform(originCS, newCS); packedRectangles.Add(placedRect); SplitFreeRectangle(f, placedRect); packedIndices.Add(idx); freeRectangles.Remove(f); List <double> itemBounds = RectBounds(placedRect); RemoveOverlaps(itemBounds); // Dispose Dynamo geometry newCS.Dispose(); originCS.Dispose(); } else { remainRectangles.Add(item); } }
/// <summary> /// Split the free area into free rectangles /// </summary> /// <param name="fRect"></param> /// <param name="item"></param> private void SplitFreeRectangle( FreeRectangle fRect, Rectangle item) { if (item.Width < fRect.width) { var fW = fRect.width - item.Width; var fH = fRect.height; var fX = fRect.xPos + item.Width; var fY = fRect.yPos; this.FreeRectangles.Add(new FreeRectangle { width = fW, height = fH, xPos = fX, yPos = fY }); } if (item.Height < fRect.height) { var fW = fRect.width; var fH = fRect.height - item.Height; var fX = fRect.xPos; var fY = fRect.yPos + item.Height; this.FreeRectangles.Add(new FreeRectangle { width = fW, height = fH, xPos = fX, yPos = fY }); } }
/// <summary> /// Scoring Method for Best Area Fits /// </summary> /// <param name="f"></param> /// <param name="item"></param> /// <returns>Score of Best Area Fits</returns> private static double BAF_Score( FreeRectangle f, Rectangle item) { double freeFArea = f.area(); double rectArea = item.Width * item.Height; return(freeFArea - rectArea); }
/// <summary> /// Checks if the rectangle fits in the freeRectangle /// </summary> /// <param name="f"></param> /// <param name="rectangle"></param> /// <param name="rotate"></param> /// <returns>boolean</returns> private static bool ItemFits( FreeRectangle f, Rectangle rectangle, bool rotate) { if (rotate == false && rectangle.Width <= f.width && rectangle.Height <= f.height) { return(true); } if (rotate == true && rectangle.Height <= f.width && rectangle.Width <= f.height) { return(true); } return(false); }
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); }
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); }
/// <summary> /// Scoring Method for Best Long Side Fits /// </summary> /// <param name="f"></param> /// <param name="item"></param> /// <returns>Score of Best Long Side Fits</returns> private static double BLSF_Score( FreeRectangle f, Rectangle item) { double widthDifference; double heightDifference; if (f.rotate) { widthDifference = f.width - item.Height; heightDifference = f.height - item.Width; } else { widthDifference = f.width - item.Width; heightDifference = f.height - item.Height; } return(new List <double> { widthDifference, heightDifference }.Max()); }
/// <summary> /// Checks if two rectangles overlaps /// https://www.geeksforgeeks.org/find-two-rectangles-overlap/ /// </summary> /// <param name="f1"></param> /// <param name="rectBounds"></param> /// <returns>boolean</returns> private static bool RectangleOverlaps( FreeRectangle f1, List <double> rectBounds) { double f1BottomLeftX = f1.xPos; double f1BottomLeftY = f1.yPos; double f1TopRightX = f1.xPos + f1.width; double f1TopRightY = f1.yPos + f1.height; // If one rectangle is on left side of other if (f1TopRightY <= rectBounds[1] || f1BottomLeftY >= rectBounds[3]) { return(false); } // If one rectangle is above other if (f1TopRightX <= rectBounds[0] || f1BottomLeftX >= rectBounds[2]) { return(false); } return(true); }
/// <summary> /// check if FreeRectangle is fully incapsulated in another /// </summary> /// <param name="f1"></param> /// <param name="f2"></param> /// <returns>boolean</returns> private static bool IsEncapsulated( FreeRectangle f1, FreeRectangle f2) { int precsion = 2; if (Math.Round(f2.xPos, precsion) < Math.Round(f1.xPos, precsion) || Math.Round(f2.xPos, precsion) > Math.Round(f1.xPos + f1.width, precsion)) { return(false); } if (Math.Round((f2.xPos + f2.width), precsion) > Math.Round((f1.xPos + f1.width), precsion)) { return(false); } if (Math.Round(f2.yPos, precsion) < Math.Round(f1.yPos, precsion) || Math.Round(f2.yPos, precsion) > Math.Round((f1.yPos + f1.height), precsion)) { return(false); } if (Math.Round((f2.yPos + f2.height), precsion) > Math.Round((f1.yPos + f1.height), precsion)) { return(false); } return(true); }
/// <summary> /// Get the min/max points of the overlap BoundingBox /// </summary> /// <param name="f1"></param> /// <param name="rectBounds"></param> /// <returns>overlapping boundary</returns> private static List <double> OverlapBound( FreeRectangle f1, List <double> rectBounds) { // return bottom left x,y and top left x,y double x1 = f1.xPos; double y1 = f1.yPos; double x2 = f1.xPos + f1.width; double y2 = f1.yPos + f1.height; double x3 = rectBounds[0]; double y3 = rectBounds[1]; double x4 = rectBounds[2]; double y4 = rectBounds[3]; double overlapBotLeftX = x1 > x3 ? x1 : x3; double overlapBotLeftY = y1 > y3 ? y1 : y3; double overlapTopRightX = x2 < x4 ? x2 : x4; double overlapTopRightY = y2 < y4 ? y2 : y4; return(new List <double> { overlapBotLeftX, overlapBotLeftY, overlapTopRightX, overlapTopRightY }); }
/// <summary> /// Clip overlap of overlapping free areas /// </summary> /// <param name="f1"></param> /// <param name="overlapBound"></param> /// <returns>free rectangles after clipping the overlap</returns> private static List <FreeRectangle> ClipOverlap( FreeRectangle f1, List <double> overlapBound) { double F1x = f1.xPos; double F1y = f1.yPos; // Bottom left x,y and top right x,y of overlap double overlapBotLeftX = overlapBound[0]; double overlapBotLeftY = overlapBound[1]; double overlapTopRightX = overlapBound[2]; double overlapTopRightY = overlapBound[3]; List <FreeRectangle> newFreeRects = new List <FreeRectangle>(); // Left side if (overlapBotLeftX > F1x) { newFreeRects.Add(new FreeRectangle { width = overlapBotLeftX - F1x, height = f1.height, xPos = F1x, yPos = F1y }); } // Right side if (overlapTopRightX < F1x + f1.width) { newFreeRects.Add(new FreeRectangle { width = (F1x + f1.width) - overlapTopRightX, height = f1.height, xPos = overlapTopRightX, yPos = F1y }); } // Bottom Side if (overlapBotLeftY > F1y) { newFreeRects.Add(new FreeRectangle { width = f1.width, height = overlapBotLeftY - F1y, xPos = F1x, yPos = F1y }); } // Top Side if (overlapTopRightY < F1y + f1.height) { newFreeRects.Add(new FreeRectangle { width = f1.width, height = (F1y + f1.height) - overlapTopRightY, xPos = F1x, yPos = overlapTopRightY }); } return(newFreeRects); }
/// <summary> /// Chooses the best free rectangle based on the placement method /// </summary> /// <param name="item"></param> /// <param name="placementMethod"></param> /// <returns>free rectangle with best score</returns> private static FreeRectangle BestFreeRect( Rectangle item, PlacementMethods placementMethod) { List <FreeRectangle> fRects = new List <FreeRectangle>(); foreach (var fRect in freeRectangles) { FreeRectangle chosenFreeRect; List <FreeRectangle> fitsItem = new List <FreeRectangle>(); if (ItemFits(fRect, item, false)) { FreeRectangle newFree = new FreeRectangle { xPos = fRect.xPos, yPos = fRect.yPos, height = fRect.height, width = fRect.width, rotate = false, }; if (placementMethod == PlacementMethods.BestShortSideFits) { newFree.score = BSSF_Score(newFree, item); } else if (placementMethod == PlacementMethods.BestLongSideFits) { newFree.score = BLSF_Score(newFree, item); } else if (placementMethod == PlacementMethods.BestAreaFits) { newFree.score = BAF_Score(newFree, item); } fitsItem.Add(newFree); } if (ItemFits(fRect, item, true)) { FreeRectangle newFree = new FreeRectangle { xPos = fRect.xPos, yPos = fRect.yPos, height = fRect.height, width = fRect.width, rotate = true, }; if (placementMethod == PlacementMethods.BestShortSideFits) { newFree.score = BSSF_Score(newFree, item); } else if (placementMethod == PlacementMethods.BestLongSideFits) { newFree.score = BLSF_Score(newFree, item); } else if (placementMethod == PlacementMethods.BestAreaFits) { newFree.score = BAF_Score(newFree, item); } fitsItem.Add(newFree); } if (fitsItem.Count == 1) { chosenFreeRect = fitsItem[0]; fRect.score = chosenFreeRect.score; fRect.rotate = chosenFreeRect.rotate; fRects.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; fRects.Add(fRect); } else { continue; } } if (fRects.Any()) { // Choose free rect with smallest score return(fRects.Aggregate((f1, f2) => f1.score < f2.score ? f1 : f2)); } else { return(null); } }