#region License Information /* HeuristicLab * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using HeuristicLab.Common; using HeuristicLab.Problems.BinPacking3D.ExtremePointPruning; using HeuristicLab.Problems.BinPacking3D.Geometry; using HeuristicLab.Problems.BinPacking3D.ResidualSpaceCalculation; 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 point projection based method by Crainic, T. G., Perboli, G., & Tadei, R. for creating extreme points. /// Each extreme point of an item is being projectet backwards, downwards and to the left. /// A new extreme point will be created where this projections instersects with another item or the bin boundins. /// public class PointProjectionBasedEPCreator : ExtremePointCreator { /// /// This lock object is needed because of creating the extreme points in an parallel for loop. /// protected object _lockAddExtremePoint = new object(); protected override void UpdateExtremePoints(BinPacking3D binPacking, PackingItem item, PackingPosition position) { binPacking.ExtremePoints.Clear(); if (binPacking.Items.Count <= 0) { return; } // generate all new extreme points parallel. This speeds up the creator. Parallel.ForEach(binPacking.Items, i => { PackingItem it = i.Value; PackingPosition pos = binPacking.Positions[i.Key]; GenerateNewExtremePointsForItem(binPacking, it, pos); }); // remove not needed extreme points. foreach (var extremePoint in binPacking.ExtremePoints.ToList()) { // check if a residual space can be removed foreach (var rs in extremePoint.Value.ToList()) { if (ResidualSpaceCanBeRemoved(binPacking, extremePoint.Key, rs)) { ((IList)extremePoint.Value).Remove(rs); } } // if the current extreme point has no more residual spaces, it can be removed. if (((IList)extremePoint.Value).Count <= 0) { binPacking.ExtremePoints.Remove(extremePoint); } } } /// /// Returns true if a given residual space can be removed. /// The given residual space can be removed if it is within another residual space and /// - neither the position of the other residual space and the current extreme point have an item below or /// - the current extreme point has an item below. /// /// /// /// /// private bool ResidualSpaceCanBeRemoved(BinPacking3D binPacking, PackingPosition position, ResidualSpace rs) { foreach (var extremePoint in binPacking.ExtremePoints) { if (position.Equals(extremePoint.Key)) { continue; } if (IsWithinResidualSpaceOfAnotherExtremePoint(new Vector3D(position), rs, extremePoint.Key, extremePoint.Value)) { var itemBelowEp = LiesOnAnyItem(binPacking, extremePoint.Key); var itemBelowPos = LiesOnAnyItem(binPacking, position); if (itemBelowEp || !itemBelowEp && !itemBelowPos) { return true; } } } return false; } /// /// Returns true if the given position lies on an item or an the ground. /// /// /// /// private bool LiesOnAnyItem(BinPacking3D binPacking, PackingPosition position) { if (position.Y == 0) { return true; } var items = binPacking.Items.Where(x => { var itemPosition = binPacking.Positions[x.Key]; var item = x.Value; int width = item.Width; int depth = item.Depth; return itemPosition.Y + item.Height == position.Y && itemPosition.X <= position.X && position.X < itemPosition.X + width && itemPosition.Z <= position.Z && position.Z < itemPosition.Z + depth; }); return items.Count() > 0; } /// /// Adds a new extreme point an the related residual spaces to a given bin packing. /// - The given position has to be valid. /// - The extreme point does not exist in the bin packing. /// - There must be at minimum one valid residual space. A residual space is invalid if the space is zero. /// /// /// /// True = the given point and its related residual spaces were successfully added to the bin packing protected override bool AddExtremePoint(BinPacking3D binPacking, PackingPosition position) { if (position == null) { return false; } if (PointIsInAnyItem(binPacking, new Vector3D(position))) { return false; } // this is necessary if the extreme points are being created in a parallel loop. lock (_lockAddExtremePoint) { if (binPacking.ExtremePoints.ContainsKey(position)) { return false; } var rs = CalculateResidualSpace(binPacking, new Vector3D(position)); if (rs.Count() <= 0) { return false; } binPacking.ExtremePoints.Add(position, rs); return true; } } /// /// Getnerates the extreme points for a given item. /// It creates extreme points by using a point projection based method and /// creates points by using an edge projection based method. /// /// /// /// protected override void GenerateNewExtremePointsForItem(BinPacking3D binPacking, PackingItem newItem, PackingPosition position) { PointProjectionForNewItem(binPacking, newItem, position); } #region Extreme point creation by using a point projection based method /// /// This method creates extreme points by using a point projection based method. /// For each item there will be created three points and each of the points will be projected twice. /// The direction of the projection depends on position of the point. /// /// /// /// protected void PointProjectionForNewItem(BinPacking3D binPacking, PackingItem newItem, PackingPosition position) { int newWidth = newItem.Width; int newDepth = newItem.Depth; var binShape = binPacking.BinShape; var sourcePoint = new PackingPosition(position.AssignedBin, position.X + newWidth, position.Y, position.Z); PointProjection(binPacking, sourcePoint, ProjectDown); PointProjection(binPacking, sourcePoint, ProjectBackward); sourcePoint = new PackingPosition(position.AssignedBin, position.X, position.Y + newItem.Height, position.Z); PointProjection(binPacking, sourcePoint, ProjectLeft); PointProjection(binPacking, sourcePoint, ProjectBackward); sourcePoint = new PackingPosition(position.AssignedBin, position.X, position.Y, position.Z + newDepth); PointProjection(binPacking, sourcePoint, ProjectDown); PointProjection(binPacking, sourcePoint, ProjectLeft); } /// /// Projects a given point by using the given projection method to the neares item. /// The given projection method returns a point which lies on a side of the nearest item in the direction. /// /// /// /// private void PointProjection(BinPacking3D binPacking, PackingPosition position, Func projectionMethod) { Vector3D sourcePoint = new Vector3D(position); if (sourcePoint.X < binPacking.BinShape.Width && sourcePoint.Y < binPacking.BinShape.Height && sourcePoint.Z < binPacking.BinShape.Depth) { Vector3D point = projectionMethod?.Invoke(binPacking, sourcePoint); if (point != null) { AddExtremePoint(binPacking, new PackingPosition(position.AssignedBin, point.X, point.Y, point.Z)); } } } #endregion /// /// Updates the residual spaces. /// It removes not needed ones. /// A residual space will be removed if the space is a subspace of another one and /// the current one has no item below or both have an item below. /// /// /// /// protected override void UpdateResidualSpace(BinPacking3D binPacking, PackingItem item, PackingPosition position) { } /// /// Returns true if any item in the bin packing encapsulates the given point /// /// /// /// private bool PointIsInAnyItem(BinPacking3D binPacking, Vector3D point) { foreach (var item in binPacking.Items) { PackingPosition position = binPacking.Positions[item.Key]; var depth = item.Value.Depth; var width = item.Value.Width; if (position.X <= point.X && point.X < position.X + width && position.Y <= point.Y && point.Y < position.Y + item.Value.Height && position.Z <= point.Z && point.Z < position.Z + depth) { return true; } } return false; } /// /// Calculates the residual spaces for an extreme point. /// /// /// /// protected override IEnumerable CalculateResidualSpace(BinPacking3D binPacking, Vector3D pos) { return ResidualSpaceCalculatorFactory.CreateCalculator().CalculateResidualSpaces(binPacking, pos); } } }