矩形零件装箱适配判断逻辑优化求助
矩形零件装箱问题的优化方案
问题根源
你当前使用的ComponentFitsInBin函数中,最后两个基于Sum的判断逻辑完全错误:
bin.SizeX - bin.PackedComponents.Sum(p => p.SizeX)计算的是所有零件X尺寸总和与Bin宽度的差值,这和Bin内实际剩余的可用空间布局毫无关系。比如Bin为10×10,放入一个8×8的零件后,SumX=8,剩余X差值为2,但实际Bin内存在2×10和8×2的空隙,完全可以放下2×10或旋转后的3×2零件,但你的逻辑会因为零件X尺寸3>2而错误拒绝。- 这种只看总长度的判断,既会错过可用空隙,又无法真正防止零件超出Bin边界(形状不匹配时,即使总长度足够,也可能重叠或超出)。
核心优化思路
要解决这个问题,必须:
- 记录已放置零件的位置信息:Bin内的
PackedComponent不能只存储尺寸,还要记录每个零件的左上角坐标和实际摆放的尺寸(因为支持90度旋转,实际尺寸可能和原始尺寸不同)。 - 替换Sum判断为碰撞检测:通过检查零件(含两种旋转状态)是否能放入Bin内的某个空位,且不与已放置零件重叠、不超出Bin边界,来判断是否适配。
代码实现示例
1. 更新PackedComponent类,增加位置和实际尺寸属性
public class PackedComponent { // 原始尺寸 public int OriginalSizeX { get; set; } public int OriginalSizeY { get; set; } // 实际摆放的尺寸(考虑旋转) public int Width { get; set; } public int Height { get; set; } // 摆放位置(左上角坐标,以Bin的左上角为原点) public int X { get; set; } public int Y { get; set; } // 计算表面积 public int GetSurfaceArea() => Width * Height; }
2. 重写ComponentFitsInBin函数
private bool ComponentFitsInBin(PackedComponent component, Bin bin) { // 快速过滤:剩余面积不足直接返回 if (bin.RemainingSurfaceArea < component.GetSurfaceArea()) return false; // 生成两种可能的摆放方向:原始方向、旋转90度 var possibleOrientations = new List<(int Width, int Height)> { (component.OriginalSizeX, component.OriginalSizeY), (component.OriginalSizeY, component.OriginalSizeX) }; foreach (var (width, height) in possibleOrientations) { // 当前方向下零件尺寸超过Bin,直接跳过 if (width > bin.SizeX || height > bin.SizeY) continue; // Bin为空时直接可以放入 if (bin.PackedComponents.Count == 0) return true; // 尝试在已放置零件的右侧或下方放置(基础填充策略) foreach (var placed in bin.PackedComponents) { // 放在当前零件右侧 int newX = placed.X + placed.Width; int newY = placed.Y; if (CanPlaceAt(newX, newY, width, height, bin)) return true; // 放在当前零件下方 newX = placed.X; newY = placed.Y + placed.Height; if (CanPlaceAt(newX, newY, width, height, bin)) return true; } // 尝试紧贴Bin边缘的空位(比如已放零件上方、右方的整行/整列空位) int maxUsedY = bin.PackedComponents.Max(p => p.Y + p.Height); if (maxUsedY + height <= bin.SizeY && CanPlaceAt(0, maxUsedY, width, height, bin)) return true; int maxUsedX = bin.PackedComponents.Max(p => p.X + p.Width); if (maxUsedX + width <= bin.SizeX && CanPlaceAt(maxUsedX, 0, width, height, bin)) return true; } // 所有方向和位置都尝试后仍无法放置 return false; } // 辅助函数:检查指定位置是否可以放置零件(不超出边界、不重叠) private bool CanPlaceAt(int x, int y, int width, int height, Bin bin) { // 检查是否超出Bin边界 if (x + width > bin.SizeX || y + height > bin.SizeY) return false; // 检查是否与已放置零件重叠 foreach (var placed in bin.PackedComponents) { bool isOverlapping = !(placed.X + placed.Width <= x || x + width <= placed.X || placed.Y + placed.Height <= y || y + height <= placed.Y); if (isOverlapping) return false; } return true; }
补充说明
- 上述代码使用的是基础的"紧邻填充"策略,你可以根据需求替换为更高效的装箱算法(比如Best Fit Decreasing、First Fit Decreasing,或专门针对矩形装箱的空位分割算法)。
RemainingSurfaceArea的检查保留作为快速过滤,避免不必要的碰撞检测,提升性能。- 旋转逻辑通过生成两种尺寸组合实现,确保不会错过可以通过旋转适配的空位。
内容的提问来源于stack exchange,提问作者Parrotmaster
相关产品推荐
相关产品推荐

