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度纵向
|
};
|
|
/// <summary>
|
/// 主计算方法
|
/// </summary>
|
public SuctionInfo CalculatePlacement(Block newBlock, List<Block> 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;
|
}
|
|
/// <summary>
|
/// 获取支撑面列表(容器底部+现有木块顶面)
|
/// </summary>
|
private List<SupportSurface> GetSupportSurfaces(List<Block> blocks)
|
{
|
var surfaces = new List<SupportSurface> {
|
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;
|
}
|
|
/// <summary>
|
/// 表面可行性检查
|
/// </summary>
|
private bool CanPlaceOnSurface(int placeW, int placeL,
|
SupportSurface surface, int blockH)
|
{
|
return surface.AvailableX >= placeW &&
|
surface.AvailableY >= placeL &&
|
surface.Z + blockH <= ContainerZ;
|
}
|
|
/// <summary>
|
/// 获取有效旋转角度(考虑规则5)
|
/// </summary>
|
private IEnumerable<int> 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;
|
}
|
|
/// <summary>
|
/// 计算实际放置尺寸(考虑旋转)
|
/// </summary>
|
private (int w, int l) GetPlacementDimensions(Block b, int rotation)
|
{
|
return rotation % 180 == 0 ? (b.W, b.L) : (b.L, b.W);
|
}
|
|
/// <summary>
|
/// 位置搜索算法(带安全间距)
|
/// </summary>
|
private Point? FindPosition(int placeW, int placeL,
|
SupportSurface surface, List<Block> 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;
|
}
|
|
/// <summary>
|
/// 位置有效性验证(含安全间距)
|
/// </summary>
|
private bool IsPositionValid(int x, int y, int w, int l,
|
int z, List<Block> 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;
|
}
|
|
/// <summary>
|
/// 计算吸盘参数(核心算法)
|
/// </summary>
|
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;
|
}
|
|
/// <summary>
|
/// 应用规则6调整:放置位置超过880mm时边缘抓取
|
/// </summary>
|
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;
|
}
|
|
/// <summary>
|
/// 应用规则7调整:抓取点尽量靠近中心
|
/// </summary>
|
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
|
};
|
}
|
|
/// <summary>
|
/// 应用旋转调整(180/270度)
|
/// </summary>
|
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];
|
}
|
}
|
|
/// <summary>
|
/// 最终放置验证
|
/// </summary>
|
private bool ValidatePlacement(SuctionInfo info, List<Block> 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度纵向
|
// };
|
|
// /// <summary>
|
// /// 主计算方法
|
// /// </summary>
|
// public SuctionInfo CalculatePlacement(Block newBlock, List<Block> 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;
|
// }
|
|
// /// <summary>
|
// /// 获取支撑面列表(容器底部+现有木块顶面)
|
// /// </summary>
|
// private List<SupportSurface> GetSupportSurfaces(List<Block> blocks)
|
// {
|
// var surfaces = new List<SupportSurface> {
|
// 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;
|
// }
|
|
// /// <summary>
|
// /// 表面可行性检查
|
// /// </summary>
|
// private bool CanPlaceOnSurface(int placeW, int placeL,
|
// SupportSurface surface, int blockH)
|
// {
|
// return surface.AvailableX >= placeW &&
|
// surface.AvailableY >= placeL &&
|
// surface.Z + blockH <= ContainerZ;
|
// }
|
|
// /// <summary>
|
// /// 获取有效旋转角度(考虑规则5)
|
// /// </summary>
|
// private IEnumerable<int> 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;
|
// }
|
|
// /// <summary>
|
// /// 计算实际放置尺寸(考虑旋转)
|
// /// </summary>
|
// private (int w, int l) GetPlacementDimensions(Block b, int rotation)
|
// {
|
// return rotation % 180 == 0 ? (b.W, b.L) : (b.L, b.W);
|
// }
|
|
// /// <summary>
|
// /// 位置搜索算法(带安全间距)
|
// /// </summary>
|
// private Point? FindPosition(int placeW, int placeL,
|
// SupportSurface surface, List<Block> 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;
|
// }
|
|
// /// <summary>
|
// /// 位置有效性验证(含安全间距)
|
// /// </summary>
|
// private bool IsPositionValid(int x, int y, int w, int l,
|
// int z, List<Block> 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;
|
// }
|
|
// /// <summary>
|
// /// 计算吸盘参数(核心算法)
|
// /// </summary>
|
// 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;
|
// }
|
|
// /// <summary>
|
// /// 计算初始抓取中心点
|
// /// </summary>
|
// 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;
|
// }
|
// }
|
|
// /// <summary>
|
// /// 应用行程调整(规则6、7)
|
// /// </summary>
|
// 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;
|
// }
|
// }
|
|
// /// <summary>
|
// /// 应用旋转调整(180/270度)
|
// /// </summary>
|
// 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];
|
// }
|
// }
|
|
// /// <summary>
|
// /// 最终放置验证
|
// /// </summary>
|
// private bool ValidatePlacement(SuctionInfo info, List<Block> 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
|