using System;
|
using System.Collections.Generic;
|
using System.Linq;
|
using System.Text;
|
using System.Threading.Tasks;
|
using WIDESEAWCS_DTO.PlacedBlockDTO;
|
using WIDESEAWCS_IBasicInfoService;
|
|
namespace WIDESEAWCS_BasicInfoService
|
{
|
public class PlaceBlockService2
|
{
|
public const int SPACING = 5;
|
|
/// <summary>
|
/// 容器尺寸
|
/// </summary>
|
public ContainerSize ContainerSize { get; private set; }
|
|
/// <summary>
|
/// 已放置货物
|
/// </summary>
|
public List<PlacedBlock> PlacedBlocks { get; private set; }
|
|
private readonly PlacedBlock containerFloor;
|
|
public PlaceBlockService2(ContainerSize containerSize, List<PlacedBlock>? placedBlocks = null)
|
{
|
containerSize.Length = containerSize.Length + 2 * SPACING;
|
containerSize.Width = containerSize.Width + 2 * SPACING;
|
ContainerSize = containerSize;
|
if (placedBlocks == null || placedBlocks.Count == 0)
|
{
|
PlacedBlocks = new List<PlacedBlock>();
|
}
|
else
|
{
|
PlacedBlocks = placedBlocks;
|
}
|
|
|
containerFloor = new PlacedBlock(new Point3D(SPACING, SPACING, 0), ContainerSize.Length - 2 * SPACING, ContainerSize.Width - 2 * SPACING, 0);
|
}
|
|
/// <summary>
|
/// 主放置方法:尝试放置指定尺寸的货物
|
/// </summary>
|
/// <param name="length">货物长度(X轴方向)</param>
|
/// <param name="width">货物宽度(Y轴方向)</param>
|
/// <param name="height">货物高度(Z轴方向)</param>
|
/// <returns>
|
/// 成功:返回可放置位置的左下前角Point3D坐标
|
/// 失败:返回null(尺寸无效或空间不足)
|
/// </returns>
|
public Point3D? PlaceBlock(int length, int width, int height)
|
{
|
if (!IsValidBlock(length, width, height))
|
return null;
|
|
return FindStackablePosition(length, width, height);
|
}
|
|
/// <summary>
|
/// 验证货物尺寸有效性
|
/// 校验规则:
|
/// 1. 各维度尺寸必须大于等于50mm
|
/// 2. 各维度尺寸不得超过容器对应维度(扣除间隔后)
|
/// 3. 高度必须 <= 容器剩余高度
|
/// </summary>
|
/// <param name="l"></param>
|
/// <param name="w"></param>
|
/// <param name="h"></param>
|
/// <returns></returns>
|
private bool IsValidBlock(int l, int w, int h)
|
{
|
return l > 0 && w > 0 && h > 0 &&
|
l <= ContainerSize.Length - 2 * SPACING &&
|
w <= ContainerSize.Width - 2 * SPACING &&
|
h < ContainerSize.Height;
|
}
|
|
/// <summary>
|
/// 在现有货物顶部寻找叠放位置(堆叠模式)
|
/// 叠放条件:
|
/// - 支撑面积 >= 被支撑面面积的70%
|
/// - 新货物完全位于支撑货物上方
|
/// - 满足间隔要求
|
/// </summary>
|
/// <param name="l">长度</param>
|
/// <param name="w">宽度</param>
|
/// <param name="h">高度</param>
|
/// <returns></returns>
|
private Point3D? FindStackablePosition(int l, int w, int h)
|
{
|
// 生成候选支撑层(包含容器底部)
|
var candidateLayers = PlacedBlocks
|
.Select(b => b.Position.Z + b.Height)
|
.Append(0)
|
.Distinct()
|
.OrderBy(z => z)
|
.ToList();
|
|
foreach (var baseZ in candidateLayers)
|
{
|
if (baseZ + h > ContainerSize.Height) continue;
|
|
// 获取当前层的支撑块(包含虚拟容器底部)
|
var supports = GetSupportBlocks(baseZ);
|
|
foreach (var support in supports)
|
{
|
// 计算实际接触面积
|
int overlapX = Math.Min(support.Position.X + support.Length, l) - Math.Max(support.Position.X, 0);
|
int overlapY = Math.Min(support.Position.Y + support.Width, w) - Math.Max(support.Position.Y, 0);
|
int contactArea = overlapX * overlapY;
|
int blockArea = l * w;
|
|
if (contactArea < blockArea * 0.7) continue;
|
|
// 计算有效叠放区域(修正间隔逻辑)
|
int xStart = support.Position.X;
|
int yStart = support.Position.Y;
|
|
// 容器底部支撑必须内缩间隔(即使尺寸相同)
|
if (support == containerFloor)
|
{
|
xStart = support.Position.X;
|
yStart = support.Position.Y;
|
}
|
|
int xEnd = support.Position.X + support.Length - l;
|
int yEnd = support.Position.Y + support.Width - w;
|
|
// 普通支撑块仅在尺寸不同时加间隔
|
if (support != containerFloor &&
|
(l != support.Length && w != support.Width))
|
{
|
xStart += SPACING;
|
yStart += SPACING;
|
xEnd -= SPACING;
|
yEnd -= SPACING;
|
}
|
|
// 最终容器边界约束
|
xEnd = Math.Min(xEnd, ContainerSize.Length - l - SPACING);
|
yEnd = Math.Min(yEnd, ContainerSize.Width - w - SPACING);
|
|
// 验证支撑面积
|
//int supportArea = support.Length * support.Width;
|
//int requiredSupportArea = (int)(l * w * 0.7); // 支撑面积 >= 被支撑面面积的70%
|
//if (supportArea < requiredSupportArea) continue;
|
|
if (xStart > xEnd || yStart > yEnd) continue;
|
|
// 优化搜索:优先角落位置
|
for (int y = yStart; y <= yEnd; y += 10)
|
{
|
for (int x = xStart; x <= xEnd; x += 10)
|
{
|
var candidate = new Point3D(x, y, baseZ);
|
if (IsPositionValid(candidate, l, w, h))
|
{
|
var placed = new PlacedBlock(candidate, l, w, h);
|
//PlacedBlocks.Add(placed);
|
return candidate;
|
}
|
}
|
}
|
}
|
}
|
return null;
|
}
|
|
/// <summary>
|
/// 获取支撑当前货物的底层货物列表
|
/// 特殊处理:当baseZ=0时返回虚拟容器底部作为支撑
|
/// 支撑条件:货物底面与支撑货物顶面接触且Z坐标匹配
|
/// </summary>
|
/// <param name="baseZ">支撑块高度</param>
|
/// <returns></returns>
|
private IEnumerable<PlacedBlock> GetSupportBlocks(int baseZ)
|
{
|
var blocks = PlacedBlocks
|
.Where(b => b.Position.Z + b.Height == baseZ)
|
.OrderByDescending(b => b.Length * b.Width).ToList();
|
|
// 当baseZ=0时添加容器底部支撑
|
if (baseZ == 0 && blocks.Count == 0)
|
{
|
return new List<PlacedBlock> { containerFloor };
|
}
|
|
return blocks;
|
|
}
|
|
private bool IsPositionValid(Point3D pos, int l, int w, int h)
|
{
|
// 容器边界校验(原有逻辑)
|
if (pos.X + l > ContainerSize.Length || pos.Y + w > ContainerSize.Width)
|
return false;
|
|
// 碰撞检测(原有逻辑)
|
var newBlock = new PlacedBlock(pos, l, w, h);
|
foreach (var existing in PlacedBlocks)
|
{
|
if (existing.Intersects(newBlock))
|
return false;
|
}
|
|
// 新增支撑面积校验(需要与FindStackablePosition中的计算保持同步)
|
if (pos.Z > 0)
|
{
|
var contactArea = GetContactArea(pos, l, w);
|
if (contactArea < l * w * 0.7)
|
return false;
|
}
|
|
return true;
|
}
|
|
// 新增接触面积计算方法
|
private int GetContactArea(Point3D pos, int l, int w)
|
{
|
return PlacedBlocks
|
.Where(b => b.Position.Z + b.Height == pos.Z)
|
.Sum(b => {
|
int overlapX = Math.Min(b.Position.X + b.Length, pos.X + l) - Math.Max(b.Position.X, pos.X);
|
int overlapY = Math.Min(b.Position.Y + b.Width, pos.Y + w) - Math.Max(b.Position.Y, pos.Y);
|
return overlapX * overlapY;
|
});
|
}
|
}
|
}
|