using HeuristicLab.Common;
using HeuristicLab.Problems.BinPacking3D.Geometry;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HeuristicLab.Problems.BinPacking3D.ExtremePointCreation {
///
/// This extreme point creation class uses the line projection based method for creating extreme points.
///
public class LineProjectionBasedEPCreator : ExtremePointCreator {
public override void UpdateExtremePoints(BinPacking3D binPacking, PackingItem item, PackingPosition position) {
// Before any extreme point for an item can be created, each residual space must be resized if the new item is in the residual space.
// If the residual spaces were not updated, it could be that an extreme point won't be generated because it lies in a residual space which
// size isn't correct anymore.
RecalculateResidualSpaces(binPacking, item, position);
GenerateNewExtremePointsForNewItem(binPacking, item, position);
foreach (var ep in GetEpsOnLeft(binPacking, item, position)) {
AddExtremePoint(binPacking, ep);
}
foreach (var ep in GetEpsBelow(binPacking, item, position)) {
AddExtremePoint(binPacking, ep);
}
foreach (var ep in GetEpsBehind(binPacking, item, position)) {
AddExtremePoint(binPacking, ep);
}
}
///
/// Returns true, if the given poisition and the related residual space is within the residual space of the given extreme point
///
/// New Position
///
///
///
///
protected override bool IsWithinResidualSpaceOfAnotherExtremePoint(Vector3D pos, Tuple rsPos, PackingPosition ep, Tuple rsEp) {
bool x = pos.X >= ep.X && rsPos.Item1 + pos.X <= rsEp.Item1 + ep.X;
bool y = (pos.Y >= ep.Y && pos.Y == 0 || pos.Y > ep.Y && pos.Y > 0) && rsPos.Item2 + pos.Y <= rsEp.Item2 + ep.Y;
bool z = pos.Z >= ep.Z && rsPos.Item3 + pos.Z <= rsEp.Item3 + ep.Z;
return x && y && z;
}
public override void UpdateResidualSpace(BinPacking3D binPacking, PackingItem item, PackingPosition position) {
foreach (var ep in binPacking.ExtremePoints.ToList()) {
var rs = binPacking.ResidualSpace[ep];
var depth = position.Rotated ? item.Width : item.Depth;
var width = position.Rotated ? item.Depth : item.Width;
var changed = false;
if (ep.Z >= position.Z && ep.Z < position.Z + depth) {
if (ep.X <= position.X && ep.Y >= position.Y && ep.Y < position.Y + item.Height) {
var diff = position.X - ep.X;
var newRSX = Math.Min(rs.Item1, diff);
rs = Tuple.Create(newRSX, rs.Item2, rs.Item3);
changed = true;
}
if (ep.Y <= position.Y && ep.X >= position.X && ep.X < position.X + width) {
var diff = position.Y - ep.Y;
var newRSY = Math.Min(rs.Item2, diff);
rs = Tuple.Create(rs.Item1, newRSY, rs.Item3);
changed = true;
}
}
if (ep.Z <= position.Z &&
ep.Y >= position.Y && ep.Y < position.Y + item.Height &&
ep.X >= position.X && ep.X < position.X + width) {
var diff = position.Z - ep.Z;
var newRSZ = Math.Min(rs.Item3, diff);
rs = Tuple.Create(rs.Item1, rs.Item2, newRSZ);
changed = true;
}
if (changed) {
if (IsNonZero(rs) && !IsWithinResidualSpaceOfAnotherExtremePoint(binPacking, new Vector3D(ep), rs)) {
binPacking.ResidualSpace[ep] = rs;
} else {
binPacking.ExtremePoints.Remove(ep);
binPacking.ResidualSpace.Remove(ep);
}
}
}
return;
}
protected override bool AddExtremePoint(BinPacking3D binPacking, PackingPosition position) {
if (binPacking.ExtremePoints.Add(position)) {
var rs = CalculateResidualSpace(binPacking, new Vector3D(position));
binPacking.ResidualSpace.Add(position, rs);
// Check if the existing extreme points are shadowed by the new point
// This is, their residual space fit entirely into the residual space of the new point
foreach (var ep in binPacking.ExtremePoints.Where(x => x != position && new Vector3D(x).IsInside(position, rs)).ToList()) {
if (IsWithinResidualSpaceOfAnotherExtremePoint(new Vector3D(ep), binPacking.ResidualSpace[ep], position, rs)) {
binPacking.ExtremePoints.Remove(ep);
binPacking.ResidualSpace.Remove(ep);
}
}
return true;
}
return false;
}
///
/// Returns true if an item is in the residual space of a given extrem point
///
/// KeyValuePair with the position of the extreme point and the size of the residual space
/// Given Item
/// Given position
///
private bool ItemIsInRs(KeyValuePair> rs, PackingItem item, PackingPosition position) {
return GetVertices(item, position).Where(pos => pos.IsInside(rs.Key, rs.Value)).Any();
}
///
/// Recalculates the residual spaces if needed.
/// It checks if an item is in an residual space and if so the residual space will be recalculated
///
///
///
///
private void RecalculateResidualSpaces(BinPacking3D binPacking, PackingItem item, PackingPosition position) {
var recalculatedSpaces = new Dictionary>();
foreach (var rs in binPacking.ResidualSpace) {
int width = rs.Value.Item1;
int height = rs.Value.Item2;
int depth = rs.Value.Item3;
if (ItemIsInRs(rs, item, position)) {
if (rs.Key.X + rs.Value.Item1 > position.X) {
width = position.X - rs.Key.X;
} else if (rs.Key.Y + rs.Value.Item2 > position.Y) {
height = position.Y - rs.Key.Y;
} else if (rs.Key.Z + rs.Value.Item3 > position.Z) {
depth = position.Z - rs.Key.Z;
}
}
var newRs = new Tuple(width, height, depth);
if (IsNonZero(newRs)) {
recalculatedSpaces.Add(rs.Key, newRs);
} else {
recalculatedSpaces.Add(rs.Key, rs.Value);
}
}
binPacking.ResidualSpace = recalculatedSpaces;
}
///
/// Returns the extremepoints on the left side of an given item
///
///
///
///
private IList GetEpsOnLeft(BinPacking3D binPacking, PackingItem item, PackingPosition position) {
IList eps = new List();
IEnumerable> items = GetItemsOnLeft(binPacking, position);
var edges = GetProjectionEdgesOnLeft(item, position);
foreach (var i in items) {
foreach (var edge in edges) {
var newEps = IntersectionsForItem(edge, GetEdgesOnRight(i.Item2, i.Item1));
foreach (var ep in newEps) {
try {
if (ep.X < binPacking.BinShape.Width && ep.Y < binPacking.BinShape.Height && ep.Z < binPacking.BinShape.Depth) {
var point = ProjectLeft(binPacking, ep);
var residualSpace = CalculateResidualSpace(binPacking, point);
if (IsNonZero(residualSpace)) {
eps.Add(new PackingPosition(position.AssignedBin, point.X, point.Y, point.Z));
}
}
} catch {
var s = ep;
}
}
}
}
return eps;
}
///
/// Returns the extremepoints below of an given item
///
///
///
///
private IList GetEpsBelow(BinPacking3D binPacking, PackingItem item, PackingPosition position) {
IList eps = new List();
IEnumerable> items = GetItemsBelow(binPacking, position);
var edges = GetProjectionEdgesBelow(item, position);
foreach (var i in items) {
foreach (var edge in edges) {
var newEps = IntersectionsForItem(edge, GetEdgesOnTop(i.Item2, i.Item1));
foreach (var ep in newEps) {
try {
var point = ProjectDown(binPacking, ep);
var residualSpace = CalculateResidualSpace(binPacking, point);
if (IsNonZero(residualSpace)) {
eps.Add(new PackingPosition(position.AssignedBin, point.X, point.Y, point.Z));
}
} catch {
var s = ep;
}
}
}
}
return eps;
}
///
/// Returns the extremepoints on the left side of an given item
///
///
///
///
private IList GetEpsBehind(BinPacking3D binPacking, PackingItem item, PackingPosition position) {
IList eps = new List();
IEnumerable> items = GetItemsBehind(binPacking, position);
var edges = GetProjectionEdgesBehind(item, position);
foreach (var i in items) {
foreach (var edge in edges) {
var newEps = IntersectionsForItem(edge, GetEdgesInFront(i.Item2, i.Item1));
foreach (var ep in newEps) {
try {
var point = ProjectBackward(binPacking, ep);
var residualSpace = CalculateResidualSpace(binPacking, point);
if (IsNonZero(residualSpace)) {
eps.Add(new PackingPosition(position.AssignedBin, point.X, point.Y, point.Z));
}
} catch {
var s = ep;
}
}
}
}
return eps;
}
#region Methods for getting the edges for items
///
/// Returns an array of packing position which represents the vertices of an item.
/// The position of a vertex in the array is mapped to an item as followed:
/// 4----------5
/// /| /|
/// / | / |
/// / 0-------/--1
/// 6--/-------7 /
/// | / | /
/// |/ |/
/// 2----------3
///
/// 0 = (0,0,0)
///
///
///
///
private Vector3D[] GetVertices(PackingItem item, PackingPosition position) {
int width = position.Rotated ? item.Depth : item.Width;
int depth = position.Rotated ? item.Width : item.Depth;
return new Vector3D[] {
new Vector3D(position.X + 0, position.Y + 0, position.Z + 0), // (0,0,0)
new Vector3D(position.X + width, position.Y + 0, position.Z + 0), // (x,0,0)
new Vector3D(position.X + 0, position.Y + 0, position.Z + depth), // (0,0,z)
new Vector3D(position.X + width, position.Y + 0, position.Z + depth), // (x,0,z)
new Vector3D(position.X + 0, position.Y + item.Height, position.Z + 0), // (0,y,0)
new Vector3D(position.X + width, position.Y + item.Height, position.Z + 0), // (x,y,0)
new Vector3D(position.X + 0, position.Y + item.Height, position.Z + depth), // (0,y,z)
new Vector3D(position.X + width, position.Y + item.Height, position.Z + depth), //(x,y,z)
};
}
private Edge3D[] GetProjectionEdgesOnLeft(PackingItem item, PackingPosition position) {
Vector3D[] points = GetVertices(item, position);
return new Edge3D[] {
new Edge3D(points[2], points[6]),
new Edge3D(points[6], points[4])
};
}
private Edge3D[] GetProjectionEdgesBelow(PackingItem item, PackingPosition position) {
Vector3D[] points = GetVertices(item, position);
return new Edge3D[] {
new Edge3D(points[2], points[3]),
new Edge3D(points[3], points[1])
};
}
private Edge3D[] GetProjectionEdgesBehind(PackingItem item, PackingPosition position) {
Vector3D[] points = GetVertices(item, position);
return new Edge3D[] {
new Edge3D(points[1], points[5]),
new Edge3D(points[5], points[4])
};
}
///
/// Returns an array of edges which contains all edges of the rigth side of an given item.
///
///
///
///
private Edge3D[] GetEdgesOnRight(PackingItem item, PackingPosition position) {
Vector3D[] points = GetVertices(item, position);
return new Edge3D[] {
new Edge3D(points[1], points[5]),
new Edge3D(points[5], points[7]),
new Edge3D(points[7], points[3]),
new Edge3D(points[3], points[1])
};
}
///
/// Returns an array of edges which contains all edges on the top of an given item.
///
///
///
///
private Edge3D[] GetEdgesOnTop(PackingItem item, PackingPosition position) {
Vector3D[] points = GetVertices(item, position);
return new Edge3D[] {
new Edge3D(points[4], points[5]),
new Edge3D(points[5], points[7]),
new Edge3D(points[7], points[6]),
new Edge3D(points[6], points[4])
};
}
///
/// Returns an array of edges which contains all edges in front of an given item.
///
///
///
///
private Edge3D[] GetEdgesInFront(PackingItem item, PackingPosition position) {
Vector3D[] points = GetVertices(item, position);
return new Edge3D[] {
new Edge3D(points[2], points[3]),
new Edge3D(points[3], points[7]),
new Edge3D(points[7], points[6]),
new Edge3D(points[6], points[2])
};
}
#endregion
#region Intersections
///
/// Returns a list of extreme points.
///
///
///
///
///
private IEnumerable IntersectionsForItem(Edge3D projectedEdge, Edge3D[] edges) {
IList eps = new List();
foreach (var edge in edges) {
var ep = edge.Intersects(projectedEdge);
if (ep != null) {
eps.Add(ep);
}
}
return eps as IEnumerable;
}
#endregion
}
}