Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 15424 was 15424, checked in by rhanghof, 7 years ago

#2817:

  • Added some comments and regions to the source code
  • Addes reference instance files created by the test algoritm of S. Martello, D. Pisinger, D. Vigo
File size: 33.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;
29
30namespace HeuristicLab.Problems.BinPacking3D {
31  [Item("BinPacking3D", "Represents a single-bin packing for a 3D bin-packing problem.")]
32  [StorableClass]
33  public class BinPacking3D : BinPacking<PackingPosition, PackingShape, PackingItem> {
34
35    [Storable]
36    public Dictionary<PackingPosition, Tuple<int, int, int>> ResidualSpace { get; protected set; }
37
38    public BinPacking3D(PackingShape binShape)
39      : base(binShape) {
40      ResidualSpace = new Dictionary<PackingPosition, Tuple<int,int,int>>();
41      AddExtremePoint(binShape.Origin);
42      InitializeOccupationLayers();
43    }
44    [StorableConstructor]
45    protected BinPacking3D(bool deserializing) : base(deserializing) { }
46    protected BinPacking3D(BinPacking3D original, Cloner cloner)
47      : base(original, cloner) {
48      this.ResidualSpace = new Dictionary<PackingPosition, Tuple<int, int, int>>();
49      foreach (var o in original.ResidualSpace)
50        this.ResidualSpace.Add(cloner.Clone(o.Key), Tuple.Create(o.Value.Item1, o.Value.Item2, o.Value.Item3));
51    }
52    public override IDeepCloneable Clone(Cloner cloner) {
53      return new BinPacking3D(this, cloner);
54    }
55
56
57    [StorableHook(HookType.AfterDeserialization)]
58    private void AfterDeserialization() {
59      // BackwardsCompatibility3.3
60      #region Backwards compatible code, remove with 3.4
61      if (ResidualSpace == null)
62        ResidualSpace = new Dictionary<PackingPosition, Tuple<int, int, int>>();
63      #endregion
64    }
65
66    public override void PackItem(int itemID, PackingItem item, PackingPosition position) {
67      // base call is deliberately omitted, because UpdateResidualSpace needs to be fitted in before
68      // generating new extreme points
69      Items[itemID] = item;
70      Positions[itemID] = position;
71      ExtremePoints.Remove(position);
72      ResidualSpace.Remove(position);
73      UpdateResidualSpace(item, position);
74      GenerateNewExtremePointsForNewItem(item, position);
75      // if an item is fit in below, before, or left of another its extreme points have to be regenerated
76      foreach (var above in GetItemsAbove(position))
77        GenerateNewExtremePointsForNewItem(above.Item2, above.Item1);
78      foreach (var front in GetItemsInfront(position))
79        GenerateNewExtremePointsForNewItem(front.Item2, front.Item1);
80      foreach (var right in GetItemsOnRight(position))
81        GenerateNewExtremePointsForNewItem(right.Item2, right.Item1);
82      AddNewItemToOccupationLayers(itemID, item, position);
83    }
84
85    protected override void GenerateNewExtremePointsForNewItem(PackingItem newItem, PackingPosition position) {
86      int newWidth = position.Rotated ? newItem.Depth : newItem.Width;
87      int newDepth = position.Rotated ? newItem.Width : newItem.Depth;
88
89      var itemPlacement = Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] }).ToList();
90      // Find ExtremePoints beginning from sourcepointX
91      var sourcePoint = new Vector3D() { X = position.X + newWidth, Y = position.Y, Z = position.Z };
92      if (sourcePoint.X < BinShape.Width && sourcePoint.Y < BinShape.Height && sourcePoint.Z < BinShape.Depth) {
93        // Projecting onto the XZ-plane
94        var below = ProjectDown(sourcePoint);
95        var residualSpace = CalculateResidualSpace(below);
96        if (IsNonZero(residualSpace)
97          && !IsWithinResidualSpaceOfAnotherExtremePoint(below, residualSpace)) {
98          // add only if the projected point's residual space is not a sub-space
99          // of another extreme point's residual space
100          var belowPoint = new PackingPosition(position.AssignedBin, below.X, below.Y, below.Z);
101          AddExtremePoint(belowPoint);
102        }
103        // Projecting onto the XY-plane
104        var back = ProjectBackward(sourcePoint);
105        residualSpace = CalculateResidualSpace(back);
106        if (IsNonZero(residualSpace)
107          && !IsWithinResidualSpaceOfAnotherExtremePoint(back, residualSpace)) {
108          // add only if the projected point's residual space is not a sub-space
109          // of another extreme point's residual space
110          var backPoint = new PackingPosition(position.AssignedBin, back.X, back.Y, back.Z);
111          AddExtremePoint(backPoint);
112        }
113      }
114
115      //Find ExtremePoints beginning from sourcepointY
116      sourcePoint = new Vector3D(position.X, position.Y + newItem.Height, position.Z);
117      if (sourcePoint.X < BinShape.Width && sourcePoint.Y < BinShape.Height && sourcePoint.Z < BinShape.Depth) {
118        // Projecting onto the YZ-plane
119        var left = ProjectLeft(sourcePoint);
120        var residualSpace = CalculateResidualSpace(left);
121        if (IsNonZero(residualSpace)
122          && !IsWithinResidualSpaceOfAnotherExtremePoint(left, residualSpace)) {
123          // add only if the projected point's residual space is not a sub-space
124          // of another extreme point's residual space
125          var leftPoint = new PackingPosition(position.AssignedBin, left.X, left.Y, left.Z);
126          AddExtremePoint(leftPoint);
127        }
128        // Projecting onto the XY-plane
129        var back = ProjectBackward(sourcePoint);
130        residualSpace = CalculateResidualSpace(back);
131        if (IsNonZero(residualSpace)
132          && !IsWithinResidualSpaceOfAnotherExtremePoint(back, residualSpace)) {
133          // add only if the projected point's residual space is not a sub-space
134          // of another extreme point's residual space
135          var backPoint = new PackingPosition(position.AssignedBin, back.X, back.Y, back.Z);
136          AddExtremePoint(backPoint);
137        }
138      }
139
140      //Find ExtremePoints beginning from sourcepointZ
141      sourcePoint = new Vector3D(position.X, position.Y, position.Z + newDepth);
142      if (sourcePoint.X < BinShape.Width && sourcePoint.Y < BinShape.Height && sourcePoint.Z < BinShape.Depth) {
143        // Projecting onto the XZ-plane
144        var below = ProjectDown(sourcePoint);
145        var residualSpace = CalculateResidualSpace(below);
146        if (IsNonZero(residualSpace)
147          && !IsWithinResidualSpaceOfAnotherExtremePoint(below, residualSpace)) {
148          // add only if the projected point's residual space is not a sub-space
149          // of another extreme point's residual space
150          var belowPoint = new PackingPosition(position.AssignedBin, below.X, below.Y, below.Z);
151          AddExtremePoint(belowPoint);
152        }
153        // Projecting onto the YZ-plane
154        var left = ProjectLeft(sourcePoint);
155        residualSpace = CalculateResidualSpace(left);
156        if (IsNonZero(residualSpace)
157          && !IsWithinResidualSpaceOfAnotherExtremePoint(left, residualSpace)) {
158          // add only if the projected point's residual space is not a sub-space
159          // of another extreme point's residual space
160          var leftPoint = new PackingPosition(position.AssignedBin, left.X, left.Y, left.Z);
161          AddExtremePoint(leftPoint);
162        }
163      }
164    }
165
166    private bool IsNonZero(Tuple<int, int, int> rs) {
167      return rs.Item1 > 0 && rs.Item2 > 0 && rs.Item3 > 0;
168    }
169
170    private bool IsWithinResidualSpaceOfAnotherExtremePoint(Vector3D pos, Tuple<int, int, int> residualSpace) {
171      var eps = ExtremePoints.Where(x => !pos.Equals(x) && pos.IsInside(x, ResidualSpace[x]));
172      return eps.Any(x => IsWithinResidualSpaceOfAnotherExtremePoint(pos, residualSpace, x, ResidualSpace[x]));
173    }
174    private bool IsWithinResidualSpaceOfAnotherExtremePoint(Vector3D pos, Tuple<int, int, int> rsPos, PackingPosition ep, Tuple<int, int, int> rsEp) {
175      return rsEp.Item1 >= pos.X - ep.X + rsPos.Item1
176          && rsEp.Item2 >= pos.Y - ep.Y + rsPos.Item2
177          && rsEp.Item3 >= pos.Z - ep.Z + rsPos.Item3;
178    }
179
180    private bool AddExtremePoint(PackingPosition pos) {
181      if (ExtremePoints.Add(pos)) {
182        var rs = CalculateResidualSpace(new Vector3D(pos));
183        ResidualSpace.Add(pos, rs);
184        // Check if existing extreme points are shadowed by the new point
185        // That is, their residual space fit entirely into the residual space of the new point
186        foreach (var ep in ExtremePoints.Where(x => x != pos && new Vector3D(x).IsInside(pos, rs)).ToList()) {
187          if (IsWithinResidualSpaceOfAnotherExtremePoint(new Vector3D(ep), ResidualSpace[ep], pos, rs)) {
188            ExtremePoints.Remove(ep);
189            ResidualSpace.Remove(ep);
190          }
191        }
192        return true;
193      }
194      return false;
195    }
196
197    private Tuple<int, int, int> CalculateResidualSpace(Vector3D pos) {
198      var itemPos = Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] });
199      var rightLimit = ProjectRight(pos);
200      var upLimit = ProjectUp(pos);
201      var forwardLimit = ProjectForward(pos);
202      return Tuple.Create(rightLimit.X - pos.X, upLimit.Y - pos.Y, forwardLimit.Z - pos.Z);
203    }
204
205    #region Projections
206       
207    private Vector3D ProjectBackward(Vector3D pos) {
208      var line = new Line3D(pos, new Vector3D(0, 0, -1));
209      return Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] })
210                  .Select(x => new Plane3D(x.Position, x.Item, Side.Front))
211                  .Concat(new[] { new Plane3D(BinShape, Side.Back) })
212                  .Select(x => x.Intersect(line))
213                  .Where(x => x != null && x.Z <= pos.Z)
214                  .MaxItems(x => x.Z).First();
215    }
216
217    private Vector3D ProjectLeft(Vector3D pos) {
218      var line = new Line3D(pos, new Vector3D(-1, 0, 0));
219      return Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] })
220                  .Select(x => new Plane3D(x.Position, x.Item, Side.Right))
221                  .Concat(new[] { new Plane3D(BinShape, Side.Left) })
222                  .Select(x => x.Intersect(line))
223                  .Where(x => x != null && x.X <= pos.X)
224                  .MaxItems(x => x.X).First();
225    }
226
227    private Vector3D ProjectDown(Vector3D pos) {
228      var line = new Line3D(pos, new Vector3D(0, -1, 0));
229      return Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] })
230                  .Select(x => new Plane3D(x.Position, x.Item, Side.Top))
231                  .Concat(new[] { new Plane3D(BinShape, Side.Bottom) })
232                  .Select(x => x.Intersect(line))
233                  .Where(x => x != null && x.Y <= pos.Y)
234                  .MaxItems(x => x.Y).First();
235    }
236
237    private Vector3D ProjectForward(Vector3D pos) {
238      var line = new Line3D(pos, new Vector3D(0, 0, 1));
239      return Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] })
240                  .Select(x => new Plane3D(x.Position, x.Item, Side.Back))
241                  .Concat(new[] { new Plane3D(BinShape, Side.Front) })
242                  .Select(x => x.Intersect(line))
243                  .Where(x => x != null && x.Z >= pos.Z)
244                  .MinItems(x => x.Z).First();
245    }
246
247    private Vector3D ProjectRight(Vector3D pos) {
248      var line = new Line3D(pos, new Vector3D(1, 0, 0));
249      return Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] })
250                  .Select(x => new Plane3D(x.Position, x.Item, Side.Left))
251                  .Concat(new[] { new Plane3D(BinShape, Side.Right) })
252                  .Select(x => x.Intersect(line))
253                  .Where(x => x != null && x.X >= pos.X)
254                  .MinItems(x => x.X).First();
255    }
256
257    private Vector3D ProjectUp(Vector3D pos) {
258      var line = new Line3D(pos, new Vector3D(0, 1, 0));
259      return Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] })
260                  .Select(x => new Plane3D(x.Position, x.Item, Side.Bottom))
261                  .Concat(new[] { new Plane3D(BinShape, Side.Top) })
262                  .Select(x => x.Intersect(line))
263                  .Where(x => x != null && x.Y >= pos.Y)
264                  .MinItems(x => x.Y).First();
265    }
266    #endregion
267
268    #region Get items
269
270   
271    private IEnumerable<Tuple<PackingPosition, PackingItem>> GetItemsAbove(PackingPosition pos) {
272      var line = new Line3D(pos, new Vector3D(0, 1, 0));
273      return Items.Select(x => new {
274        Item = x.Value,
275        Position = Positions[x.Key],
276        Intersection = line.Intersect(new Plane3D(Positions[x.Key], x.Value, Side.Bottom))
277      }).Where(x => x.Intersection != null && x.Intersection.Y >= pos.Y)
278        .Select(x => Tuple.Create(x.Position, x.Item));
279    }
280
281    private IEnumerable<Tuple<PackingPosition, PackingItem>> GetItemsInfront(PackingPosition pos) {
282      var line = new Line3D(pos, new Vector3D(0, 0, 1));
283      return Items.Select(x => new {
284        Item = x.Value,
285        Position = Positions[x.Key],
286        Intersection = line.Intersect(new Plane3D(Positions[x.Key], x.Value, Side.Back))
287      }).Where(x => x.Intersection != null && x.Intersection.Y >= pos.Y)
288        .Select(x => Tuple.Create(x.Position, x.Item));
289    }
290
291    private IEnumerable<Tuple<PackingPosition, PackingItem>> GetItemsOnRight(PackingPosition pos) {
292      var line = new Line3D(pos, new Vector3D(1, 0, 0));
293      return Items.Select(x => new {
294        Item = x.Value,
295        Position = Positions[x.Key],
296        Intersection = line.Intersect(new Plane3D(Positions[x.Key], x.Value, Side.Left))
297      }).Where(x => x.Intersection != null && x.Intersection.Y >= pos.Y)
298        .Select(x => Tuple.Create(x.Position, x.Item));
299    }
300    #endregion
301
302    public override PackingPosition FindExtremePointForItem(PackingItem item, bool rotated, bool stackingConstraints) {
303      PackingItem newItem = new PackingItem(
304        rotated ? item.Depth : item.Width,
305        item.Height,
306        rotated ? item.Width : item.Depth,
307        item.TargetBin, item.Weight, item.Material);
308
309      var ep = ExtremePoints.Where(x => IsPositionFeasible(newItem, x, stackingConstraints))
310                            .FirstOrDefault();
311      if (ep != null) {
312        var result = new PackingPosition(ep.AssignedBin, ep.X, ep.Y, ep.Z, rotated);
313        return result;
314      }
315      return null;
316    }
317
318    public override bool IsPositionFeasible(PackingItem item, PackingPosition position, bool stackingConstraints) {
319      var feasible = base.IsPositionFeasible(item, position, stackingConstraints);
320      return feasible
321        && IsSupportedByAtLeastOnePoint(item, position)
322        && (!stackingConstraints || (IsStaticStable(item, position) && IsWeightSupported(item, position)));
323    }
324
325    #region Sliding based packing   
326    public override PackingPosition FindPositionBySliding(PackingItem item, bool rotated, bool stackingConstraints) {
327      //Starting-position at upper right corner (=left bottom point of item-rectangle is at position item.width,item.height)
328      PackingPosition currentPosition = new PackingPosition(0,
329        BinShape.Width - (rotated ? item.Depth : item.Width),
330        BinShape.Height - item.Height,
331        BinShape.Depth - (rotated ? item.Width : item.Depth), rotated);
332      //Slide the item as far as possible to the bottom
333      while (IsPositionFeasible(item, PackingPosition.MoveDown(currentPosition), stackingConstraints)
334        || IsPositionFeasible(item, PackingPosition.MoveLeft(currentPosition), stackingConstraints)
335        || IsPositionFeasible(item, PackingPosition.MoveBack(currentPosition), stackingConstraints)) {
336        //Slide the item as far as possible to the left
337        while (IsPositionFeasible(item, PackingPosition.MoveLeft(currentPosition), stackingConstraints)
338      || IsPositionFeasible(item, PackingPosition.MoveBack(currentPosition), stackingConstraints)) {
339          //Slide the item as far as possible to the back
340          while (IsPositionFeasible(item, PackingPosition.MoveBack(currentPosition), stackingConstraints)) {
341            currentPosition = PackingPosition.MoveBack(currentPosition);
342          }
343          if (IsPositionFeasible(item, PackingPosition.MoveLeft(currentPosition), stackingConstraints))
344            currentPosition = PackingPosition.MoveLeft(currentPosition);
345        }
346        if (IsPositionFeasible(item, PackingPosition.MoveDown(currentPosition), stackingConstraints))
347          currentPosition = PackingPosition.MoveDown(currentPosition);
348      }
349
350      return IsPositionFeasible(item, currentPosition, stackingConstraints) ? currentPosition : null;
351    }
352
353    public override void SlidingBasedPacking(ref IList<int> sequence, IList<PackingItem> items, bool stackingConstraints) {
354      var temp = new List<int>(sequence);
355      for (int i = 0; i < temp.Count; i++) {
356        var item = items[temp[i]];
357        var position = FindPositionBySliding(item, false, stackingConstraints);
358        if (position != null) {
359          PackItem(temp[i], item, position);
360          sequence.Remove(temp[i]);
361        }
362      }
363    }
364    public override void SlidingBasedPacking(ref IList<int> sequence, IList<PackingItem> items, Dictionary<int, bool> rotationArray, bool stackingConstraints) {
365      var temp = new List<int>(sequence);
366      for (int i = 0; i < temp.Count; i++) {
367        var item = items[temp[i]];
368        var position = FindPositionBySliding(item, rotationArray[i], stackingConstraints);
369        if (position != null) {
370          PackItem(temp[i], item, position);
371          sequence.Remove(temp[i]);
372        }
373      }
374    }
375    #endregion
376    public override void ExtremePointBasedPacking(ref IList<int> sequence, IList<PackingItem> items, bool stackingConstraints) {
377      var temp = new List<int>(sequence);
378      foreach (int itemID in temp) {
379        var item = items[itemID];
380        var positionFound = FindExtremePointForItem(item, false, stackingConstraints);
381        if (positionFound != null) {
382          PackItem(itemID, item, positionFound);
383          sequence.Remove(itemID);
384        }
385      }
386    }
387    public override void ExtremePointBasedPacking(ref IList<int> sequence, IList<PackingItem> items, bool stackingConstraints, Dictionary<int, bool> rotationArray) {
388      var temp = new List<int>(sequence);
389      foreach (int itemID in temp) {
390        var item = items[itemID];
391        var positionFound = FindExtremePointForItem(item, rotationArray[itemID], stackingConstraints);
392        if (positionFound != null) {
393          PackItem(itemID, item, positionFound);
394          sequence.Remove(itemID);
395        }
396      }
397    }
398    public override int ShortestPossibleSideFromPoint(PackingPosition position) {
399
400      int shortestSide = int.MaxValue;
401      int width = BinShape.Width;
402      int height = BinShape.Height;
403      int depth = BinShape.Depth;
404
405      if (position.X >= width || position.Y >= height || position.Z >= depth)
406        return shortestSide;
407
408      PackingPosition current = new PackingPosition(0, position.X, position.Y, position.Z);
409      while (current.X < width && IsPointOccupied(current)) { current = PackingPosition.MoveRight(current); }
410      if (current.X - position.X < shortestSide)
411        shortestSide = current.X - position.X;
412
413
414      current = new PackingPosition(0, position.X, position.Y, position.Z);
415      while (current.Y < height && IsPointOccupied(current)) { current = PackingPosition.MoveUp(current); }
416      if (current.Y - position.Y < shortestSide)
417        shortestSide = current.Y - position.Y;
418
419
420      current = new PackingPosition(0, position.X, position.Y, position.Z);
421      while (current.Z < depth && IsPointOccupied(current)) { current = PackingPosition.MoveFront(current); }
422      if (current.Z - position.Z < shortestSide)
423        shortestSide = current.Z - position.Z;
424
425      return shortestSide;
426    }
427    public override bool IsStaticStable(PackingItem item, PackingPosition position) {
428      //Static stability is given, if item is placed on the ground
429      if (position.Y == 0)
430        return true;
431
432      if (IsPointOccupied(new PackingPosition(0, position.X, position.Y - 1, position.Z))
433        && IsPointOccupied(new PackingPosition(0, position.X + item.Width - 1, position.Y - 1, position.Z))
434        && IsPointOccupied(new PackingPosition(0, position.X, position.Y - 1, position.Z + item.Depth - 1))
435        && IsPointOccupied(new PackingPosition(0, position.X + item.Width - 1, position.Y - 1, position.Z + item.Depth - 1)))
436        return true;
437
438      return false;
439    }
440
441    public bool IsSupportedByAtLeastOnePoint(PackingItem item, PackingPosition position) {
442      if (position.Y == 0)
443        return true;
444
445      int y = position.Y - 1;
446      for (int x = position.X; x < position.X + item.Width; x++)
447        for (int z = position.Z; z < position.Z + item.Depth; z++)
448          if (IsPointOccupied(new PackingPosition(0, x, y, z)))
449            return true;
450
451      return false;
452    }
453    public bool IsWeightSupported(PackingItem item, PackingPosition ep) {
454      if (ep.Y == 0)
455        return true;
456
457      if (Items[PointOccupation(new PackingPosition(0, ep.X, ep.Y - 1, ep.Z))].SupportsStacking(item)
458        && Items[PointOccupation(new PackingPosition(0, ep.X + item.Width - 1, ep.Y - 1, ep.Z))].SupportsStacking(item)
459        && Items[PointOccupation(new PackingPosition(0, ep.X, ep.Y - 1, ep.Z + item.Depth - 1))].SupportsStacking(item)
460        && Items[PointOccupation(new PackingPosition(0, ep.X + item.Width - 1, ep.Y - 1, ep.Z + item.Depth - 1))].SupportsStacking(item))
461        return true;
462
463      return false;
464    }
465
466    protected override void InitializeOccupationLayers() {
467      for (int i = 0; i * 10 <= BinShape.Depth; i += 1) {
468        OccupationLayers[i] = new List<int>();
469      }
470    }
471    protected override void AddNewItemToOccupationLayers(int itemID, PackingItem item, PackingPosition position) {
472      int z1 = position.Z / 10;
473      int z2 = (position.Z + (position.Rotated ? item.Width : item.Depth)) / 10;
474
475      for (int i = z1; i <= z2; i++)
476        OccupationLayers[i].Add(itemID);
477    }
478    protected override List<int> GetLayerItemIDs(PackingPosition position) {
479      return OccupationLayers[position.Z / 10];
480    }
481    protected override List<int> GetLayerItemIDs(PackingItem item, PackingPosition position) {
482      List<int> result = new List<int>();
483      int z1 = position.Z / 10;
484      int z2 = (position.Z + (position.Rotated ? item.Width : item.Depth)) / 10;
485
486      for (int i = z1; i <= z2; i++)
487        result.AddRange(OccupationLayers[i]);
488      return result;
489    }
490   
491    public void UpdateResidualSpace(PackingItem item, PackingPosition pos) {
492      foreach (var ep in ExtremePoints.ToList()) {
493        var rs = ResidualSpace[ep];
494        var depth = pos.Rotated ? item.Width : item.Depth;
495        var width = pos.Rotated ? item.Depth : item.Width;
496        var changed = false;
497        if (ep.Z >= pos.Z && ep.Z < pos.Z + depth) {
498          if (ep.X <= pos.X && ep.Y >= pos.Y && ep.Y < pos.Y + item.Height) {
499            var diff = pos.X - ep.X;
500            var newRSX = Math.Min(rs.Item1, diff);
501            rs = Tuple.Create(newRSX, rs.Item2, rs.Item3);
502            changed = true;
503          }
504          if (ep.Y <= pos.Y && ep.X >= pos.X && ep.X < pos.X + width) {
505            var diff = pos.Y - ep.Y;
506            var newRSY = Math.Min(rs.Item2, diff);
507            rs = Tuple.Create(rs.Item1, newRSY, rs.Item3);
508            changed = true;
509          }
510        }
511        if (ep.Z <= pos.Z &&
512            ep.Y >= pos.Y && ep.Y < pos.Y + item.Height &&
513            ep.X >= pos.X && ep.X < pos.X + width) {
514          var diff = pos.Z - ep.Z;
515          var newRSZ = Math.Min(rs.Item3, diff);
516          rs = Tuple.Create(rs.Item1, rs.Item2, newRSZ);
517          changed = true;
518        }
519        if (changed) {
520          if (IsNonZero(rs) && !IsWithinResidualSpaceOfAnotherExtremePoint(new Vector3D(ep), rs)) {
521            ResidualSpace[ep] = rs;
522          } else {
523            ExtremePoints.Remove(ep);
524            ResidualSpace.Remove(ep);
525          }
526        }
527      }
528      return;
529    }
530
531    #region Helper classes for vector math
532    private class Vector3D {
533      public int X;
534      public int Y;
535      public int Z;
536      public Vector3D() { }
537      public Vector3D(int x, int y, int z) {
538        X = x;
539        Y = y;
540        Z = z;
541      }
542      public Vector3D(PackingPosition pos) {
543        X = pos.X;
544        Y = pos.Y;
545        Z = pos.Z;
546      }
547      public static Vector3D AlongX(Vector3D pos, PackingItem item) {
548        return new Vector3D(
549          pos.X + item.Width,
550          pos.Y,
551          pos.Z
552        );
553      }
554      public static Vector3D AlongY(Vector3D pos, PackingItem item) {
555        return new Vector3D(
556          pos.X,
557          pos.Y + item.Height,
558          pos.Z
559        );
560      }
561      public static Vector3D AlongZ(Vector3D pos, PackingItem item) {
562        return new Vector3D(
563          pos.X,
564          pos.Y,
565          pos.Z + item.Depth
566        );
567      }
568      public static Vector3D AlongX(PackingPosition pos, PackingItem item) {
569        return new Vector3D(
570          pos.X + item.Width,
571          pos.Y,
572          pos.Z
573        );
574      }
575      public static Vector3D AlongY(PackingPosition pos, PackingItem item) {
576        return new Vector3D(
577          pos.X,
578          pos.Y + item.Height,
579          pos.Z
580        );
581      }
582      public static Vector3D AlongZ(PackingPosition pos, PackingItem item) {
583        return new Vector3D(
584          pos.X,
585          pos.Y,
586          pos.Z + item.Depth
587        );
588      }
589
590      public Vector3D Cross(Vector3D b) {
591        return new Vector3D(
592          Y * b.Z - Z * b.Y,
593          -X * b.Z + Z * b.X,
594          X * b.Y - Y * b.X
595        );
596      }
597
598      public bool IsInside(PackingPosition pos, Tuple<int, int, int> rs) {
599        return X >= pos.X && X < pos.X + rs.Item1
600          && Y >= pos.Y && Y < pos.Y + rs.Item2
601          && Z >= pos.Z && Z < pos.Z + rs.Item3;
602      }
603
604      public static int operator *(Vector3D a, Vector3D b) {
605        return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
606      }
607      public static Vector3D operator *(int a, Vector3D b) {
608        return new Vector3D(a * b.X, a * b.Y, a * b.Z);
609      }
610      public static Vector3D operator *(Vector3D a, int b) {
611        return new Vector3D(a.X * b, a.Y * b, a.Z * b);
612      }
613      public static Vector3D operator +(Vector3D a, Vector3D b) {
614        return new Vector3D(a.X + b.X, a.Y + b.Y, a.Z + b.Z);
615      }
616      public static Vector3D operator -(Vector3D a, Vector3D b) {
617        return new Vector3D(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
618      }
619
620      public override bool Equals(object obj) {
621        var packPos = obj as PackingPosition;
622        if (packPos != null) {
623          return X == packPos.X && Y == packPos.Y && Z == packPos.Z;
624        }
625        var vec = obj as Vector3D;
626        if (vec != null) {
627          return X == vec.X && Y == vec.Y && Z == vec.Z;
628        }
629        return false;
630      }
631    }
632
633    // A line is given as a point and a directing vector
634    private class Line3D {
635      public Vector3D Point;
636      public Vector3D Direction;
637
638      public Line3D(Vector3D point, Vector3D direction) {
639        Point = point;
640        Direction = direction;
641      }
642      public Line3D(PackingPosition position, Vector3D direction) {
643        Point = new Vector3D(position);
644        Direction = direction;
645      }
646
647      public bool Intersects(Plane3D plane) {
648        return plane.Intersects(this);
649      }
650
651      public Vector3D Intersect(Plane3D plane) {
652        return plane.Intersect(this);
653      }
654    }
655
656    private enum Side { Top, Left, Bottom, Right, Front, Back }
657    // A plane is given as a point and two directing vectors
658    private class Plane3D {
659      public Vector3D Point { get; private set; }
660      public Vector3D Direction1 { get; private set; }
661      public Vector3D Direction2 { get; private set; }
662      public Vector3D Normal { get; private set; }
663      private int rhs;
664
665      public Plane3D(PackingShape bin, Side side) {
666        switch (side) {
667          case Side.Top: // ZX plane
668            Point = new Vector3D(0, bin.Height, 0);
669            Direction1 = new Vector3D(0, 0, bin.Depth);
670            Direction2 = new Vector3D(bin.Width, 0, 0);
671            break;
672          case Side.Left: // ZY plane
673            Point = new Vector3D(0, 0, 0);
674            Direction1 = new Vector3D(0, 0, bin.Depth);
675            Direction2 = new Vector3D(0, bin.Height, 0);
676            break;
677          case Side.Bottom: // XZ plane
678            Point = new Vector3D(0, 0, 0);
679            Direction1 = new Vector3D(bin.Width, 0, 0);
680            Direction2 = new Vector3D(0, 0, bin.Depth);
681            break;
682          case Side.Right: // YZ plane
683            Point = new Vector3D(bin.Width, 0, 0);
684            Direction1 = new Vector3D(0, bin.Height, 0);
685            Direction2 = new Vector3D(0, 0, bin.Depth);
686            break;
687          case Side.Front: // XY plane
688            Point = new Vector3D(0, 0, bin.Depth);
689            Direction1 = new Vector3D(bin.Width, 0, 0);
690            Direction2 = new Vector3D(0, bin.Height, 0);
691            break;
692          case Side.Back: // YX plane
693            Point = new Vector3D(0, 0, 0);
694            Direction1 = new Vector3D(0, bin.Height, 0);
695            Direction2 = new Vector3D(bin.Width, 0, 0);
696            break;
697        }
698        Normal = Direction1.Cross(Direction2);
699        rhs = 0;
700      }
701
702      public Plane3D(PackingPosition pos, PackingItem item, Side side) {
703        // the directing vectors are chosen such that normal always points outside the packing item
704        switch (side) {
705          case Side.Top: // ZX plane
706            Point = new Vector3D(pos.X, pos.Y + item.Height, pos.Z);
707            Direction1 = new Vector3D(0, 0, pos.Rotated ? item.Width : item.Depth);
708            Direction2 = new Vector3D(pos.Rotated ? item.Depth : item.Width, 0, 0);
709            break;
710          case Side.Left: // ZY plane
711            Point = new Vector3D(pos.X, pos.Y, pos.Z);
712            Direction1 = new Vector3D(0, 0, pos.Rotated ? item.Width : item.Depth);
713            Direction2 = new Vector3D(0, item.Height, 0);
714            break;
715          case Side.Bottom: // XZ plane
716            Point = new Vector3D(pos.X, pos.Y, pos.Z);
717            Direction1 = new Vector3D(pos.Rotated ? item.Depth : item.Width, 0, 0);
718            Direction2 = new Vector3D(0, 0, pos.Rotated ? item.Width : item.Depth);
719            break;
720          case Side.Right: // YZ plane
721            Point = new Vector3D(pos.X + (pos.Rotated ? item.Depth : item.Width), pos.Y, pos.Z);
722            Direction1 = new Vector3D(0, item.Height, 0);
723            Direction2 = new Vector3D(0, 0, pos.Rotated ? item.Width : item.Depth);
724            break;
725          case Side.Front: // XY plane
726            Point = new Vector3D(pos.X, pos.Y, pos.Z + (pos.Rotated ? item.Width : item.Depth));
727            Direction1 = new Vector3D(pos.Rotated ? item.Depth : item.Width, 0, 0);
728            Direction2 = new Vector3D(0, item.Height, 0);
729            break;
730          case Side.Back: // YX plane
731            Point = new Vector3D(pos.X, pos.Y, pos.Z);
732            Direction1 = new Vector3D(0, item.Height, 0);
733            Direction2 = new Vector3D(pos.Rotated ? item.Depth : item.Width, 0, 0);
734            break;
735        }
736        Normal = Direction1.Cross(Direction2);
737        rhs = Normal.X * Point.X + Normal.Y * Point.Y + Normal.Z * Point.Z;
738      }
739
740      public bool IsElementOf(Vector3D point) {
741        return Normal.X * point.X + Normal.Y * point.Y + Normal.Z * point.Z == rhs;
742      }
743
744      public bool Intersects(Line3D line) {
745        return Intersect(line) != null;
746      }
747
748      public Vector3D Intersect(Line3D line) {
749        var denom = Normal * line.Direction;
750        if (denom == 0) {
751          // dir is perpendicular to the normal vector of the plane
752          // this means they intersect if p1 is element of the plane
753          // also the plane does not stretch infinitely, but is bound
754          // to the parallelogram spanned by the point and the two
755          // directing vectors
756          if (IsElementOf(line.Point) && IsWithinDirectionalVectors(line.Point))
757            return line.Point;
758          else return null;
759        }
760        var intersect = line.Point + ((Normal * (Point - line.Point)) / denom) * line.Direction;
761        if (IsWithinDirectionalVectors(intersect)) return intersect;
762        return null;
763      }
764
765      public bool IsWithinDirectionalVectors(Vector3D point) {
766        return point.X >= Point.X && (Direction1.X + Direction2.X == 0 || (point.X < Point.X + Direction1.X || point.X < Point.X + Direction2.X))
767            && point.Y >= Point.Y && (Direction1.Y + Direction2.Y == 0 || (point.Y < Point.Y + Direction1.Y || point.Y < Point.Y + Direction2.Y))
768            && point.Z >= Point.Z && (Direction1.Z + Direction2.Z == 0 || (point.Z < Point.Z + Direction1.Z || point.Z < Point.Z + Direction2.Z));
769      }
770    }
771    #endregion
772  }
773}
Note: See TracBrowser for help on using the repository browser.