Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 15308 was 15308, checked in by abeham, 7 years ago

#2817:

  • fixed remaining bugs
File size: 33.5 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    private Vector3D ProjectBackward(Vector3D pos) {
206      var line = new Line3D(pos, new Vector3D(0, 0, -1));
207      return Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] })
208                  .Select(x => new Plane3D(x.Position, x.Item, Side.Front))
209                  .Concat(new[] { new Plane3D(BinShape, Side.Back) })
210                  .Select(x => x.Intersect(line))
211                  .Where(x => x != null && x.Z <= pos.Z)
212                  .MaxItems(x => x.Z).First();
213    }
214
215    private Vector3D ProjectLeft(Vector3D pos) {
216      var line = new Line3D(pos, new Vector3D(-1, 0, 0));
217      return Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] })
218                  .Select(x => new Plane3D(x.Position, x.Item, Side.Right))
219                  .Concat(new[] { new Plane3D(BinShape, Side.Left) })
220                  .Select(x => x.Intersect(line))
221                  .Where(x => x != null && x.X <= pos.X)
222                  .MaxItems(x => x.X).First();
223    }
224
225    private Vector3D ProjectDown(Vector3D pos) {
226      var line = new Line3D(pos, new Vector3D(0, -1, 0));
227      return Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] })
228                  .Select(x => new Plane3D(x.Position, x.Item, Side.Top))
229                  .Concat(new[] { new Plane3D(BinShape, Side.Bottom) })
230                  .Select(x => x.Intersect(line))
231                  .Where(x => x != null && x.Y <= pos.Y)
232                  .MaxItems(x => x.Y).First();
233    }
234
235    private Vector3D ProjectForward(Vector3D pos) {
236      var line = new Line3D(pos, new Vector3D(0, 0, 1));
237      return Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] })
238                  .Select(x => new Plane3D(x.Position, x.Item, Side.Back))
239                  .Concat(new[] { new Plane3D(BinShape, Side.Front) })
240                  .Select(x => x.Intersect(line))
241                  .Where(x => x != null && x.Z >= pos.Z)
242                  .MinItems(x => x.Z).First();
243    }
244
245    private Vector3D ProjectRight(Vector3D pos) {
246      var line = new Line3D(pos, new Vector3D(1, 0, 0));
247      return Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] })
248                  .Select(x => new Plane3D(x.Position, x.Item, Side.Left))
249                  .Concat(new[] { new Plane3D(BinShape, Side.Right) })
250                  .Select(x => x.Intersect(line))
251                  .Where(x => x != null && x.X >= pos.X)
252                  .MinItems(x => x.X).First();
253    }
254
255    private Vector3D ProjectUp(Vector3D pos) {
256      var line = new Line3D(pos, new Vector3D(0, 1, 0));
257      return Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] })
258                  .Select(x => new Plane3D(x.Position, x.Item, Side.Bottom))
259                  .Concat(new[] { new Plane3D(BinShape, Side.Top) })
260                  .Select(x => x.Intersect(line))
261                  .Where(x => x != null && x.Y >= pos.Y)
262                  .MinItems(x => x.Y).First();
263    }
264
265    private IEnumerable<Tuple<PackingPosition, PackingItem>> GetItemsAbove(PackingPosition pos) {
266      var line = new Line3D(pos, new Vector3D(0, 1, 0));
267      return Items.Select(x => new {
268        Item = x.Value,
269        Position = Positions[x.Key],
270        Intersection = line.Intersect(new Plane3D(Positions[x.Key], x.Value, Side.Bottom))
271      }).Where(x => x.Intersection != null && x.Intersection.Y >= pos.Y)
272        .Select(x => Tuple.Create(x.Position, x.Item));
273    }
274
275    private IEnumerable<Tuple<PackingPosition, PackingItem>> GetItemsInfront(PackingPosition pos) {
276      var line = new Line3D(pos, new Vector3D(0, 0, 1));
277      return Items.Select(x => new {
278        Item = x.Value,
279        Position = Positions[x.Key],
280        Intersection = line.Intersect(new Plane3D(Positions[x.Key], x.Value, Side.Back))
281      }).Where(x => x.Intersection != null && x.Intersection.Y >= pos.Y)
282        .Select(x => Tuple.Create(x.Position, x.Item));
283    }
284
285    private IEnumerable<Tuple<PackingPosition, PackingItem>> GetItemsOnRight(PackingPosition pos) {
286      var line = new Line3D(pos, new Vector3D(1, 0, 0));
287      return Items.Select(x => new {
288        Item = x.Value,
289        Position = Positions[x.Key],
290        Intersection = line.Intersect(new Plane3D(Positions[x.Key], x.Value, Side.Left))
291      }).Where(x => x.Intersection != null && x.Intersection.Y >= pos.Y)
292        .Select(x => Tuple.Create(x.Position, x.Item));
293    }
294
295    public override PackingPosition FindExtremePointForItem(PackingItem item, bool rotated, bool stackingConstraints) {
296      PackingItem newItem = new PackingItem(
297        rotated ? item.Depth : item.Width,
298        item.Height,
299        rotated ? item.Width : item.Depth,
300        item.TargetBin, item.Weight, item.Material);
301
302      var ep = ExtremePoints.Where(x => IsPositionFeasible(newItem, x, stackingConstraints))
303                            .FirstOrDefault();
304      if (ep != null) {
305        var result = new PackingPosition(ep.AssignedBin, ep.X, ep.Y, ep.Z, rotated);
306        return result;
307      }
308      return null;
309    }
310
311    public override bool IsPositionFeasible(PackingItem item, PackingPosition position, bool stackingConstraints) {
312      var feasible = base.IsPositionFeasible(item, position, stackingConstraints);
313      return feasible
314        && IsSupportedByAtLeastOnePoint(item, position)
315        && (!stackingConstraints || (IsStaticStable(item, position) && IsWeightSupported(item, position)));
316    }
317
318    public override PackingPosition FindPositionBySliding(PackingItem item, bool rotated, bool stackingConstraints) {
319      //Starting-position at upper right corner (=left bottom point of item-rectangle is at position item.width,item.height)
320      PackingPosition currentPosition = new PackingPosition(0,
321        BinShape.Width - (rotated ? item.Depth : item.Width),
322        BinShape.Height - item.Height,
323        BinShape.Depth - (rotated ? item.Width : item.Depth), rotated);
324      //Slide the item as far as possible to the bottom
325      while (IsPositionFeasible(item, PackingPosition.MoveDown(currentPosition), stackingConstraints)
326        || IsPositionFeasible(item, PackingPosition.MoveLeft(currentPosition), stackingConstraints)
327        || IsPositionFeasible(item, PackingPosition.MoveBack(currentPosition), stackingConstraints)) {
328        //Slide the item as far as possible to the left
329        while (IsPositionFeasible(item, PackingPosition.MoveLeft(currentPosition), stackingConstraints)
330      || IsPositionFeasible(item, PackingPosition.MoveBack(currentPosition), stackingConstraints)) {
331          //Slide the item as far as possible to the back
332          while (IsPositionFeasible(item, PackingPosition.MoveBack(currentPosition), stackingConstraints)) {
333            currentPosition = PackingPosition.MoveBack(currentPosition);
334          }
335          if (IsPositionFeasible(item, PackingPosition.MoveLeft(currentPosition), stackingConstraints))
336            currentPosition = PackingPosition.MoveLeft(currentPosition);
337        }
338        if (IsPositionFeasible(item, PackingPosition.MoveDown(currentPosition), stackingConstraints))
339          currentPosition = PackingPosition.MoveDown(currentPosition);
340      }
341
342      return IsPositionFeasible(item, currentPosition, stackingConstraints) ? currentPosition : null;
343    }
344
345    public override void SlidingBasedPacking(ref IList<int> sequence, IList<PackingItem> items, bool stackingConstraints) {
346      var temp = new List<int>(sequence);
347      for (int i = 0; i < temp.Count; i++) {
348        var item = items[temp[i]];
349        var position = FindPositionBySliding(item, false, stackingConstraints);
350        if (position != null) {
351          PackItem(temp[i], item, position);
352          sequence.Remove(temp[i]);
353        }
354      }
355    }
356    public override void SlidingBasedPacking(ref IList<int> sequence, IList<PackingItem> items, Dictionary<int, bool> rotationArray, bool stackingConstraints) {
357      var temp = new List<int>(sequence);
358      for (int i = 0; i < temp.Count; i++) {
359        var item = items[temp[i]];
360        var position = FindPositionBySliding(item, rotationArray[i], stackingConstraints);
361        if (position != null) {
362          PackItem(temp[i], item, position);
363          sequence.Remove(temp[i]);
364        }
365      }
366    }
367    public override void ExtremePointBasedPacking(ref IList<int> sequence, IList<PackingItem> items, bool stackingConstraints) {
368      var temp = new List<int>(sequence);
369      foreach (int itemID in temp) {
370        var item = items[itemID];
371        var positionFound = FindExtremePointForItem(item, false, stackingConstraints);
372        if (positionFound != null) {
373          PackItem(itemID, item, positionFound);
374          sequence.Remove(itemID);
375        }
376      }
377    }
378    public override void ExtremePointBasedPacking(ref IList<int> sequence, IList<PackingItem> items, bool stackingConstraints, Dictionary<int, bool> rotationArray) {
379      var temp = new List<int>(sequence);
380      foreach (int itemID in temp) {
381        var item = items[itemID];
382        var positionFound = FindExtremePointForItem(item, rotationArray[itemID], stackingConstraints);
383        if (positionFound != null) {
384          PackItem(itemID, item, positionFound);
385          sequence.Remove(itemID);
386        }
387      }
388    }
389    public override int ShortestPossibleSideFromPoint(PackingPosition position) {
390
391      int shortestSide = int.MaxValue;
392      int width = BinShape.Width;
393      int height = BinShape.Height;
394      int depth = BinShape.Depth;
395
396      if (position.X >= width || position.Y >= height || position.Z >= depth)
397        return shortestSide;
398
399      PackingPosition current = new PackingPosition(0, position.X, position.Y, position.Z);
400      while (current.X < width && IsPointOccupied(current)) { current = PackingPosition.MoveRight(current); }
401      if (current.X - position.X < shortestSide)
402        shortestSide = current.X - position.X;
403
404
405      current = new PackingPosition(0, position.X, position.Y, position.Z);
406      while (current.Y < height && IsPointOccupied(current)) { current = PackingPosition.MoveUp(current); }
407      if (current.Y - position.Y < shortestSide)
408        shortestSide = current.Y - position.Y;
409
410
411      current = new PackingPosition(0, position.X, position.Y, position.Z);
412      while (current.Z < depth && IsPointOccupied(current)) { current = PackingPosition.MoveFront(current); }
413      if (current.Z - position.Z < shortestSide)
414        shortestSide = current.Z - position.Z;
415
416      return shortestSide;
417    }
418    public override bool IsStaticStable(PackingItem item, PackingPosition position) {
419      //Static stability is given, if item is placed on the ground
420      if (position.Y == 0)
421        return true;
422
423      if (IsPointOccupied(new PackingPosition(0, position.X, position.Y - 1, position.Z))
424        && IsPointOccupied(new PackingPosition(0, position.X + item.Width - 1, position.Y - 1, position.Z))
425        && IsPointOccupied(new PackingPosition(0, position.X, position.Y - 1, position.Z + item.Depth - 1))
426        && IsPointOccupied(new PackingPosition(0, position.X + item.Width - 1, position.Y - 1, position.Z + item.Depth - 1)))
427        return true;
428
429      return false;
430    }
431
432    public bool IsSupportedByAtLeastOnePoint(PackingItem item, PackingPosition position) {
433      if (position.Y == 0)
434        return true;
435
436      int y = position.Y - 1;
437      for (int x = position.X; x < position.X + item.Width; x++)
438        for (int z = position.Z; z < position.Z + item.Depth; z++)
439          if (IsPointOccupied(new PackingPosition(0, x, y, z)))
440            return true;
441
442      return false;
443    }
444    public bool IsWeightSupported(PackingItem item, PackingPosition ep) {
445      if (ep.Y == 0)
446        return true;
447
448      if (Items[PointOccupation(new PackingPosition(0, ep.X, ep.Y - 1, ep.Z))].SupportsStacking(item)
449        && Items[PointOccupation(new PackingPosition(0, ep.X + item.Width - 1, ep.Y - 1, ep.Z))].SupportsStacking(item)
450        && Items[PointOccupation(new PackingPosition(0, ep.X, ep.Y - 1, ep.Z + item.Depth - 1))].SupportsStacking(item)
451        && Items[PointOccupation(new PackingPosition(0, ep.X + item.Width - 1, ep.Y - 1, ep.Z + item.Depth - 1))].SupportsStacking(item))
452        return true;
453
454      return false;
455    }
456
457    protected override void InitializeOccupationLayers() {
458      for (int i = 0; i * 10 <= BinShape.Depth; i += 1) {
459        OccupationLayers[i] = new List<int>();
460      }
461    }
462    protected override void AddNewItemToOccupationLayers(int itemID, PackingItem item, PackingPosition position) {
463      int z1 = position.Z / 10;
464      int z2 = (position.Z + (position.Rotated ? item.Width : item.Depth)) / 10;
465
466      for (int i = z1; i <= z2; i++)
467        OccupationLayers[i].Add(itemID);
468    }
469    protected override List<int> GetLayerItemIDs(PackingPosition position) {
470      return OccupationLayers[position.Z / 10];
471    }
472    protected override List<int> GetLayerItemIDs(PackingItem item, PackingPosition position) {
473      List<int> result = new List<int>();
474      int z1 = position.Z / 10;
475      int z2 = (position.Z + (position.Rotated ? item.Width : item.Depth)) / 10;
476
477      for (int i = z1; i <= z2; i++)
478        result.AddRange(OccupationLayers[i]);
479      return result;
480    }
481   
482    public void UpdateResidualSpace(PackingItem item, PackingPosition pos) {
483      foreach (var ep in ExtremePoints.ToList()) {
484        var rs = ResidualSpace[ep];
485        var depth = pos.Rotated ? item.Width : item.Depth;
486        var width = pos.Rotated ? item.Depth : item.Width;
487        var changed = false;
488        if (ep.Z >= pos.Z && ep.Z < pos.Z + depth) {
489          if (ep.X <= pos.X && ep.Y >= pos.Y && ep.Y < pos.Y + item.Height) {
490            var diff = pos.X - ep.X;
491            var newRSX = Math.Min(rs.Item1, diff);
492            rs = Tuple.Create(newRSX, rs.Item2, rs.Item3);
493            changed = true;
494          }
495          if (ep.Y <= pos.Y && ep.X >= pos.X && ep.X < pos.X + width) {
496            var diff = pos.Y - ep.Y;
497            var newRSY = Math.Min(rs.Item2, diff);
498            rs = Tuple.Create(rs.Item1, newRSY, rs.Item3);
499            changed = true;
500          }
501        }
502        if (ep.Z <= pos.Z &&
503            ep.Y >= pos.Y && ep.Y < pos.Y + item.Height &&
504            ep.X >= pos.X && ep.X < pos.X + width) {
505          var diff = pos.Z - ep.Z;
506          var newRSZ = Math.Min(rs.Item3, diff);
507          rs = Tuple.Create(rs.Item1, rs.Item2, newRSZ);
508          changed = true;
509        }
510        if (changed) {
511          if (IsNonZero(rs) && !IsWithinResidualSpaceOfAnotherExtremePoint(new Vector3D(ep), rs)) {
512            ResidualSpace[ep] = rs;
513          } else {
514            ExtremePoints.Remove(ep);
515            ResidualSpace.Remove(ep);
516          }
517        }
518      }
519      return;
520    }
521
522    #region Helper classes for vector math
523    private class Vector3D {
524      public int X;
525      public int Y;
526      public int Z;
527      public Vector3D() { }
528      public Vector3D(int x, int y, int z) {
529        X = x;
530        Y = y;
531        Z = z;
532      }
533      public Vector3D(PackingPosition pos) {
534        X = pos.X;
535        Y = pos.Y;
536        Z = pos.Z;
537      }
538      public static Vector3D AlongX(Vector3D pos, PackingItem item) {
539        return new Vector3D(
540          pos.X + item.Width,
541          pos.Y,
542          pos.Z
543        );
544      }
545      public static Vector3D AlongY(Vector3D pos, PackingItem item) {
546        return new Vector3D(
547          pos.X,
548          pos.Y + item.Height,
549          pos.Z
550        );
551      }
552      public static Vector3D AlongZ(Vector3D pos, PackingItem item) {
553        return new Vector3D(
554          pos.X,
555          pos.Y,
556          pos.Z + item.Depth
557        );
558      }
559      public static Vector3D AlongX(PackingPosition pos, PackingItem item) {
560        return new Vector3D(
561          pos.X + item.Width,
562          pos.Y,
563          pos.Z
564        );
565      }
566      public static Vector3D AlongY(PackingPosition pos, PackingItem item) {
567        return new Vector3D(
568          pos.X,
569          pos.Y + item.Height,
570          pos.Z
571        );
572      }
573      public static Vector3D AlongZ(PackingPosition pos, PackingItem item) {
574        return new Vector3D(
575          pos.X,
576          pos.Y,
577          pos.Z + item.Depth
578        );
579      }
580
581      public Vector3D Cross(Vector3D b) {
582        return new Vector3D(
583          Y * b.Z - Z * b.Y,
584          -X * b.Z + Z * b.X,
585          X * b.Y - Y * b.X
586        );
587      }
588
589      public bool IsInside(PackingPosition pos, Tuple<int, int, int> rs) {
590        return X >= pos.X && X < pos.X + rs.Item1
591          && Y >= pos.Y && Y < pos.Y + rs.Item2
592          && Z >= pos.Z && Z < pos.Z + rs.Item3;
593      }
594
595      public static int operator *(Vector3D a, Vector3D b) {
596        return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
597      }
598      public static Vector3D operator *(int a, Vector3D b) {
599        return new Vector3D(a * b.X, a * b.Y, a * b.Z);
600      }
601      public static Vector3D operator *(Vector3D a, int b) {
602        return new Vector3D(a.X * b, a.Y * b, a.Z * b);
603      }
604      public static Vector3D operator +(Vector3D a, Vector3D b) {
605        return new Vector3D(a.X + b.X, a.Y + b.Y, a.Z + b.Z);
606      }
607      public static Vector3D operator -(Vector3D a, Vector3D b) {
608        return new Vector3D(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
609      }
610
611      public override bool Equals(object obj) {
612        var packPos = obj as PackingPosition;
613        if (packPos != null) {
614          return X == packPos.X && Y == packPos.Y && Z == packPos.Z;
615        }
616        var vec = obj as Vector3D;
617        if (vec != null) {
618          return X == vec.X && Y == vec.Y && Z == vec.Z;
619        }
620        return false;
621      }
622    }
623
624    // A line is given as a point and a directing vector
625    private class Line3D {
626      public Vector3D Point;
627      public Vector3D Direction;
628
629      public Line3D(Vector3D point, Vector3D direction) {
630        Point = point;
631        Direction = direction;
632      }
633      public Line3D(PackingPosition position, Vector3D direction) {
634        Point = new Vector3D(position);
635        Direction = direction;
636      }
637
638      public bool Intersects(Plane3D plane) {
639        return plane.Intersects(this);
640      }
641
642      public Vector3D Intersect(Plane3D plane) {
643        return plane.Intersect(this);
644      }
645    }
646
647    private enum Side { Top, Left, Bottom, Right, Front, Back }
648    // A plane is given as a point and two directing vectors
649    private class Plane3D {
650      public Vector3D Point { get; private set; }
651      public Vector3D Direction1 { get; private set; }
652      public Vector3D Direction2 { get; private set; }
653      public Vector3D Normal { get; private set; }
654      private int rhs;
655
656      public Plane3D(PackingShape bin, Side side) {
657        switch (side) {
658          case Side.Top: // ZX plane
659            Point = new Vector3D(0, bin.Height, 0);
660            Direction1 = new Vector3D(0, 0, bin.Depth);
661            Direction2 = new Vector3D(bin.Width, 0, 0);
662            break;
663          case Side.Left: // ZY plane
664            Point = new Vector3D(0, 0, 0);
665            Direction1 = new Vector3D(0, 0, bin.Depth);
666            Direction2 = new Vector3D(0, bin.Height, 0);
667            break;
668          case Side.Bottom: // XZ plane
669            Point = new Vector3D(0, 0, 0);
670            Direction1 = new Vector3D(bin.Width, 0, 0);
671            Direction2 = new Vector3D(0, 0, bin.Depth);
672            break;
673          case Side.Right: // YZ plane
674            Point = new Vector3D(bin.Width, 0, 0);
675            Direction1 = new Vector3D(0, bin.Height, 0);
676            Direction2 = new Vector3D(0, 0, bin.Depth);
677            break;
678          case Side.Front: // XY plane
679            Point = new Vector3D(0, 0, bin.Depth);
680            Direction1 = new Vector3D(bin.Width, 0, 0);
681            Direction2 = new Vector3D(0, bin.Height, 0);
682            break;
683          case Side.Back: // YX plane
684            Point = new Vector3D(0, 0, 0);
685            Direction1 = new Vector3D(0, bin.Height, 0);
686            Direction2 = new Vector3D(bin.Width, 0, 0);
687            break;
688        }
689        Normal = Direction1.Cross(Direction2);
690        rhs = 0;
691      }
692
693      public Plane3D(PackingPosition pos, PackingItem item, Side side) {
694        // the directing vectors are chosen such that normal always points outside the packing item
695        switch (side) {
696          case Side.Top: // ZX plane
697            Point = new Vector3D(pos.X, pos.Y + item.Height, pos.Z);
698            Direction1 = new Vector3D(0, 0, pos.Rotated ? item.Width : item.Depth);
699            Direction2 = new Vector3D(pos.Rotated ? item.Depth : item.Width, 0, 0);
700            break;
701          case Side.Left: // ZY plane
702            Point = new Vector3D(pos.X, pos.Y, pos.Z);
703            Direction1 = new Vector3D(0, 0, pos.Rotated ? item.Width : item.Depth);
704            Direction2 = new Vector3D(0, item.Height, 0);
705            break;
706          case Side.Bottom: // XZ plane
707            Point = new Vector3D(pos.X, pos.Y, pos.Z);
708            Direction1 = new Vector3D(pos.Rotated ? item.Depth : item.Width, 0, 0);
709            Direction2 = new Vector3D(0, 0, pos.Rotated ? item.Width : item.Depth);
710            break;
711          case Side.Right: // YZ plane
712            Point = new Vector3D(pos.X + (pos.Rotated ? item.Depth : item.Width), pos.Y, pos.Z);
713            Direction1 = new Vector3D(0, item.Height, 0);
714            Direction2 = new Vector3D(0, 0, pos.Rotated ? item.Width : item.Depth);
715            break;
716          case Side.Front: // XY plane
717            Point = new Vector3D(pos.X, pos.Y, pos.Z + (pos.Rotated ? item.Width : item.Depth));
718            Direction1 = new Vector3D(pos.Rotated ? item.Depth : item.Width, 0, 0);
719            Direction2 = new Vector3D(0, item.Height, 0);
720            break;
721          case Side.Back: // YX plane
722            Point = new Vector3D(pos.X, pos.Y, pos.Z);
723            Direction1 = new Vector3D(0, item.Height, 0);
724            Direction2 = new Vector3D(pos.Rotated ? item.Depth : item.Width, 0, 0);
725            break;
726        }
727        Normal = Direction1.Cross(Direction2);
728        rhs = Normal.X * Point.X + Normal.Y * Point.Y + Normal.Z * Point.Z;
729      }
730
731      public bool IsElementOf(Vector3D point) {
732        return Normal.X * point.X + Normal.Y * point.Y + Normal.Z * point.Z == rhs;
733      }
734
735      public bool Intersects(Line3D line) {
736        return Intersect(line) != null;
737      }
738
739      public Vector3D Intersect(Line3D line) {
740        var denom = Normal * line.Direction;
741        if (denom == 0) {
742          // dir is perpendicular to the normal vector of the plane
743          // this means they intersect if p1 is element of the plane
744          // also the plane does not stretch infinitely, but is bound
745          // to the parallelogram spanned by the point and the two
746          // directing vectors
747          if (IsElementOf(line.Point) && IsWithinDirectionalVectors(line.Point))
748            return line.Point;
749          else return null;
750        }
751        var intersect = line.Point + ((Normal * (Point - line.Point)) / denom) * line.Direction;
752        if (IsWithinDirectionalVectors(intersect)) return intersect;
753        return null;
754      }
755
756      public bool IsWithinDirectionalVectors(Vector3D point) {
757        return point.X >= Point.X && (Direction1.X + Direction2.X == 0 || (point.X < Point.X + Direction1.X || point.X < Point.X + Direction2.X))
758            && point.Y >= Point.Y && (Direction1.Y + Direction2.Y == 0 || (point.Y < Point.Y + Direction1.Y || point.Y < Point.Y + Direction2.Y))
759            && point.Z >= Point.Z && (Direction1.Z + Direction2.Z == 0 || (point.Z < Point.Z + Direction1.Z || point.Z < Point.Z + Direction2.Z));
760      }
761    }
762    #endregion
763  }
764}
Note: See TracBrowser for help on using the repository browser.