Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2817-BinPackingSpeedup/HeuristicLab.Problems.BinPacking/3.3/3D/Packer/BinPackerMinRSLeft.cs

Last change on this file was 15844, checked in by rhanghof, 6 years ago

#2817:

  • Some bug fixes at the placing algorithm
File size: 17.6 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using System.Text;
26using System.Threading.Tasks;
27using HeuristicLab.Common;
28using HeuristicLab.Encodings.PermutationEncoding;
29using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
30using HeuristicLab.Problems.BinPacking3D.ExtremePointCreation;
31using HeuristicLab.Problems.BinPacking3D.ExtremePointPruning;
32
33namespace HeuristicLab.Problems.BinPacking3D.Packer {
34  internal class BinPackerMinRSLeft : BinPacker {
35    #region Constructors for HEAL
36    [StorableConstructor]
37    protected BinPackerMinRSLeft(bool deserializing) : base(deserializing) { }
38
39    public BinPackerMinRSLeft(BinPackerMinRSLeft original, Cloner cloner) : base(original, cloner) {
40    }
41
42    public override IDeepCloneable Clone(Cloner cloner) {
43      return new BinPackerMinRSLeft(this, cloner);
44    }
45    #endregion
46
47
48
49    public BinPackerMinRSLeft() : base() { }
50
51    /// <summary>
52    /// This proportion of the residual space left to the item height is used for deciding if a not stackable item should be placed.
53    /// </summary>
54    private const double NOT_STACKABLE_RS_LEFT_TO_ITEM_HEIGHT_PROPORTION = 1.1;
55
56    public override IList<BinPacking3D> PackItems(Permutation sortedItems, PackingShape binShape, IList<PackingItem> items, ExtremePointCreationMethod epCreationMethod, ExtremePointPruningMethod epPruningMethod, bool useStackingConstraints) {
57      var workingItems = CloneItems(items);
58      IList<BinPacking3D> packingList = new List<BinPacking3D>();
59      IList<int> remainingIds = new List<int>(sortedItems);
60
61      try {
62        while (remainingIds.Count > 0) {
63          BinPacking3D packingBin = new BinPacking3D(binShape);
64          PackRemainingItems(ref remainingIds, ref packingBin, workingItems, epCreationMethod, useStackingConstraints);
65          packingList.Add(packingBin);
66        }
67      } catch (BinPacking3DException e) {
68      }
69
70      ExtremePointPruningFactory.CreatePruning(epPruningMethod).PruneExtremePoints(packingList);
71
72      return packingList;
73    }
74
75
76    public override void PackItemsToPackingList(IList<BinPacking3D> packingList, Permutation sortedItems, PackingShape binShape, IList<PackingItem> items, ExtremePointCreationMethod epCreationMethod, ExtremePointPruningMethod epPruningMethod, bool useStackingConstraints) {
77      var workingItems = CloneItems(items);
78      IList<int> remainingIds = new List<int>(sortedItems);
79
80      try {
81        if (packingList.Count > 0) {
82          BinPacking3D packingBin = packingList.Last();
83          PackRemainingItems(ref remainingIds, ref packingBin, workingItems, epCreationMethod, useStackingConstraints);
84        }
85
86        while (remainingIds.Count > 0) {
87          BinPacking3D packingBin = new BinPacking3D(binShape);
88          PackRemainingItems(ref remainingIds, ref packingBin, workingItems, epCreationMethod, useStackingConstraints);
89          packingList.Add(packingBin);
90        }
91      } catch (BinPacking3DException e) {
92      }
93
94      ExtremePointPruningFactory.CreatePruning(epPruningMethod).PruneExtremePoints(packingList);
95    }
96
97
98
99    /// <summary>
100    /// Tries to pack the remainig items into a given BinPacking3D object. Each item could be packed into the BinPacking3D object will be removed from the list of remaining ids
101    /// </summary>
102    /// <param name="remainingIds">List of remaining ids. After the method has been executed the list has to have less items</param>
103    /// <param name="packingBin">This object will be filled with some of the given items</param>
104    /// <param name="items">List of packing items. Some of the items will be assigned to the BinPacking3D object</param>
105    /// <param name="epCreationMethod"></param>
106    /// <param name="useStackingConstraints"></param>
107    protected virtual void PackRemainingItems(ref IList<int> remainingIds, ref BinPacking3D packingBin, IList<PackingItem> items, ExtremePointCreationMethod epCreationMethod, bool useStackingConstraints) {
108      IExtremePointCreator extremePointCreator = ExtremePointCreatorFactory.CreateExtremePointCreator(epCreationMethod, useStackingConstraints);
109
110      var remainingNotWeightSupportedItems = new List<int>();
111      foreach (var itemId in new List<int>(remainingIds)) {
112        var item = items[itemId];
113        var clonedPackingBin = packingBin.Clone() as BinPacking3D;
114        ExtremePointPruningFactory.CreatePruning(ExtremePointPruningMethod.PruneBehind).PruneExtremePoints(clonedPackingBin, item.SequenceGroup - 1);
115
116        // If an item doesn't support any weight it should have a minimum waste of the residual space.
117        // As long as there are weight supporting items left, put the non supporting items into a collection
118        // and try to find positions where they don't waste too much space.
119        // If there are no weight supporting items left the non supporting ones have to be treated as a supporting one.
120        if (item.IsStackabel && item.SupportedWeight <= 0 && useStackingConstraints && remainingIds.Any(x => items[x].SupportedWeight > 0)) {
121          remainingNotWeightSupportedItems.Add(itemId);
122        } else if (!item.IsStackabel) {
123          PackingPosition position = FindPackingPositionForNotStackableItem(clonedPackingBin, item, useStackingConstraints);
124          // if a valid packing position could be found, the current item can be added to the given bin
125          if (position != null) {
126            PackItem(packingBin, itemId, item, position, extremePointCreator, useStackingConstraints);
127            remainingIds.Remove(itemId);
128          }
129        } else {
130          PackingPosition position = FindPackingPositionForItem(clonedPackingBin, item, useStackingConstraints);
131          // if a valid packing position could be found, the current item can be added to the given bin
132          if (position != null) {
133            PackItem(packingBin, itemId, item, position, extremePointCreator, useStackingConstraints);
134            remainingIds.Remove(itemId);
135          }
136        }
137
138        // try to find a valid position for a non weight supporting item
139        var weightSupportedLeft = remainingIds.Any(x => items[x].SupportedWeight > 0);
140        foreach (var saId in new List<int>(remainingNotWeightSupportedItems)) {
141          item = items[saId];
142          PackingPosition position = null;
143          if (weightSupportedLeft) {
144            position = FindPackingPositionForWeightUnsupportedItem(clonedPackingBin, item, useStackingConstraints);
145          } else {
146            position = FindPackingPositionForItem(clonedPackingBin, item, useStackingConstraints);
147          }
148
149          if (position != null) {
150            PackItem(packingBin, saId, item, position, extremePointCreator, useStackingConstraints);
151            remainingIds.Remove(saId);
152            remainingNotWeightSupportedItems.Remove(saId);
153          }
154        }
155      }
156    }
157
158    /// <summary>
159    /// Finds a valid packing position for a not stackable item.
160    /// Not stackable means that an item can't be placed on another one and no other item can be placed on it.
161    /// The item will be rotated and tilted so it needs the minimum area.
162    /// </summary>
163    /// <param name="packingBin"></param>
164    /// <param name="packingItem"></param>
165    /// <param name="useStackingConstraints"></param>
166    /// <returns></returns>
167    private PackingPosition FindPackingPositionForNotStackableItem(BinPacking3D packingBin, PackingItem packingItem, bool useStackingConstraints) {
168      if (!useStackingConstraints) {
169        return FindPackingPositionForItem(packingBin, packingItem, useStackingConstraints);
170      }
171      var areas = new List<Tuple<double, bool, bool>>();
172      areas.Add(CalculateArea(packingItem, false, false));
173      if (packingItem.TiltEnabled) {
174        areas.Add(CalculateArea(packingItem, false, true));
175      }
176      if (packingItem.RotateEnabled) {
177        areas.Add(CalculateArea(packingItem, true, false));
178      }
179      if (packingItem.RotateEnabled && packingItem.TiltEnabled) {
180        areas.Add(CalculateArea(packingItem, true, true));
181      }
182
183      areas.Sort((x1, x2) => (int)(x1.Item1 - x2.Item1));
184      var orientation = areas.FirstOrDefault();
185      if (orientation == null) {
186        return null;
187      }
188
189
190      PackingItem newItem = new PackingItem(packingItem) {
191        Rotated = orientation.Item2,
192        Tilted = orientation.Item3
193      };
194
195      var rsds = new SortedSet<ResidualSpaceDifference>();
196      foreach (var ep in packingBin.ExtremePoints.Where(x => x.Key.Y == 0)) {
197        var position = ep.Key;
198        foreach (var rs in ep.Value) {
199          var rsd = ResidualSpaceDifference.Create(position, newItem, rs);
200          if (rsd != null) {
201            rsds.Add(rsd);
202          }
203        }
204      }
205      var d = rsds.FirstOrDefault(x => packingBin.IsPositionFeasible(x.Item, x.Position, useStackingConstraints));
206
207      if (d == null) {
208        return null;
209      }
210     
211      packingItem.Rotated = orientation.Item2;
212      packingItem.Tilted = orientation.Item3;
213      return d.Position;
214    }
215
216    Tuple<double, bool, bool> CalculateArea(PackingItem packingItem, bool rotated, bool tilted) {
217      var item = new PackingItem(packingItem) {
218        Rotated = rotated,
219        Tilted = tilted
220      };
221      return Tuple.Create<double, bool, bool>(item.Width * item.Depth, rotated, tilted);
222    }
223
224    /// <summary>
225    /// Tries to find a valid position for a non stackable item.
226    /// Positions will only be valid if the height difference of its residual space is smaller then the hight of the item.
227    /// </summary>
228    /// <param name="packingBin"></param>
229    /// <param name="packingItem"></param>
230    /// <param name="useStackingConstraints"></param>
231    /// <returns></returns>
232    private PackingPosition FindPackingPositionForWeightUnsupportedItem(BinPacking3D packingBin, PackingItem packingItem, bool useStackingConstraints) {
233      if (!CheckItemDimensions(packingBin, packingItem)) {
234        throw new BinPacking3DException($"The dimensions of the given item exceeds the bin dimensions. " +
235          $"Bin: ({packingBin.BinShape.Width} {packingBin.BinShape.Depth} {packingBin.BinShape.Height})" +
236          $"Item: ({packingItem.Width} {packingItem.Depth} {packingItem.Height})");
237      }
238      var rsds = CalculateResidalSpaceDifferences(packingBin, packingItem, useStackingConstraints).ToList();
239      var rsd = rsds.Where(x => x != null && (x.Y / (double)x.Item.Height) < NOT_STACKABLE_RS_LEFT_TO_ITEM_HEIGHT_PROPORTION).OrderByDescending(x => x.Y % x.Item.Height).FirstOrDefault();
240
241      if (rsd == null) {
242        return null;
243      }
244
245      packingItem.Rotated = rsd.Item.Rotated;
246      packingItem.Tilted = rsd.Item.Tilted;
247
248      return rsd.Position;
249    }
250
251    protected override PackingPosition FindPackingPositionForItem(BinPacking3D packingBin, PackingItem packingItem, bool useStackingConstraints) {
252      if (!CheckItemDimensions(packingBin, packingItem)) {
253        throw new BinPacking3DException($"The dimensions of the given item exceeds the bin dimensions. " +
254          $"Bin: ({packingBin.BinShape.Width} {packingBin.BinShape.Depth} {packingBin.BinShape.Height})" +
255          $"Item: ({packingItem.Width} {packingItem.Depth} {packingItem.Height})");
256      }
257
258      var rsds = CalculateResidalSpaceDifferences(packingBin, packingItem, useStackingConstraints).Where(x => x != null);
259      var rsd = rsds.FirstOrDefault();
260
261
262      if (rsd == null) {
263        return null;
264      }
265
266      packingItem.Rotated = rsd.Item.Rotated;
267      packingItem.Tilted = rsd.Item.Tilted;
268
269      return rsd.Position;
270    }
271
272    /// <summary>
273    ///
274    /// </summary>
275    /// <param name="packingBin"></param>
276    /// <param name="packingItem"></param>
277    /// <param name="useStackingConstraints"></param>
278    /// <returns></returns>
279    private SortedSet<ResidualSpaceDifference> CalculateResidalSpaceDifferences(BinPacking3D packingBin, PackingItem packingItem, bool useStackingConstraints) {
280      var rsds = new SortedSet<ResidualSpaceDifference>();
281
282      rsds.Add(FindResidualSpaceDifferenceForItem(packingBin, packingItem, useStackingConstraints, rotated: false, tilted: false));
283
284      if (packingItem.TiltEnabled) {
285        rsds.Add(FindResidualSpaceDifferenceForItem(packingBin, packingItem, useStackingConstraints, rotated: false, tilted: true));
286      }
287      if (packingItem.RotateEnabled) {
288        rsds.Add(FindResidualSpaceDifferenceForItem(packingBin, packingItem, useStackingConstraints, rotated: true, tilted: false));
289      }
290      if (packingItem.RotateEnabled && packingItem.TiltEnabled) {
291        rsds.Add(FindResidualSpaceDifferenceForItem(packingBin, packingItem, useStackingConstraints, rotated: true, tilted: true));
292      }
293      return rsds;
294    }
295
296    /// <summary>
297    ///
298    /// </summary>
299    /// <param name="packingBin"></param>
300    /// <param name="packingItem"></param>
301    /// <param name="useStackingConstraints"></param>
302    /// <param name="rotated"></param>
303    /// <param name="tilted"></param>
304    /// <returns></returns>
305    protected ResidualSpaceDifference FindResidualSpaceDifferenceForItem(BinPacking3D packingBin, PackingItem packingItem, bool useStackingConstraints, bool rotated, bool tilted) {
306      PackingItem newItem = new PackingItem(packingItem) {
307        Rotated = rotated,
308        Tilted = tilted
309      };
310
311      var rsds = new SortedSet<ResidualSpaceDifference>();
312      foreach (var ep in packingBin.ExtremePoints) {
313        var position = ep.Key;
314        foreach (var rs in ep.Value) {
315          var rsd = ResidualSpaceDifference.Create(position, newItem, rs);
316          if (rsd != null) {
317            rsds.Add(rsd);
318          }
319        }
320      }
321
322      // If the packer uses line projection it migth happen, that an extreme point right of the disired on will be chosen.
323      // To avoid this, if there is an extreme point on the rigth side available this point will be returned.
324      var possible = rsds.Where(rsd => packingBin.IsPositionFeasible(rsd.Item, rsd.Position, useStackingConstraints));
325      var prevered = possible.FirstOrDefault();
326
327      return possible.Where(x => x.Position.Z == prevered.Position.Z && x.Position.Y == prevered.Position.Y).OrderBy(x => x.Position.X).FirstOrDefault();
328    }
329
330    protected override bool CheckItemDimensions(BinPacking3D packingBin, PackingItem item) {
331      bool ok = false;
332      int width = item.OriginalWidth;
333      int height = item.OriginalHeight;
334      int depth = item.OriginalDepth;
335
336      ok |= CheckItemDimensions(packingBin, width, height, depth);
337
338      if (item.RotateEnabled && item.TiltEnabled) {
339        ok |= CheckItemDimensions(packingBin, depth, height, width);//rotated
340        ok |= CheckItemDimensions(packingBin, height, width, depth);//tilted
341        ok |= CheckItemDimensions(packingBin, depth, width, height);//rotated & tilted
342      } else if (item.RotateEnabled) {
343        ok |= CheckItemDimensions(packingBin, depth, height, width);
344      } else if (item.TiltEnabled) {
345        ok |= CheckItemDimensions(packingBin, height, width, depth);
346      }
347
348      return ok;
349    }
350
351    private bool CheckItemDimensions(BinPacking3D packingBin, int width, int height, int depth) {
352      return base.CheckItemDimensions(packingBin, new PackingItem() {
353        OriginalWidth = width,
354        OriginalHeight = height,
355        OriginalDepth = depth
356      });
357    }
358
359
360    protected class ResidualSpaceDifference : IComparable {
361      public static ResidualSpaceDifference Create(PackingPosition position, PackingItem item, ResidualSpace rs) {
362        var x = rs.Width - item.Width;
363        var y = rs.Height - item.Height;
364        var z = rs.Depth - item.Depth;
365        // the item can't be places in the residual space
366        if (rs.IsZero() || x < 0 || y < 0 || z < 0) {
367          return null;
368        }
369
370        return new ResidualSpaceDifference() {
371          Position = position,
372          Item = item,
373          X = x,
374          Y = y,
375          Z = z
376        };
377      }
378
379      public ResidualSpaceDifference() { }
380
381      public PackingItem Item { get; set; }
382
383      public PackingPosition Position { get; set; }
384      public int X { get; set; }
385      public int Y { get; set; }
386      public int Z { get; set; }
387
388
389      public int CompareTo(object obj) {
390        if (!(obj is ResidualSpaceDifference)) {
391          return 0;
392        }
393        var rsd = obj as ResidualSpaceDifference;
394
395        var x = this.X - rsd.X;
396        var y = this.Y - rsd.Y;
397        var z = this.Z - rsd.Z;
398
399        if (x != 0) {
400          return x;
401        } else if (y != 0) {
402          return y;
403        } else if (z != 0) {
404          return z;
405        }
406
407        return 0;
408      }
409
410      public override string ToString() {
411        return string.Format("({1},{2},{3})", X, Y, Z);
412      }
413    }
414
415  }
416}
Note: See TracBrowser for help on using the repository browser.