Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2817-BinPackingSpeedup/HeuristicLab.Problems.BinPacking/3.3/3D/BinPacking3D.cs @ 15471

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

#2817

  • Added some unit tests
  • Enhanced the documentation
File size: 32.6 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2016 Joseph Helm and 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 HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
26using HeuristicLab.Core;
27using HeuristicLab.Common;
28using HeuristicLab.Problems.BinPacking;
29using HeuristicLab.Problems.BinPacking3D.Geometry;
30
31namespace HeuristicLab.Problems.BinPacking3D {
32  [Item("BinPacking3D", "Represents a single-bin packing for a 3D bin-packing problem.")]
33  [StorableClass]
34  public class BinPacking3D : BinPacking<PackingPosition, PackingShape, PackingItem> {
35
36    [Storable]
37    public Dictionary<PackingPosition, Tuple<int, int, int>> ResidualSpace { get; protected set; }
38
39    public BinPacking3D(PackingShape binShape)
40      : base(binShape) {
41      ResidualSpace = new Dictionary<PackingPosition, Tuple<int, int, int>>();
42      AddExtremePoint(binShape.Origin);
43      InitializeOccupationLayers();
44    }
45    [StorableConstructor]
46    protected BinPacking3D(bool deserializing) : base(deserializing) { }
47    protected BinPacking3D(BinPacking3D original, Cloner cloner)
48      : base(original, cloner) {
49      this.ResidualSpace = new Dictionary<PackingPosition, Tuple<int, int, int>>();
50      foreach (var o in original.ResidualSpace)
51        this.ResidualSpace.Add(cloner.Clone(o.Key), Tuple.Create(o.Value.Item1, o.Value.Item2, o.Value.Item3));
52    }
53    public override IDeepCloneable Clone(Cloner cloner) {
54      return new BinPacking3D(this, cloner);
55    }
56
57
58    [StorableHook(HookType.AfterDeserialization)]
59    private void AfterDeserialization() {
60      // BackwardsCompatibility3.3
61      #region Backwards compatible code, remove with 3.4
62      if (ResidualSpace == null)
63        ResidualSpace = new Dictionary<PackingPosition, Tuple<int, int, int>>();
64      #endregion
65    }
66
67    #region New methods for bin packer class
68
69    /// <summary>
70    /// Puts a given item into the bin packing at the given position.
71    /// </summary>
72    /// <param name="itemID">Offset in the internal item array</param>
73    /// <param name="item">Item</param>
74    /// <param name="position">Position of the item in the bin packing</param>
75    public override void PackItem(int itemID, PackingItem item, PackingPosition position) {
76      Items[itemID] = item;
77      Positions[itemID] = position;
78      ExtremePoints.Remove(position);
79      ResidualSpace.Remove(position);
80    }
81
82    /// <summary>
83    /// Updates the extreme points of the bin
84    /// </summary>
85    /// <param name="item"></param>
86    /// <param name="position"></param>
87    /// <param name="stackingConstraints"></param>
88    public void UpdateExtremePoints(PackingItem item, PackingPosition position, bool stackingConstraints) {
89      if (stackingConstraints) {
90        UpdateExtremePointsWithStackingConstriants(item, position);
91      } else {
92        UpdateExtremePointsWithoutStackingConstriants(item, position);
93      }
94    }
95
96    /// <summary>
97    /// Updates the extreme points of the bin.
98    /// As there is no need to project the extreme points to the next side of an item, the extreme points are only generated for the current item.
99    /// </summary>
100    /// <param name="item"></param>
101    /// <param name="position"></param>
102    private void UpdateExtremePointsWithStackingConstriants(PackingItem newItem, PackingPosition position) {
103
104      //UpdateExtremePointsWithoutStackingConstriants(newItem, position);
105      //return;
106
107      int newWidth = position.Rotated ? newItem.Depth : newItem.Width;
108      int newDepth = position.Rotated ? newItem.Width : newItem.Depth;
109      var ep1 = new PackingPosition(position.AssignedBin, position.X + newWidth, position.Y, position.Z);
110      var ep2 = new PackingPosition(position.AssignedBin, position.X, position.Y + newItem.Height, position.Z);
111      var ep3 = new PackingPosition(position.AssignedBin, position.X, position.Y, position.Z + newDepth);
112      AddExtremePoint(ep1);
113      AddExtremePoint(ep2);
114      AddExtremePoint(ep3);
115    }
116       
117    private Tuple<int, int, int> CalculateResidualSpace(Vector3D pos) {
118      var itemPos = Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] });
119      Vector3D limit = new Vector3D() { X = BinShape.Width, Y = BinShape.Height, Z = BinShape.Depth };
120      if (pos.X < limit.X && pos.Y < limit.Y && pos.Z < limit.Z) {
121        var rightLimit = ProjectRight(pos);
122        var upLimit = ProjectUp(pos);
123        var forwardLimit = ProjectForward(pos);
124        if (rightLimit.X > 0) {
125          limit.X = rightLimit.X;
126        }
127        if (upLimit.Y > 0) {
128          limit.Y = upLimit.Y;
129        }
130        if (forwardLimit.Z > 0) {
131          limit.Z = forwardLimit.Z;
132        }
133      }
134     
135      if (limit.X - pos.X <= 0 || limit.Y - pos.Y <= 0  || limit.Z - pos.Z <= 0) {
136        return Tuple.Create(0, 0, 0);
137      }     
138      return Tuple.Create(limit.X - pos.X, limit.Y - pos.Y, limit.Z - pos.Z);
139    }
140
141
142    private Vector3D CreateRs(PackingPosition position) {
143      return new Vector3D() { X = position.X, Y = position.Y, Z = position.Z };
144    }
145
146    private void UpdateExtremePointsWithoutStackingConstriants(PackingItem item, PackingPosition position) {
147      GenerateNewExtremePointsForNewItem(item, position);
148
149      // if an item is fit in below, before, or left of another its extreme points have to be regenerated
150      foreach (var above in GetItemsAbove(position)) {
151        GenerateNewExtremePointsForNewItem(above.Item2, above.Item1);
152      }
153      foreach (var front in GetItemsInfront(position)) {
154        GenerateNewExtremePointsForNewItem(front.Item2, front.Item1);
155      }
156      foreach (var right in GetItemsOnRight(position)) {
157        GenerateNewExtremePointsForNewItem(right.Item2, right.Item1);
158      }
159    }
160
161
162
163    /// <summary>
164    /// In this case feasability is defined as following:
165    /// 1. the item fits into the bin-borders;
166    /// 2. the point is supported by something;
167    /// 3. the item does not collide with another already packed item
168    ///
169    /// Unfortunatelly this method does not support item rotation
170    /// </summary>
171    /// <param name="item"></param>
172    /// <param name="position"></param>
173    /// <param name="stackingConstraints"></param>
174    /// <returns></returns>
175    public override bool IsPositionFeasible(PackingItem item, PackingPosition position, bool stackingConstraints) {
176      var b1 = CheckItemDimensions(item);
177      var b2 = ItemCanBePlaced(item, position);
178      var b3 = CheckStackingConstraints(item, position, stackingConstraints);
179
180      return b1 && b2 && b3;
181    }
182
183    /// <summary>
184    /// Compares the dimensions of a given item and the current bin.
185    /// </summary>
186    /// <param name="item"></param>
187    /// <returns>Returns true if the dimensions of an given item are less or equal to the bin.</returns>
188    private bool CheckItemDimensions(PackingItem item) {
189      return BinShape.Width >= item.Width && BinShape.Height >= item.Height && BinShape.Depth >= item.Depth;
190    }
191
192    /// <summary>
193    /// Returns true if a given item can be placed in the current bin
194    /// </summary>
195    /// <param name="givenItem"></param>
196    /// <param name="givenItemPosition"></param>
197    /// <returns></returns>
198    private bool ItemCanBePlaced(PackingItem givenItem, PackingPosition givenItemPosition) {
199      // Check if the boundings of the bin would injured
200      if (givenItemPosition.X + givenItem.Width > BinShape.Width ||
201          givenItemPosition.Y + givenItem.Height > BinShape.Height ||
202          givenItemPosition.Z + givenItem.Depth > BinShape.Depth) {
203        return false;
204      }
205
206      //if the given item collides with any other item, it can not be placed
207      foreach (var item in Items) {
208        if (ItemsCollides(new Tuple<PackingPosition, PackingItem>(Positions[item.Key], item.Value),
209                          new Tuple<PackingPosition, PackingItem>(givenItemPosition, givenItem))) {
210          return false;
211        }
212      }
213      return true;
214    }
215
216    /// <summary>
217    /// Checks if two given items in a space collides
218    /// </summary>
219    /// <param name="t1"></param>
220    /// <param name="t2"></param>
221    /// <returns></returns>
222    private bool ItemsCollides(Tuple<PackingPosition, PackingItem> t1, Tuple<PackingPosition, PackingItem> t2) {
223      var position1 = t1.Item1;
224      var item1 = t1.Item2;
225      var position2 = t2.Item1;
226      var item2 = t2.Item2;
227      var cx = (position2.X == position1.X) || (position2.X < position1.X && position2.X + item2.Width > position1.X) || (position2.X > position1.X && position1.X + item1.Width > position2.X);
228      var cy = (position2.Y == position1.Y) || (position2.Y < position1.Y && position2.Y + item2.Height > position1.Y) || (position2.Y > position1.Y && position1.Y + item1.Height > position2.Y);
229      var cz = (position2.Z == position1.Z) || (position2.Z < position1.Z && position2.Z + item2.Depth > position1.Z) || (position2.Z > position1.Z && position1.Z + item1.Depth > position2.Z);
230      return cx && cy && cz;
231    }
232
233    /// <summary>
234    /// Checks the stacking constraints. This method depends that the given item can be placed at this position
235    /// </summary>
236    /// <param name="item"></param>
237    /// <param name="position"></param>
238    /// <param name="stackingConstraints"></param>
239    /// <returns></returns>
240    private bool CheckStackingConstraints(PackingItem item, PackingPosition position, bool stackingConstraints) {
241      if (position.Y == 0 || !stackingConstraints && HasOnePointWithAnItemBelow(item, position)) {
242        return true;
243      }
244
245      return IsStaticStable(item, position) && IsWeightSupported(item, position);
246    }
247
248
249    /// <summary>
250    /// Checks if a given item has any point lieing on another item.
251    /// </summary>
252    /// <param name="item"></param>
253    /// <param name="position"></param>
254    /// <returns></returns>
255    private bool HasOnePointWithAnItemBelow(PackingItem item, PackingPosition position) {
256      bool p1, p2, p3, p4;
257      PointsLiesOnAnItem(item, position, out p1, out p2, out p3, out p4);
258
259      return p1 || p2 || p3 || p4;
260    }
261
262    /// <summary>
263    /// Checks if a given item is static stable.
264    /// A item is static stable if all edges have an object below.
265    /// </summary>
266    /// <param name="item"></param>
267    /// <param name="position"></param>
268    /// <returns></returns>
269    public override bool IsStaticStable(PackingItem item, PackingPosition position) {
270      bool p1, p2, p3, p4;
271      PointsLiesOnAnItem(item, position, out p1, out p2, out p3, out p4);
272      return p1 && p2 && p3 && p4;
273    }
274
275    /// <summary>
276    /// This method sets the out parameters p1 ... p4 if the point lies on another item.
277    /// p1 ... p3 represents one point on the bottom side of an given item.
278    /// +---------+
279    /// |p1     p2|
280    /// |         |
281    /// |p4     p3|
282    /// +---------+
283    /// </summary>
284    /// <param name="item">Given item</param>
285    /// <param name="position">Given item position</param>
286    /// <param name="p1"></param>
287    /// <param name="p2"></param>
288    /// <param name="p3"></param>
289    /// <param name="p4"></param>
290    private void PointsLiesOnAnItem(PackingItem item, PackingPosition position, out bool p1, out bool p2, out bool p3, out bool p4) {
291      IEnumerable<Tuple<PackingPosition, PackingItem>> itemsP1;
292      IEnumerable<Tuple<PackingPosition, PackingItem>> itemsP2;
293      IEnumerable<Tuple<PackingPosition, PackingItem>> itemsP3;
294      IEnumerable<Tuple<PackingPosition, PackingItem>> itemsP4;
295
296      GetItemsUnderItemWithContact(item, position, out itemsP1, out itemsP2, out itemsP3, out itemsP4);
297
298      p1 = itemsP1.Where(x => position.X < x.Item1.X + x.Item2.Width && position.Z < x.Item1.Z + x.Item2.Depth).Any();
299      p2 = itemsP2.Where(x => position.X + item.Width > x.Item1.X && position.Z < x.Item1.Z + x.Item2.Depth).Any();
300      p3 = itemsP3.Where(x => position.X + item.Width > x.Item1.X && position.Z + item.Depth > x.Item1.Z).Any();
301      p4 = itemsP4.Where(x => position.X < x.Item1.X + x.Item2.Width && position.Z + item.Depth > x.Item1.Z).Any();
302    }
303
304
305    /// <summary>
306    /// This method returns a collection for the out parameters itemsP1 ... itemsP4 with the items below
307    /// itemsP1 ... itemsP4 represents one point on the bottom side of an given item.
308    /// +---------+
309    /// |p1     p2|
310    /// |         |
311    /// |p4     p3|
312    /// +---------+
313    /// </summary>
314    /// <param name="item"></param>
315    /// <param name="position"></param>
316    /// <param name="itemsP1"></param>
317    /// <param name="itemsP2"></param>
318    /// <param name="itemsP3"></param>
319    /// <param name="itemsP4"></param>
320    private void GetItemsUnderItemWithContact(PackingItem item, PackingPosition position,
321                                   out IEnumerable<Tuple<PackingPosition, PackingItem>> itemsP1,
322                                   out IEnumerable<Tuple<PackingPosition, PackingItem>> itemsP2,
323                                   out IEnumerable<Tuple<PackingPosition, PackingItem>> itemsP3,
324                                   out IEnumerable<Tuple<PackingPosition, PackingItem>> itemsP4) {
325      itemsP1 = GetItemsBelowForPosition(new PackingPosition(0, position.X, position.Y, position.Z, false));
326      itemsP2 = GetItemsBelowForPosition(new PackingPosition(0, position.X + item.Width - 1, position.Y, position.Z, false));
327      itemsP3 = GetItemsBelowForPosition(new PackingPosition(0, position.X, position.Y, position.Z + item.Depth - 1, false));
328      itemsP4 = GetItemsBelowForPosition(new PackingPosition(0, position.X + item.Width - 1, position.Y, position.Z + item.Depth - 1, false));
329
330    }
331
332    /// <summary>
333    /// Returns a collection of items which are below a given point.
334    /// The top side of every item is at the same level as the Y-coordinate of the given position.
335    /// </summary>
336    /// <param name="position"></param>
337    /// <returns></returns>
338    private IEnumerable<Tuple<PackingPosition, PackingItem>> GetItemsBelowForPosition(PackingPosition position) {
339      return GetItemsBelow(position).Where(x => (x.Item1.Y + x.Item2.Height) == position.Y);
340    }
341
342
343    /// <summary>
344    /// Checks if a given the weight of an given item is supportet by the items below.
345    /// </summary>
346    /// <param name="item"></param>
347    /// <param name="position"></param>
348    /// <returns></returns>
349    private bool IsWeightSupported(PackingItem item, PackingPosition position) {
350      if (position.Y == 0) {
351        return true;
352      }
353      IEnumerable<Tuple<PackingPosition, PackingItem>> itemsP1;
354      IEnumerable<Tuple<PackingPosition, PackingItem>> itemsP2;
355      IEnumerable<Tuple<PackingPosition, PackingItem>> itemsP3;
356      IEnumerable<Tuple<PackingPosition, PackingItem>> itemsP4;
357
358      GetItemsUnderItemWithContact(item, position, out itemsP1, out itemsP2, out itemsP3, out itemsP4);
359
360      return itemsP1.Where(x => x.Item2.SupportsStacking(item)).Any() &&
361        itemsP2.Where(x => x.Item2.SupportsStacking(item)).Any() &&
362        itemsP3.Where(x => x.Item2.SupportsStacking(item)).Any() &&
363        itemsP4.Where(x => x.Item2.SupportsStacking(item)).Any();
364    }
365
366
367
368    #endregion
369
370    /// <summary>
371    /// Generates new extreme points for a given item and its position.
372    /// It also recalcualtes the residual space and removes the extreme points which are not needed any more.
373    /// </summary>
374    /// <param name="newItem"></param>
375    /// <param name="position"></param>
376    protected override void GenerateNewExtremePointsForNewItem(PackingItem newItem, PackingPosition position) {
377      int newWidth = position.Rotated ? newItem.Depth : newItem.Width;
378      int newDepth = position.Rotated ? newItem.Width : newItem.Depth;
379
380      var itemPlacement = Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] }).ToList();
381      // Find ExtremePoints beginning from sourcepointX
382      var sourcePoint = new Vector3D() { X = position.X + newWidth, Y = position.Y, Z = position.Z };
383      if (sourcePoint.X < BinShape.Width && sourcePoint.Y < BinShape.Height && sourcePoint.Z < BinShape.Depth) {
384        // Projecting onto the XZ-plane
385        var below = ProjectDown(sourcePoint);
386        var residualSpace = CalculateResidualSpace(below);
387        if (IsNonZero(residualSpace)
388          && !IsWithinResidualSpaceOfAnotherExtremePoint(below, residualSpace)) {
389          // add only if the projected point's residual space is not a sub-space
390          // of another extreme point's residual space
391          var belowPoint = new PackingPosition(position.AssignedBin, below.X, below.Y, below.Z);
392          AddExtremePoint(belowPoint);
393        }
394        // Projecting onto the XY-plane
395        var back = ProjectBackward(sourcePoint);
396        residualSpace = CalculateResidualSpace(back);
397        if (IsNonZero(residualSpace)
398          && !IsWithinResidualSpaceOfAnotherExtremePoint(back, residualSpace)) {
399          // add only if the projected point's residual space is not a sub-space
400          // of another extreme point's residual space
401          var backPoint = new PackingPosition(position.AssignedBin, back.X, back.Y, back.Z);
402          AddExtremePoint(backPoint);
403        }
404      }
405
406      //Find ExtremePoints beginning from sourcepointY
407      sourcePoint = new Vector3D(position.X, position.Y + newItem.Height, position.Z);
408      if (sourcePoint.X < BinShape.Width && sourcePoint.Y < BinShape.Height && sourcePoint.Z < BinShape.Depth) {
409        // Projecting onto the YZ-plane
410        var left = ProjectLeft(sourcePoint);
411        var residualSpace = CalculateResidualSpace(left);
412        if (IsNonZero(residualSpace)
413          && !IsWithinResidualSpaceOfAnotherExtremePoint(left, residualSpace)) {
414          // add only if the projected point's residual space is not a sub-space
415          // of another extreme point's residual space
416          var leftPoint = new PackingPosition(position.AssignedBin, left.X, left.Y, left.Z);
417          AddExtremePoint(leftPoint);
418        }
419        // Projecting onto the XY-plane
420        var back = ProjectBackward(sourcePoint);
421        residualSpace = CalculateResidualSpace(back);
422        if (IsNonZero(residualSpace)
423          && !IsWithinResidualSpaceOfAnotherExtremePoint(back, residualSpace)) {
424          // add only if the projected point's residual space is not a sub-space
425          // of another extreme point's residual space
426          var backPoint = new PackingPosition(position.AssignedBin, back.X, back.Y, back.Z);
427          AddExtremePoint(backPoint);
428        }
429      }
430
431      //Find ExtremePoints beginning from sourcepointZ
432      sourcePoint = new Vector3D(position.X, position.Y, position.Z + newDepth);
433      if (sourcePoint.X < BinShape.Width && sourcePoint.Y < BinShape.Height && sourcePoint.Z < BinShape.Depth) {
434        // Projecting onto the XZ-plane
435        var below = ProjectDown(sourcePoint);
436        var residualSpace = CalculateResidualSpace(below);
437        if (IsNonZero(residualSpace)
438          && !IsWithinResidualSpaceOfAnotherExtremePoint(below, residualSpace)) {
439          // add only if the projected point's residual space is not a sub-space
440          // of another extreme point's residual space
441          var belowPoint = new PackingPosition(position.AssignedBin, below.X, below.Y, below.Z);
442          AddExtremePoint(belowPoint);
443        }
444        // Projecting onto the YZ-plane
445        var left = ProjectLeft(sourcePoint);
446        residualSpace = CalculateResidualSpace(left);
447        if (IsNonZero(residualSpace)
448          && !IsWithinResidualSpaceOfAnotherExtremePoint(left, residualSpace)) {
449          // add only if the projected point's residual space is not a sub-space
450          // of another extreme point's residual space
451          var leftPoint = new PackingPosition(position.AssignedBin, left.X, left.Y, left.Z);
452          AddExtremePoint(leftPoint);
453        }
454      }
455    }
456
457    /// <summary>
458    /// Returns true if all values of a given tuple are not zero
459    /// </summary>
460    /// <param name="rs">Tuple with three integer values which represents a residual space</param>
461    /// <returns></returns>
462    private bool IsNonZero(Tuple<int, int, int> rs) {
463      return rs.Item1 > 0 && rs.Item2 > 0 && rs.Item3 > 0;
464    }
465
466    /// <summary>
467    ///
468    /// </summary>
469    /// <param name="pos"></param>
470    /// <param name="residualSpace"></param>
471    /// <returns></returns>
472    private bool IsWithinResidualSpaceOfAnotherExtremePoint(Vector3D pos, Tuple<int, int, int> residualSpace) {
473      var eps = ExtremePoints.Where(x => !pos.Equals(x) && pos.IsInside(x, ResidualSpace[x]));
474      return eps.Any(x => IsWithinResidualSpaceOfAnotherExtremePoint(pos, residualSpace, x, ResidualSpace[x]));
475    }
476    private bool IsWithinResidualSpaceOfAnotherExtremePoint(Vector3D pos, Tuple<int, int, int> rsPos, PackingPosition ep, Tuple<int, int, int> rsEp) {
477      return rsEp.Item1 >= pos.X - ep.X + rsPos.Item1
478          && rsEp.Item2 >= pos.Y - ep.Y + rsPos.Item2
479          && rsEp.Item3 >= pos.Z - ep.Z + rsPos.Item3;
480    }
481
482    /// <summary>
483    /// Adds an extrem point to the extreme point collection of the bin packing.
484    /// </summary>
485    /// <param name="pos">Position of the new extreme point</param>
486    /// <returns>Retruns true if the extreme point could be added</returns>
487    private bool AddExtremePoint(PackingPosition pos) {
488      if (ExtremePoints.Add(pos)) {
489        var rs = CalculateResidualSpace(new Vector3D(pos));
490        ResidualSpace.Add(pos, rs);
491        // Check if the existing extreme points are shadowed by the new point
492        // This is, their residual space fit entirely into the residual space of the new point
493        foreach (var ep in ExtremePoints.Where(x => x != pos && new Vector3D(x).IsInside(pos, rs)).ToList()) {
494          if (IsWithinResidualSpaceOfAnotherExtremePoint(new Vector3D(ep), ResidualSpace[ep], pos, rs)) {
495            ExtremePoints.Remove(ep);
496            ResidualSpace.Remove(ep);
497          }
498        }
499        return true;
500      }
501      return false;
502    }
503       
504    #region Projections
505
506    private Vector3D ProjectBackward(Vector3D pos) {
507      var line = new Line3D(pos, new Vector3D(0, 0, -1));
508      return Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] })
509                  .Select(x => new Plane3D(x.Position, x.Item, Side.Front))
510                  .Concat(new[] { new Plane3D(BinShape, Side.Back) })
511                  .Select(x => x.Intersect(line))
512                  .Where(x => x != null && x.Z <= pos.Z)
513                  .MaxItems(x => x.Z).First();
514    }
515
516    private Vector3D ProjectLeft(Vector3D pos) {
517      var line = new Line3D(pos, new Vector3D(-1, 0, 0));
518      return Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] })
519                  .Select(x => new Plane3D(x.Position, x.Item, Side.Right))
520                  .Concat(new[] { new Plane3D(BinShape, Side.Left) })
521                  .Select(x => x.Intersect(line))
522                  .Where(x => x != null && x.X <= pos.X)
523                  .MaxItems(x => x.X).First();
524    }
525
526    private Vector3D ProjectDown(Vector3D pos) {
527      var line = new Line3D(pos, new Vector3D(0, -1, 0));
528      return Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] })
529                  .Select(x => new Plane3D(x.Position, x.Item, Side.Top))
530                  .Concat(new[] { new Plane3D(BinShape, Side.Bottom) })
531                  .Select(x => x.Intersect(line))
532                  .Where(x => x != null && x.Y <= pos.Y)
533                  .MaxItems(x => x.Y).First();
534    }
535
536    private Vector3D ProjectForward(Vector3D pos) {
537      var line = new Line3D(pos, new Vector3D(0, 0, 1));
538      return Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] })
539                  .Select(x => new Plane3D(x.Position, x.Item, Side.Back))
540                  .Concat(new[] { new Plane3D(BinShape, Side.Front) })
541                  .Select(x => x.Intersect(line))
542                  .Where(x => x != null && x.Z >= pos.Z)
543                  .MinItems(x => x.Z).First();
544    }
545
546    private Vector3D ProjectRight(Vector3D pos) {
547      var line = new Line3D(pos, new Vector3D(1, 0, 0));
548      return Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] })
549                  .Select(x => new Plane3D(x.Position, x.Item, Side.Left))
550                  .Concat(new[] { new Plane3D(BinShape, Side.Right) })
551                  .Select(x => x.Intersect(line))
552                  .Where(x => x != null && x.X >= pos.X)
553                  .MinItems(x => x.X).First();
554    }
555
556    private Vector3D ProjectUp(Vector3D pos) {
557      var line = new Line3D(pos, new Vector3D(0, 1, 0));
558      return Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] })
559                  .Select(x => new Plane3D(x.Position, x.Item, Side.Bottom))
560                  .Concat(new[] { new Plane3D(BinShape, Side.Top) })
561                  .Select(x => x.Intersect(line))
562                  .Where(x => x != null && x.Y >= pos.Y)
563                  .MinItems(x => x.Y).First();
564    }
565    #endregion
566
567    #region Get items
568
569    private IEnumerable<Tuple<PackingPosition, PackingItem>> GetItemsAbove(PackingPosition pos) {
570      var line = new Line3D(pos, new Vector3D(0, 1, 0));
571      return Items.Select(x => new {
572        Item = x.Value,
573        Position = Positions[x.Key],
574        Intersection = line.Intersect(new Plane3D(Positions[x.Key], x.Value, Side.Bottom))
575      }).Where(x => x.Intersection != null && x.Intersection.Y >= pos.Y)
576        .Select(x => Tuple.Create(x.Position, x.Item));
577    }
578
579    private IEnumerable<Tuple<PackingPosition, PackingItem>> GetItemsInfront(PackingPosition pos) {
580      var line = new Line3D(pos, new Vector3D(0, 0, 1));
581      return Items.Select(x => new {
582        Item = x.Value,
583        Position = Positions[x.Key],
584        Intersection = line.Intersect(new Plane3D(Positions[x.Key], x.Value, Side.Back))
585      }).Where(x => x.Intersection != null && x.Intersection.Y >= pos.Y)
586        .Select(x => Tuple.Create(x.Position, x.Item));
587    }
588
589    private IEnumerable<Tuple<PackingPosition, PackingItem>> GetItemsOnRight(PackingPosition pos) {
590      var line = new Line3D(pos, new Vector3D(1, 0, 0));
591      return Items.Select(x => new {
592        Item = x.Value,
593        Position = Positions[x.Key],
594        Intersection = line.Intersect(new Plane3D(Positions[x.Key], x.Value, Side.Left))
595      }).Where(x => x.Intersection != null && x.Intersection.Y >= pos.Y)
596        .Select(x => Tuple.Create(x.Position, x.Item));
597    }
598
599    private IEnumerable<Tuple<PackingPosition, PackingItem>> GetItemsBelow(PackingPosition pos) {
600      var line = new Line3D(pos, new Vector3D(0, 1, 0));
601      return Items.Select(x => new {
602        Item = x.Value,
603        Position = Positions[x.Key],
604        Intersection = line.Intersect(new Plane3D(Positions[x.Key], x.Value, Side.Top))
605      }).Where(x => x.Intersection != null && x.Intersection.Y <= pos.Y)
606        .Select(x => Tuple.Create(x.Position, x.Item));
607    }
608
609    private IEnumerable<Tuple<PackingPosition, PackingItem>> GetItemsBehind(PackingPosition pos) {
610      var line = new Line3D(pos, new Vector3D(0, 0, 1));
611      return Items.Select(x => new {
612        Item = x.Value,
613        Position = Positions[x.Key],
614        Intersection = line.Intersect(new Plane3D(Positions[x.Key], x.Value, Side.Front))
615      }).Where(x => x.Intersection != null && x.Intersection.Y >= pos.Y)
616        .Select(x => Tuple.Create(x.Position, x.Item));
617    }
618
619    private IEnumerable<Tuple<PackingPosition, PackingItem>> GetItemsOnLeft(PackingPosition pos) {
620      var line = new Line3D(pos, new Vector3D(1, 0, 0));
621      return Items.Select(x => new {
622        Item = x.Value,
623        Position = Positions[x.Key],
624        Intersection = line.Intersect(new Plane3D(Positions[x.Key], x.Value, Side.Right))
625      }).Where(x => x.Intersection != null && x.Intersection.Y >= pos.Y)
626        .Select(x => Tuple.Create(x.Position, x.Item));
627    }
628
629    #endregion
630
631
632    #region Sliding based packing  and obsolet methods
633    public override PackingPosition FindExtremePointForItem(PackingItem item, bool rotated, bool stackingConstraints) {
634      throw new NotSupportedException();
635      PackingItem newItem = new PackingItem(
636        rotated ? item.Depth : item.Width,
637        item.Height,
638        rotated ? item.Width : item.Depth,
639        item.TargetBin, item.Weight, item.Material);
640
641      var ep = ExtremePoints.Where(x => IsPositionFeasible(newItem, x, stackingConstraints))
642                            .FirstOrDefault();
643      if (ep != null) {
644        var result = new PackingPosition(ep.AssignedBin, ep.X, ep.Y, ep.Z, rotated);
645        return result;
646      }
647      return null;
648    }
649
650
651
652
653    public override PackingPosition FindPositionBySliding(PackingItem item, bool rotated, bool stackingConstraints) {
654      throw new NotSupportedException();
655    }
656
657    public override void SlidingBasedPacking(ref IList<int> sequence, IList<PackingItem> items, bool stackingConstraints) {
658      throw new NotSupportedException();
659    }
660    public override void SlidingBasedPacking(ref IList<int> sequence, IList<PackingItem> items, Dictionary<int, bool> rotationArray, bool stackingConstraints) {
661      throw new NotSupportedException();
662    }
663
664
665    public override void ExtremePointBasedPacking(ref IList<int> sequence, IList<PackingItem> items, bool stackingConstraints) {
666      throw new NotSupportedException();
667    }
668
669    public override void ExtremePointBasedPacking(ref IList<int> sequence, IList<PackingItem> items, bool stackingConstraints, Dictionary<int, bool> rotationArray) {
670      throw new NotSupportedException();
671    }
672
673    public override int ShortestPossibleSideFromPoint(PackingPosition position) {
674      throw new NotSupportedException();
675    }
676
677
678    protected override void InitializeOccupationLayers() {
679    }
680    protected override void AddNewItemToOccupationLayers(int itemID, PackingItem item, PackingPosition position) {
681    }
682
683
684    protected override List<int> GetLayerItemIDs(PackingPosition position) {
685      return null;
686    }
687    protected override List<int> GetLayerItemIDs(PackingItem item, PackingPosition position) {
688      return null;
689    }
690    #endregion
691
692
693    /// <summary>
694    /// Updates the resiual space for a packing item.
695    /// </summary>
696    /// <param name="item"></param>
697    /// <param name="pos"></param>
698    public void UpdateResidualSpace(PackingItem item, PackingPosition pos) {
699      foreach (var ep in ExtremePoints.ToList()) {
700        var rs = ResidualSpace[ep];
701        var depth = pos.Rotated ? item.Width : item.Depth;
702        var width = pos.Rotated ? item.Depth : item.Width;
703        var changed = false;
704        if (ep.Z >= pos.Z && ep.Z < pos.Z + depth) {
705          if (ep.X <= pos.X && ep.Y >= pos.Y && ep.Y < pos.Y + item.Height) {
706            var diff = pos.X - ep.X;
707            var newRSX = Math.Min(rs.Item1, diff);
708            rs = Tuple.Create(newRSX, rs.Item2, rs.Item3);
709            changed = true;
710          }
711          if (ep.Y <= pos.Y && ep.X >= pos.X && ep.X < pos.X + width) {
712            var diff = pos.Y - ep.Y;
713            var newRSY = Math.Min(rs.Item2, diff);
714            rs = Tuple.Create(rs.Item1, newRSY, rs.Item3);
715            changed = true;
716          }
717        }
718
719        if (ep.Z <= pos.Z &&
720            ep.Y >= pos.Y && ep.Y < pos.Y + item.Height &&
721            ep.X >= pos.X && ep.X < pos.X + width) {
722          var diff = pos.Z - ep.Z;
723          var newRSZ = Math.Min(rs.Item3, diff);
724          rs = Tuple.Create(rs.Item1, rs.Item2, newRSZ);
725          changed = true;
726        }
727
728        if (changed) {
729          if (IsNonZero(rs) && !IsWithinResidualSpaceOfAnotherExtremePoint(new Vector3D(ep), rs)) {
730            ResidualSpace[ep] = rs;
731          } else {
732            ExtremePoints.Remove(ep);
733            ResidualSpace.Remove(ep);
734          }
735        }
736      }
737      return;
738    }
739  }
740}
Note: See TracBrowser for help on using the repository browser.