using System; using System.Collections.Generic; using System.Linq; using WIDESEAWCS_Common; public class ContainerPlacer { // 容器尺寸常量 private const int ContainerY = 2800; // 长度方向(Y轴) private const int ContainerX = 600; // 宽度方向(X轴) private const int ContainerZ = 500; // 高度方向(Z轴) // 系统参数 private const int SafetySpacing = 10; // 安全间距 private const int YAxisMaxPlace = 880;// 放置Y坐标最大值 private const int SuctionSpacing = 660; // 吸盘间距 // 吸盘尺寸配置(横向/纵向) private static readonly (int x, int y)[] SuctionSizes = { (530, 130), // 0度横向 (130, 530) // 90度纵向 }; /// /// 主计算方法 /// public SuctionInfo CalculatePlacement(Block newBlock, List existingBlocks) { // 获取所有支撑面并按高度排序 var supportSurfaces = GetSupportSurfaces(existingBlocks) .OrderBy(s => s.Z) .ToList(); foreach (var surface in supportSurfaces) { // 尝试所有有效旋转组合 foreach (var rotation in GetValidRotations(newBlock)) { // 计算实际放置尺寸 //var (placeW, placeL) = GetPlacementDimensions(newBlock, rotation); // 跳过尺寸不符的表面 if (!CanPlaceOnSurface(newBlock.W, newBlock.L, surface, newBlock.H)) continue; // 搜索可用位置 var position = FindPosition(newBlock.W, newBlock.L, surface, existingBlocks); if (!position.HasValue) continue; // 计算吸盘参数 var suction = CalculateSuctionParams(newBlock, position.Value, surface.Z, rotation, newBlock.W, newBlock.L); suction.Point = position.Value; // 最终验证 if (ValidatePlacement(suction, existingBlocks)) { return suction; } } } return null; } /// /// 获取支撑面列表(容器底部+现有木块顶面) /// private List GetSupportSurfaces(List blocks) { var surfaces = new List { new SupportSurface { // 容器底部 Z = 0, X = 0, Y = 0, AvailableX = ContainerX, AvailableY = ContainerY } }; // 添加现有木块顶面 surfaces.AddRange(blocks.Select(b => new SupportSurface { Z = b.Z + b.H, X = b.X, Y = b.Y, AvailableX = b.W, AvailableY = b.L })); return surfaces; } /// /// 表面可行性检查 /// private bool CanPlaceOnSurface(int placeW, int placeL, SupportSurface surface, int blockH) { return surface.AvailableX >= placeW && surface.AvailableY >= placeL && surface.Z + blockH <= ContainerZ; } /// /// 获取有效旋转角度(考虑规则5) /// private IEnumerable GetValidRotations(Block block) { // 0°/180°横向双吸盘(规则5) if (block.L > 920 && block.W >= 100 || block.L < 450) { yield return 0; yield return 180; } // 90°/270°纵向双吸盘(无条件允许) yield return 90; yield return 270; } /// /// 计算实际放置尺寸(考虑旋转) /// private (int w, int l) GetPlacementDimensions(Block b, int rotation) { return rotation % 180 == 0 ? (b.W, b.L) : (b.L, b.W); } /// /// 位置搜索算法(带安全间距) /// private Point? FindPosition(int placeW, int placeL, SupportSurface surface, List existing) { // 搜索步长设置为安全间距 const int step = 10; for (int x = surface.X; x + placeW <= surface.X + surface.AvailableX; x += step) { for (int y = surface.Y; y + placeL <= surface.Y + surface.AvailableY; y += step) { if (IsPositionValid(x, y, placeW, placeL, surface.Z, existing)) { return new Point(x, y); } } } return null; } /// /// 位置有效性验证(含安全间距) /// private bool IsPositionValid(int x, int y, int w, int l, int z, List existing) { // 创建带安全间距的检测区域 var newArea = new Rectangle( x - SafetySpacing, y - SafetySpacing, x + w + SafetySpacing, y + l + SafetySpacing); var a = existing.Where(block => block.Z + block.H - z > 1).ToList(); // 碰撞检测 foreach (var b in a) { var existArea = new Rectangle( b.X - SafetySpacing, b.Y - SafetySpacing, b.X + b.W + SafetySpacing, b.Y + b.L + SafetySpacing); if (newArea.Intersects(existArea)) return false; } // 容器边界检查 return x >= 0 && x + w <= ContainerX && y >= 0 && y + l <= ContainerY; } /// /// 计算吸盘参数(核心算法) /// private SuctionInfo CalculateSuctionParams(Block block, Point position, int surfaceZ, int rotation, int placeW, int placeL) { var info = new SuctionInfo { Rotation = rotation }; var suctionType = rotation % 180 == 0 ? 0 : 1; // 计算理论放置中心 info.PlaceCenter = new int[3] { position.X + placeW/2, position.Y + placeL/2, surfaceZ + block.H }; // 先应用规则6:放置位置调整 if (ApplyRule6Adjustment(info, suctionType)) { // 调整后需要重新计算抓取点 ApplyRule7Adjustment(info, block, suctionType); } else { // 未触发规则6时直接应用规则7 ApplyRule7Adjustment(info, block, suctionType); } // 应用旋转调整 //ApplyRotationAdjustment(info, rotation, block); return info; } /// /// 应用规则6调整:放置位置超过880mm时边缘抓取 /// private bool ApplyRule6Adjustment(SuctionInfo info, int suctionType) { if (info.PlaceCenter[1] <= YAxisMaxPlace) return false; // 计算吸盘尺寸 int suctionSize = SuctionSizes[suctionType].y; // 计算调整后的放置位置(规则6) info.PlaceCenter[1] = YAxisMaxPlace - suctionSize / 2; info.Rotation += 180; // 添加旋转标记 return true; } /// /// 应用规则7调整:抓取点尽量靠近中心 /// private void ApplyRule7Adjustment(SuctionInfo info, Block block, int suctionType) { // 计算理论最大行程 int maxTravel = YAxisMaxPlace - SuctionSizes[suctionType].y / 2; // 计算理想抓取点 info.PickCenter = new int[3] { block.W / 2, Math.Min(block.L / 2, maxTravel), // Y方向尽量接近中心 block.H }; } /// /// 应用旋转调整(180/270度) /// private void ApplyRotationAdjustment(SuctionInfo info, int rotation, Block block) { if (rotation == 180 || info.Rotation == 180) { // Y轴镜像 info.PickCenter[1] = block.L - info.PickCenter[1]; info.PlaceCenter[1] = ContainerY - info.PlaceCenter[1]; } else if (rotation == 270 || info.Rotation == 270) { // X轴镜像 info.PickCenter[0] = ContainerX - info.PickCenter[0]; info.PlaceCenter[0] = ContainerX - info.PlaceCenter[0]; } } /// /// 最终放置验证 /// private bool ValidatePlacement(SuctionInfo info, List existing) { // 空间边界检查 if (info.PlaceCenter[0] < 0 || info.PlaceCenter[0] > ContainerX) return false; if (info.PlaceCenter[1] < 0 || info.PlaceCenter[1] > YAxisMaxPlace) return false; if (info.PlaceCenter[2] > ContainerZ) return false; // 吸盘投影覆盖检查 var suctionType = info.Rotation % 180 == 0 ? 0 : 1; var (sx, sy) = SuctionSizes[suctionType]; var projection = new Rectangle( info.PlaceCenter[0] - sx / 2, info.PlaceCenter[1] - sy / 2, info.PlaceCenter[0] + sx / 2, info.PlaceCenter[1] + sy / 2 ); // 检查是否完全在支撑面内 return existing.All(b => !projection.Intersects(new Rectangle( b.X - SafetySpacing, b.Y - SafetySpacing, b.X + b.W + SafetySpacing, b.Y + b.L + SafetySpacing ))); } } // 辅助类定义-------------------------------------------- public class SupportSurface { public int X { get; set; } public int Y { get; set; } public int Z { get; set; } public int AvailableX { get; set; } public int AvailableY { get; set; } } public class Rectangle { public int Left { get; } public int Top { get; } public int Right { get; } public int Bottom { get; } public Rectangle(int x1, int y1, int x2, int y2) { Left = Math.Min(x1, x2); Right = Math.Max(x1, x2); Top = Math.Min(y1, y2); Bottom = Math.Max(y1, y2); } public bool Intersects(Rectangle other) { return !(Right < other.Left || Left > other.Right || Bottom < other.Top || Top > other.Bottom); } } #region //using System; //using System.Collections.Generic; //using System.Linq; //using WIDESEAWCS_Common; //public class ContainerPlacer //{ // // 容器尺寸常量 // private const int ContainerY = 2800; // 长度方向(Y轴) // private const int ContainerX = 700; // 宽度方向(X轴) // private const int ContainerZ = 500; // 高度方向(Z轴) // // 系统参数 // private const int SafetySpacing = 10; // 安全间距 // private const int YAxisMax = 880; // Y坐标最大值 // private const int SuctionSpacing = 660; // 吸盘间距 // // 吸盘尺寸配置(横向/纵向) // private static readonly (int x, int y)[] SuctionSizes = // { // (530, 130), // 0度横向 // (130, 530) // 90度纵向 // }; // /// // /// 主计算方法 // /// // public SuctionInfo CalculatePlacement(Block newBlock, List existingBlocks) // { // // 获取所有支撑面并按高度排序 // var supportSurfaces = GetSupportSurfaces(existingBlocks) // .OrderBy(s => s.Z) // .ToList(); // foreach (var surface in supportSurfaces) // { // // 尝试所有有效旋转组合 // foreach (var rotation in GetValidRotations(newBlock)) // { // // 计算实际放置尺寸 // var (placeW, placeL) = GetPlacementDimensions(newBlock, rotation); // // 跳过尺寸不符的表面 // if (!CanPlaceOnSurface(placeW, placeL, surface, newBlock.H)) continue; // // 搜索可用位置 // var position = FindPosition(placeW, placeL, surface, existingBlocks); // if (!position.HasValue) continue; // // 计算吸盘参数 // var suction = CalculateSuctionParams(newBlock, position.Value, // surface.Z, rotation, placeW, placeL); // // 最终验证 // if (ValidatePlacement(suction, existingBlocks)) // { // return suction; // } // } // } // return null; // } // /// // /// 获取支撑面列表(容器底部+现有木块顶面) // /// // private List GetSupportSurfaces(List blocks) // { // var surfaces = new List { // new SupportSurface { // 容器底部 // Z = 0, X = 0, Y = 0, // AvailableX = ContainerX, AvailableY = ContainerY // } // }; // // 添加现有木块顶面 // surfaces.AddRange(blocks.Select(b => new SupportSurface // { // Z = b.Z + b.H, // X = b.X, // Y = b.Y, // AvailableX = b.W, // AvailableY = b.L // })); // return surfaces; // } // /// // /// 表面可行性检查 // /// // private bool CanPlaceOnSurface(int placeW, int placeL, // SupportSurface surface, int blockH) // { // return surface.AvailableX >= placeW && // surface.AvailableY >= placeL && // surface.Z + blockH <= ContainerZ; // } // /// // /// 获取有效旋转角度(考虑规则5) // /// // private IEnumerable GetValidRotations(Block block) // { // // 0°/180°横向双吸盘(规则5) // if (block.L > 920 && block.W >= 300) // { // yield return 0; // yield return 180; // } // // 90°/270°纵向双吸盘(无条件允许) // yield return 90; // yield return 270; // } // /// // /// 计算实际放置尺寸(考虑旋转) // /// // private (int w, int l) GetPlacementDimensions(Block b, int rotation) // { // return rotation % 180 == 0 ? (b.W, b.L) : (b.L, b.W); // } // /// // /// 位置搜索算法(带安全间距) // /// // private Point? FindPosition(int placeW, int placeL, // SupportSurface surface, List existing) // { // // 搜索步长设置为安全间距 // const int step = 10; // for (int x = surface.X; x + placeW <= surface.X + surface.AvailableX; x += step) // { // for (int y = surface.Y; y + placeL <= surface.Y + surface.AvailableY; y += step) // { // if (IsPositionValid(x, y, placeW, placeL, surface.Z, existing)) // { // return new Point(x, y); // } // } // } // return null; // } // /// // /// 位置有效性验证(含安全间距) // /// // private bool IsPositionValid(int x, int y, int w, int l, // int z, List existing) // { // // 创建带安全间距的检测区域 // var newArea = new Rectangle( // x - SafetySpacing, // y - SafetySpacing, // x + w + SafetySpacing, // y + l + SafetySpacing); // // 碰撞检测 // foreach (var b in existing.Where(block => // Math.Abs(block.Z + block.H - z) < 0.001)) // 同平面检测 // { // var existArea = new Rectangle( // b.X - SafetySpacing, // b.Y - SafetySpacing, // b.X + b.W + SafetySpacing, // b.Y + b.L + SafetySpacing); // if (newArea.Intersects(existArea)) return false; // } // // 容器边界检查 // return x >= 0 && x + w <= ContainerX && // y >= 0 && y + l <= ContainerY; // } // /// // /// 计算吸盘参数(核心算法) // /// // private SuctionInfo CalculateSuctionParams(Block block, Point position, // int surfaceZ, int rotation, int placeW, int placeL) // { // var info = new SuctionInfo { Rotation = rotation }; // var suctionType = rotation % 180 == 0 ? 0 : 1; // // 计算理论中心点 // info.PlaceCenter = new int[3] { // position.X + placeW/2, // position.Y + placeL/2, // surfaceZ + block.H // }; // // 计算初始抓取中心 // CalculateInitialPickCenter(info, block, rotation, suctionType); // // 应用行程调整(规则6、7) // ApplyTravelAdjustment(info, suctionType); // // 应用旋转调整(180/270度) // ApplyRotationAdjustment(info, rotation, block); // return info; // } // /// // /// 计算初始抓取中心点 // /// // private void CalculateInitialPickCenter(SuctionInfo info, Block block, // int rotation, int suctionType) // { // switch (rotation % 360) // { // case 0: // 横向双吸盘 // case 180: // info.PickCenter = new int[3] { // block.W / 2, // X中心 // block.L / 2, // Y中心 // block.H // Z坐标 // }; // break; // case 90: // 纵向双吸盘 // case 270: // info.PickCenter = new int[3] { // block.W / 2, // X中心 // block.L / 2, // Y中心 // block.H // Z坐标 // }; // break; // } // } // /// // /// 应用行程调整(规则6、7) // /// // private void ApplyTravelAdjustment(SuctionInfo info, int suctionType) // { // // 计算理论行程需求 // int requiredTravel = info.PickCenter[1]; // // 当超过最大允许行程时进行调整 // if (requiredTravel > YAxisMax) // { // // 计算吸盘尺寸 // int suctionSize = SuctionSizes[suctionType].y; // // 计算调整后的抓取位置(尽量靠近中心) // int adjustedPickY = info.PlaceCenter[1] > YAxisMax // ? YAxisMax - suctionSize / 2 // 抓取远端 // : suctionSize / 2; // 抓取近端 // // 更新抓取中心并标记旋转 // info.PlaceCenter[1] = adjustedPickY; // info.Rotation += 180; // } // } // /// // /// 应用旋转调整(180/270度) // /// // private void ApplyRotationAdjustment(SuctionInfo info, int rotation, Block block) // { // if (rotation == 180) // { // // Y轴镜像 // info.PickCenter[1] = block.L - info.PickCenter[1]; // info.PlaceCenter[1] = ContainerY - info.PlaceCenter[1]; // } // else if (rotation == 270) // { // // X轴镜像 // info.PickCenter[0] = ContainerX - info.PickCenter[0]; // info.PlaceCenter[0] = ContainerX - info.PlaceCenter[0]; // } // } // /// // /// 最终放置验证 // /// // private bool ValidatePlacement(SuctionInfo info, List existing) // { // // 空间边界检查 // if (info.PlaceCenter[0] < 0 || info.PlaceCenter[0] > ContainerX) return false; // if (info.PlaceCenter[1] < 0 || info.PlaceCenter[1] > YAxisMax) return false; // if (info.PlaceCenter[2] > ContainerZ) return false; // // 投影覆盖检查 // var projection = new Rectangle( // info.PlaceCenter[0] - SuctionSizes[info.Rotation % 180 == 0 ? 0 : 1].x / 2, // info.PlaceCenter[1] - SuctionSizes[info.Rotation % 180 == 0 ? 0 : 1].y / 2, // info.PlaceCenter[0] + SuctionSizes[info.Rotation % 180 == 0 ? 0 : 1].x / 2, // info.PlaceCenter[1] + SuctionSizes[info.Rotation % 180 == 0 ? 0 : 1].y / 2 // ); // // 检查是否完全在支撑面内 // return existing.All(b => !projection.Intersects(new Rectangle( // b.X - SafetySpacing, // b.Y - SafetySpacing, // b.X + b.W + SafetySpacing, // b.Y + b.L + SafetySpacing // ))); // } //} //// 辅助类定义-------------------------------------------- //public class SupportSurface //{ // public int X { get; set; } // public int Y { get; set; } // public int Z { get; set; } // public int AvailableX { get; set; } // public int AvailableY { get; set; } //} //public struct Point //{ // public int X { get; } // public int Y { get; } // public Point(int x, int y) => (X, Y) = (x, y); //} //public class Rectangle //{ // public int Left { get; } // public int Top { get; } // public int Right { get; } // public int Bottom { get; } // public Rectangle(int x1, int y1, int x2, int y2) // { // Left = Math.Min(x1, x2); // Right = Math.Max(x1, x2); // Top = Math.Min(y1, y2); // Bottom = Math.Max(y1, y2); // } // public bool Intersects(Rectangle other) // { // return !(Right < other.Left || // Left > other.Right || // Bottom < other.Top || // Top > other.Bottom); // } //} #endregion