Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2817:

  • Avoided generating unnecessary extreme points
  • Added vector calculation code for line/plane intersection
File size: 31.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      ResidualSpace.Remove(position);
68      base.PackItem(itemID, item, position);
69    }
70
71    protected override void GenerateNewExtremePointsForNewItem(PackingItem newItem, PackingPosition position) {
72      int newWidth = position.Rotated ? newItem.Depth : newItem.Width;
73      int newDepth = position.Rotated ? newItem.Width : newItem.Depth;
74
75      var itemPlacement = Items.Select(x => new { Item = x.Value, Position = Positions[x.Key] }).ToList();
76      // Find ExtremePoints beginning from sourcepointX
77      var sourcePoint = new Vector3D() { X = position.X + newWidth, Y = position.Y, Z = position.Z };
78      if (sourcePoint.X < BinShape.Width && sourcePoint.Y < BinShape.Height && sourcePoint.Z < BinShape.Depth) {
79        // Projecting onto the XZ-plane
80        var line = new Line3D(sourcePoint, new Vector3D(0, -1, 0));
81        var below = itemPlacement.Select(x => new Plane3D(x.Position, x.Item, Side.Top))
82                                 .Concat(new[] { new Plane3D(BinShape, Side.Bottom) })
83                                 .Select(x => x.Intersect(line))
84                                 .Where(x => x != null && x.Y <= sourcePoint.Y)
85                                 .MaxItems(x => x.Y).First();
86        var residualSpace = CalculateResidualSpace(below);
87        var eps = ExtremePoints.Where(x => IsInside(below, x, ResidualSpace[x]));
88        if (!eps.Any(x => ResidualSpace[x].Item1 >= below.X - x.X + residualSpace.Item1
89          && ResidualSpace[x].Item2 >= below.Y - x.Y + residualSpace.Item2
90          && ResidualSpace[x].Item3 >= below.Z - x.Z + residualSpace.Item3)) {
91          // add only if the projected point's residual space is not a sub-space
92          // of another extreme point's residual space
93          var belowPoint = new PackingPosition(position.AssignedBin, below.X, below.Y, below.Z);
94          AddExtremePoint(belowPoint);
95        }
96        line = new Line3D(sourcePoint, new Vector3D(0, 0, -1));
97        // Projecting onto the XY-plane
98        var back = itemPlacement.Select(x => new Plane3D(x.Position, x.Item, Side.Front))
99                                .Concat(new[] { new Plane3D(BinShape, Side.Back) })
100                                .Select(x => x.Intersect(line))
101                                .Where(x => x != null && x.Z <= sourcePoint.Z)
102                                .MaxItems(x => x.Y).First();
103        residualSpace = CalculateResidualSpace(below);
104        eps = ExtremePoints.Where(x => IsInside(below, x, ResidualSpace[x]));
105        if (!eps.Any(x => ResidualSpace[x].Item1 >= below.X - x.X + residualSpace.Item1
106          && ResidualSpace[x].Item2 >= below.Y - x.Y + residualSpace.Item2
107          && ResidualSpace[x].Item3 >= below.Z - x.Z + residualSpace.Item3)) {
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 line = new Line3D(sourcePoint, new Vector3D(-1, 0, 0));
120        var left = itemPlacement.Select(x => new Plane3D(x.Position, x.Item, Side.Right))
121                                 .Concat(new[] { new Plane3D(BinShape, Side.Left) })
122                                 .Select(x => x.Intersect(line))
123                                 .Where(x => x != null && x.X <= sourcePoint.X)
124                                 .MaxItems(x => x.Y).First();
125        var residualSpace = CalculateResidualSpace(left);
126        var eps = ExtremePoints.Where(x => IsInside(left, x, ResidualSpace[x]));
127        if (!eps.Any(x => ResidualSpace[x].Item1 >= left.X - x.X + residualSpace.Item1
128          && ResidualSpace[x].Item2 >= left.Y - x.Y + residualSpace.Item2
129          && ResidualSpace[x].Item3 >= left.Z - x.Z + residualSpace.Item3)) {
130          // add only if the projected point's residual space is not a sub-space
131          // of another extreme point's residual space
132          var leftPoint = new PackingPosition(position.AssignedBin, left.X, left.Y, left.Z);
133          AddExtremePoint(leftPoint);
134        }
135        line = new Line3D(sourcePoint, new Vector3D(0, 0, -1));
136        // Projecting onto the XY-plane
137        var back = itemPlacement.Select(x => new Plane3D(x.Position, x.Item, Side.Front))
138                                .Concat(new[] { new Plane3D(BinShape, Side.Back) })
139                                .Select(x => x.Intersect(line))
140                                .Where(x => x != null && x.Z <= sourcePoint.Z)
141                                .MaxItems(x => x.Y).First();
142        residualSpace = CalculateResidualSpace(left);
143        eps = ExtremePoints.Where(x => IsInside(left, x, ResidualSpace[x]));
144        if (!eps.Any(x => ResidualSpace[x].Item1 >= left.X - x.X + residualSpace.Item1
145          && ResidualSpace[x].Item2 >= left.Y - x.Y + residualSpace.Item2
146          && ResidualSpace[x].Item3 >= left.Z - x.Z + residualSpace.Item3)) {
147          // add only if the projected point's residual space is not a sub-space
148          // of another extreme point's residual space
149          var backPoint = new PackingPosition(position.AssignedBin, back.X, back.Y, back.Z);
150          AddExtremePoint(backPoint);
151        }
152      }
153
154      //Find ExtremePoints beginning from sourcepointZ
155      sourcePoint = new Vector3D(position.X, position.Y, position.Z + newDepth);
156      if (sourcePoint.X < BinShape.Width && sourcePoint.Y < BinShape.Height && sourcePoint.Z < BinShape.Depth) {
157        // Projecting onto the YZ-plane
158        var line = new Line3D(sourcePoint, new Vector3D(-1, 0, 0));
159        var left = itemPlacement.Select(x => new Plane3D(x.Position, x.Item, Side.Right))
160                                 .Concat(new[] { new Plane3D(BinShape, Side.Left) })
161                                 .Select(x => x.Intersect(line))
162                                 .Where(x => x != null && x.X <= sourcePoint.X)
163                                 .MaxItems(x => x.Y).First();
164        var residualSpace = CalculateResidualSpace(left);
165        var eps = ExtremePoints.Where(x => IsInside(left, x, ResidualSpace[x]));
166        if (!eps.Any(x => ResidualSpace[x].Item1 >= left.X - x.X + residualSpace.Item1
167          && ResidualSpace[x].Item2 >= left.Y - x.Y + residualSpace.Item2
168          && ResidualSpace[x].Item3 >= left.Z - x.Z + residualSpace.Item3)) {
169          // add only if the projected point's residual space is not a sub-space
170          // of another extreme point's residual space
171          var leftPoint = new PackingPosition(position.AssignedBin, left.X, left.Y, left.Z);
172          AddExtremePoint(leftPoint);
173        }
174        // Projecting onto the XZ-plane
175        line = new Line3D(sourcePoint, new Vector3D(0, -1, 0));
176        var below = itemPlacement.Select(x => new Plane3D(x.Position, x.Item, Side.Top))
177                                 .Concat(new[] { new Plane3D(BinShape, Side.Bottom) })
178                                 .Select(x => x.Intersect(line))
179                                 .Where(x => x != null && x.Y <= sourcePoint.Y)
180                                 .MaxItems(x => x.Y).First();
181        residualSpace = CalculateResidualSpace(below);
182        eps = ExtremePoints.Where(x => IsInside(below, x, ResidualSpace[x]));
183        if (!eps.Any(x => ResidualSpace[x].Item1 >= below.X - x.X + residualSpace.Item1
184          && ResidualSpace[x].Item2 >= below.Y - x.Y + residualSpace.Item2
185          && ResidualSpace[x].Item3 >= below.Z - x.Z + residualSpace.Item3)) {
186          // add only if the projected point's residual space is not a sub-space
187          // of another extreme point's residual space
188          var belowPoint = new PackingPosition(position.AssignedBin, below.X, below.Y, below.Z);
189          AddExtremePoint(belowPoint);
190        }
191      }
192    }
193
194    private bool IsBelow(PackingPosition lowerPos, PackingItem lowerItem, Vector3D upper) {
195      return lowerPos.X <= upper.X && lowerPos.X + lowerItem.Width > upper.X
196        && lowerPos.Z <= upper.Z && lowerPos.Z + lowerItem.Depth > upper.Z
197        && lowerPos.Y + lowerItem.Height < upper.Y;
198    }
199
200    private bool IsLeft(PackingPosition leftPos, PackingItem leftItem, Vector3D right) {
201      return leftPos.Y <= right.Y && leftPos.Y + leftItem.Height > right.Y
202        && leftPos.Z <= right.Z && leftPos.Z + leftItem.Depth > right.Z
203        && leftPos.X + leftItem.Width < right.X;
204    }
205
206    private bool IsBack(PackingPosition backPos, PackingItem backItem, Vector3D front) {
207      return backPos.Y <= front.Y && backPos.Y + backItem.Height > front.Y
208        && backPos.X <= front.X && backPos.X + backItem.Width > front.X
209        && backPos.Z + backItem.Depth < front.Z;
210    }
211
212    private bool AddExtremePoint(PackingPosition current) {
213      if (ExtremePoints.Add(current)) {
214        ResidualSpace.Add(current, CalculateResidualSpace(current));
215        return true;
216      }
217      return false;
218    }
219
220    private Tuple<int, int, int> CalculateResidualSpace(Vector3D pos) {
221      return Tuple.Create(BinShape.Width - pos.X, BinShape.Height - pos.Y, BinShape.Depth - pos.Z);
222    }
223
224    private Tuple<int, int, int> CalculateResidualSpace(PackingPosition pos) {
225      return Tuple.Create(BinShape.Width - pos.X, BinShape.Height - pos.Y, BinShape.Depth - pos.Z);
226    }
227
228    private bool IsInside(Vector3D point, PackingPosition pos, PackingItem item) {
229      return point.X >= pos.X && point.X < pos.X + (pos.Rotated ? item.Depth : item.Width)
230        && point.Y >= pos.Y && point.Y < pos.Y + item.Height
231        && point.Z >= pos.Z && point.Z < pos.Z + (pos.Rotated ? item.Width : item.Depth);
232    }
233
234    private bool IsInside(Vector3D point, Vector3D pos, PackingItem item) {
235      return point.X >= pos.X && point.X < pos.X + item.Width
236        && point.Y >= pos.Y && point.Y < pos.Y + item.Height
237        && point.Z >= pos.Z && point.Z < pos.Z + item.Depth;
238    }
239
240    private bool IsInside(Vector3D point, PackingPosition pos, Tuple<int, int, int> rs) {
241      return point.X >= pos.X && point.X < pos.X + rs.Item1
242        && point.Y >= pos.Y && point.Y < pos.Y + rs.Item2
243        && point.Z >= pos.Z && point.Z < pos.Z + rs.Item3;
244    }
245
246    private bool IsInside(Vector3D point, Vector3D pos, Tuple<int, int, int> rs) {
247      return point.X >= pos.X && point.X < pos.X + rs.Item1
248        && point.Y >= pos.Y && point.Y < pos.Y + rs.Item2
249        && point.Z >= pos.Z && point.Z < pos.Z + rs.Item3;
250    }
251
252    public override PackingPosition FindExtremePointForItem(PackingItem item, bool rotated, bool stackingConstraints) {
253      PackingItem newItem = new PackingItem(
254        rotated ? item.Depth : item.Width,
255        item.Height,
256        rotated ? item.Width : item.Depth,
257        item.TargetBin, item.Weight, item.Material);
258
259      var ep = ExtremePoints.Where(x => IsPositionFeasible(newItem, x, stackingConstraints))
260                            .OrderBy(x => x.X + x.Y + x.Z) // order by manhattan distance to try those closest to origin first
261                            .FirstOrDefault();
262      if (ep != null) {
263        var result = new PackingPosition(ep.AssignedBin, ep.X, ep.Y, ep.Z, rotated);
264        return result;
265      }
266      return null;
267    }
268
269    public override bool IsPositionFeasible(PackingItem item, PackingPosition position, bool stackingConstraints) {
270      var feasible = base.IsPositionFeasible(item, position, stackingConstraints);
271      return feasible
272        && IsSupportedByAtLeastOnePoint(item, position)
273        && (!stackingConstraints || (IsStaticStable(item, position) && IsWeightSupported(item, position)));
274    }
275
276    public override PackingPosition FindPositionBySliding(PackingItem item, bool rotated, bool stackingConstraints) {
277      //Starting-position at upper right corner (=left bottom point of item-rectangle is at position item.width,item.height)
278      PackingPosition currentPosition = new PackingPosition(0,
279        BinShape.Width - (rotated ? item.Depth : item.Width),
280        BinShape.Height - item.Height,
281        BinShape.Depth - (rotated ? item.Width : item.Depth), rotated);
282      //Slide the item as far as possible to the bottom
283      while (IsPositionFeasible(item, PackingPosition.MoveDown(currentPosition), stackingConstraints)
284        || IsPositionFeasible(item, PackingPosition.MoveLeft(currentPosition), stackingConstraints)
285        || IsPositionFeasible(item, PackingPosition.MoveBack(currentPosition), stackingConstraints)) {
286        //Slide the item as far as possible to the left
287        while (IsPositionFeasible(item, PackingPosition.MoveLeft(currentPosition), stackingConstraints)
288      || IsPositionFeasible(item, PackingPosition.MoveBack(currentPosition), stackingConstraints)) {
289          //Slide the item as far as possible to the back
290          while (IsPositionFeasible(item, PackingPosition.MoveBack(currentPosition), stackingConstraints)) {
291            currentPosition = PackingPosition.MoveBack(currentPosition);
292          }
293          if (IsPositionFeasible(item, PackingPosition.MoveLeft(currentPosition), stackingConstraints))
294            currentPosition = PackingPosition.MoveLeft(currentPosition);
295        }
296        if (IsPositionFeasible(item, PackingPosition.MoveDown(currentPosition), stackingConstraints))
297          currentPosition = PackingPosition.MoveDown(currentPosition);
298      }
299
300      return IsPositionFeasible(item, currentPosition, stackingConstraints) ? currentPosition : null;
301    }
302
303    public override void SlidingBasedPacking(ref IList<int> sequence, IList<PackingItem> items, bool stackingConstraints) {
304      var temp = new List<int>(sequence);
305      for (int i = 0; i < temp.Count; i++) {
306        var item = items[temp[i]];
307        var position = FindPositionBySliding(item, false, stackingConstraints);
308        if (position != null) {
309          PackItem(temp[i], item, position);
310          sequence.Remove(temp[i]);
311        }
312      }
313    }
314    public override void SlidingBasedPacking(ref IList<int> sequence, IList<PackingItem> items, Dictionary<int, bool> rotationArray, bool stackingConstraints) {
315      var temp = new List<int>(sequence);
316      for (int i = 0; i < temp.Count; i++) {
317        var item = items[temp[i]];
318        var position = FindPositionBySliding(item, rotationArray[i], stackingConstraints);
319        if (position != null) {
320          PackItem(temp[i], item, position);
321          sequence.Remove(temp[i]);
322        }
323      }
324    }
325    public override void ExtremePointBasedPacking(ref IList<int> sequence, IList<PackingItem> items, bool stackingConstraints) {
326      var temp = new List<int>(sequence);
327      foreach (int itemID in temp) {
328        var item = items[itemID];
329        var positionFound = FindExtremePointForItem(item, false, stackingConstraints);
330        if (positionFound != null) {
331          PackItem(itemID, item, positionFound);
332          if (Items.Count > 1)
333            UpdateResidualSpace(item, positionFound);
334          sequence.Remove(itemID);
335        }
336      }
337    }
338    public override void ExtremePointBasedPacking(ref IList<int> sequence, IList<PackingItem> items, bool stackingConstraints, Dictionary<int, bool> rotationArray) {
339      var temp = new List<int>(sequence);
340      foreach (int itemID in temp) {
341        var item = items[itemID];
342        var positionFound = FindExtremePointForItem(item, rotationArray[itemID], stackingConstraints);
343        if (positionFound != null) {
344          PackItem(itemID, item, positionFound);
345          sequence.Remove(itemID);
346        }
347      }
348    }
349    public override int ShortestPossibleSideFromPoint(PackingPosition position) {
350
351      int shortestSide = int.MaxValue;
352      int width = BinShape.Width;
353      int height = BinShape.Height;
354      int depth = BinShape.Depth;
355
356      if (position.X >= width || position.Y >= height || position.Z >= depth)
357        return shortestSide;
358
359      PackingPosition current = new PackingPosition(0, position.X, position.Y, position.Z);
360      while (current.X < width && IsPointOccupied(current)) { current = PackingPosition.MoveRight(current); }
361      if (current.X - position.X < shortestSide)
362        shortestSide = current.X - position.X;
363
364
365      current = new PackingPosition(0, position.X, position.Y, position.Z);
366      while (current.Y < height && IsPointOccupied(current)) { current = PackingPosition.MoveUp(current); }
367      if (current.Y - position.Y < shortestSide)
368        shortestSide = current.Y - position.Y;
369
370
371      current = new PackingPosition(0, position.X, position.Y, position.Z);
372      while (current.Z < depth && IsPointOccupied(current)) { current = PackingPosition.MoveFront(current); }
373      if (current.Z - position.Z < shortestSide)
374        shortestSide = current.Z - position.Z;
375
376      return shortestSide;
377    }
378    public override bool IsStaticStable(PackingItem item, PackingPosition position) {
379      //Static stability is given, if item is placed on the ground
380      if (position.Y == 0)
381        return true;
382
383      if (IsPointOccupied(new PackingPosition(0, position.X, position.Y - 1, position.Z))
384        && IsPointOccupied(new PackingPosition(0, position.X + item.Width - 1, position.Y - 1, position.Z))
385        && IsPointOccupied(new PackingPosition(0, position.X, position.Y - 1, position.Z + item.Depth - 1))
386        && IsPointOccupied(new PackingPosition(0, position.X + item.Width - 1, position.Y - 1, position.Z + item.Depth - 1)))
387        return true;
388
389      return false;
390    }
391
392
393    public bool IsSupportedByAtLeastOnePoint(PackingItem item, PackingPosition position) {
394      if (position.Y == 0)
395        return true;
396
397      int y = position.Y - 1;
398      for (int x = position.X; x < position.X + item.Width; x++)
399        for (int z = position.Z; z < position.Z + item.Depth; z++)
400          if (IsPointOccupied(new PackingPosition(0, x, y, z)))
401            return true;
402
403      return false;
404    }
405    public bool IsWeightSupported(PackingItem item, PackingPosition ep) {
406      if (ep.Y == 0)
407        return true;
408
409      if (Items[PointOccupation(new PackingPosition(0, ep.X, ep.Y - 1, ep.Z))].SupportsStacking(item)
410        && Items[PointOccupation(new PackingPosition(0, ep.X + item.Width - 1, ep.Y - 1, ep.Z))].SupportsStacking(item)
411        && Items[PointOccupation(new PackingPosition(0, ep.X, ep.Y - 1, ep.Z + item.Depth - 1))].SupportsStacking(item)
412        && Items[PointOccupation(new PackingPosition(0, ep.X + item.Width - 1, ep.Y - 1, ep.Z + item.Depth - 1))].SupportsStacking(item))
413        return true;
414
415      return false;
416    }
417
418    protected override void InitializeOccupationLayers() {
419      for (int i = 0; i * 10 <= BinShape.Depth; i += 1) {
420        OccupationLayers[i] = new List<int>();
421      }
422    }
423    protected override void AddNewItemToOccupationLayers(int itemID, PackingItem item, PackingPosition position) {
424      int z1 = position.Z / 10;
425      int z2 = (position.Z + (position.Rotated ? item.Width : item.Depth)) / 10;
426
427      for (int i = z1; i <= z2; i++)
428        OccupationLayers[i].Add(itemID);
429    }
430    protected override List<int> GetLayerItemIDs(PackingPosition position) {
431      return OccupationLayers[position.Z / 10];
432    }
433    protected override List<int> GetLayerItemIDs(PackingItem item, PackingPosition position) {
434      List<int> result = new List<int>();
435      int z1 = position.Z / 10;
436      int z2 = (position.Z + (position.Rotated ? item.Width : item.Depth)) / 10;
437
438      for (int i = z1; i <= z2; i++)
439        result.AddRange(OccupationLayers[i]);
440      return result;
441    }
442   
443    public void UpdateResidualSpace(PackingItem item, PackingPosition pos) {
444      foreach (var ep in ExtremePoints) {
445        if (ep.Z >= pos.Z && ep.Z <= pos.Z + item.Depth) {
446          if (ep.X <= pos.X && ep.Y > pos.Y && ep.Y < pos.Y + item.Height) {
447            var diff = pos.X - ep.X;
448            var newRSX = ResidualSpace[ep].Item1 < diff ? ResidualSpace[ep].Item1 : diff;
449            ResidualSpace[ep] = Tuple.Create(newRSX, ResidualSpace[ep].Item2, ResidualSpace[ep].Item3);
450          }
451          if (ep.Y <= pos.Y && ep.X > pos.X && ep.X < pos.X + item.Width) {
452            var diff = pos.Y - ep.Y;
453            var newRSY = ResidualSpace[ep].Item2 < diff ? ResidualSpace[ep].Item2 : diff;
454            ResidualSpace[ep] = Tuple.Create(ResidualSpace[ep].Item1, newRSY, ResidualSpace[ep].Item3);
455          }
456        }
457        if (ep.Z <= pos.Z &&
458          ep.Y > pos.Y && ep.Y < pos.Y + item.Height &&
459          ep.X > pos.X && ep.X < pos.X + item.Width) {
460            var diff = pos.Z - ep.Z;
461            var newRSZ = ResidualSpace[ep].Item3 < diff ? ResidualSpace[ep].Item3 : diff;
462            ResidualSpace[ep] = Tuple.Create(ResidualSpace[ep].Item1, ResidualSpace[ep].Item2, newRSZ);
463        }
464      }
465      return;
466    }
467
468    private class Vector3D {
469      public int X;
470      public int Y;
471      public int Z;
472      public Vector3D() { }
473      public Vector3D(int x, int y, int z) {
474        X = x;
475        Y = y;
476        Z = z;
477      }
478      public Vector3D(PackingPosition pos) {
479        X = pos.X;
480        Y = pos.Y;
481        Z = pos.Z;
482      }
483      public static Vector3D AlongX(Vector3D pos, PackingItem item) {
484        return new Vector3D(
485          pos.X + item.Width,
486          pos.Y,
487          pos.Z
488        );
489      }
490      public static Vector3D AlongY(Vector3D pos, PackingItem item) {
491        return new Vector3D(
492          pos.X,
493          pos.Y + item.Height,
494          pos.Z
495        );
496      }
497      public static Vector3D AlongZ(Vector3D pos, PackingItem item) {
498        return new Vector3D(
499          pos.X,
500          pos.Y,
501          pos.Z + item.Depth
502        );
503      }
504      public static Vector3D AlongX(PackingPosition pos, PackingItem item) {
505        return new Vector3D(
506          pos.X + item.Width,
507          pos.Y,
508          pos.Z
509        );
510      }
511      public static Vector3D AlongY(PackingPosition pos, PackingItem item) {
512        return new Vector3D(
513          pos.X,
514          pos.Y + item.Height,
515          pos.Z
516        );
517      }
518      public static Vector3D AlongZ(PackingPosition pos, PackingItem item) {
519        return new Vector3D(
520          pos.X,
521          pos.Y,
522          pos.Z + item.Depth
523        );
524      }
525
526      public Vector3D Cross(Vector3D b) {
527        return new Vector3D(
528          Y * b.Z - Z * b.Y,
529          -X * b.Z + Z * b.X,
530          X * b.Y - Y * b.X
531        );
532      }
533      public static int operator *(Vector3D a, Vector3D b) {
534        return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
535      }
536      public static Vector3D operator *(int a, Vector3D b) {
537        return new Vector3D(a * b.X, a * b.Y, a * b.Z);
538      }
539      public static Vector3D operator *(Vector3D a, int b) {
540        return new Vector3D(a.X * b, a.Y * b, a.Z * b);
541      }
542      public static Vector3D operator +(Vector3D a, Vector3D b) {
543        return new Vector3D(a.X + b.X, a.Y + b.Y, a.Z + b.Z);
544      }
545      public static Vector3D operator -(Vector3D a, Vector3D b) {
546        return new Vector3D(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
547      }
548    }
549
550    // A line is given as a point and a directing vector
551    private class Line3D {
552      public Vector3D Point;
553      public Vector3D Direction;
554
555      public Line3D(Vector3D point, Vector3D direction) {
556        Point = point;
557        Direction = direction;
558      }
559
560      public bool Intersects(Plane3D plane) {
561        return plane.Intersects(this);
562      }
563
564      public Vector3D Intersect(Plane3D plane) {
565        return plane.Intersect(this);
566      }
567    }
568
569    private enum Side { Top, Left, Bottom, Right, Front, Back }
570    // A plane is given as a point and two directing vectors
571    private class Plane3D {
572      public Vector3D Point { get; private set; }
573      public Vector3D Direction1 { get; private set; }
574      public Vector3D Direction2 { get; private set; }
575      public Vector3D Normal { get; private set; }
576      private int rhs;
577
578      public Plane3D(PackingShape bin, Side side) {
579        switch (side) {
580          case Side.Top: // ZX plane
581            Point = new Vector3D(0, bin.Height, 0);
582            Direction1 = new Vector3D(0, 0, bin.Depth);
583            Direction2 = new Vector3D(bin.Width, 0, 0);
584            break;
585          case Side.Left: // ZY plane
586            Point = new Vector3D(0, 0, 0);
587            Direction1 = new Vector3D(0, 0, bin.Depth);
588            Direction2 = new Vector3D(0, bin.Height, 0);
589            break;
590          case Side.Bottom: // XZ plane
591            Point = new Vector3D(0, 0, 0);
592            Direction1 = new Vector3D(bin.Width, 0, 0);
593            Direction2 = new Vector3D(0, 0, bin.Depth);
594            break;
595          case Side.Right: // YZ plane
596            Point = new Vector3D(bin.Width, 0, 0);
597            Direction1 = new Vector3D(0, bin.Height, 0);
598            Direction2 = new Vector3D(0, 0, bin.Depth);
599            break;
600          case Side.Front: // XY plane
601            Point = new Vector3D(0, 0, bin.Depth);
602            Direction1 = new Vector3D(bin.Width, 0, 0);
603            Direction2 = new Vector3D(0, bin.Height, 0);
604            break;
605          case Side.Back: // YX plane
606            Point = new Vector3D(0, 0, 0);
607            Direction1 = new Vector3D(0, bin.Height, 0);
608            Direction2 = new Vector3D(bin.Width, 0, 0);
609            break;
610        }
611        Normal = Direction1.Cross(Direction2);
612        rhs = 0;
613      }
614
615      public Plane3D(PackingPosition pos, PackingItem item, Side side) {
616        // the directing vectors are chosen such that Normal always points outside the packing item
617        switch (side) {
618          case Side.Top: // ZX plane
619            Point = new Vector3D(pos.X, pos.Y + item.Height, pos.Z);
620            Direction1 = new Vector3D(0, 0, pos.Rotated ? item.Width : item.Depth);
621            Direction2 = new Vector3D(pos.Rotated ? item.Depth : item.Width, 0, 0);
622            break;
623          case Side.Left: // ZY plane
624            Point = new Vector3D(pos.X, pos.Y, pos.Z);
625            Direction1 = new Vector3D(0, 0, pos.Rotated ? item.Width : item.Depth);
626            Direction2 = new Vector3D(0, item.Height, 0);
627            break;
628          case Side.Bottom: // XZ plane
629            Point = new Vector3D(pos.X, pos.Y, pos.Z);
630            Direction1 = new Vector3D(pos.Rotated ? item.Depth : item.Width, 0, 0);
631            Direction2 = new Vector3D(0, 0, pos.Rotated ? item.Width : item.Depth);
632            break;
633          case Side.Right: // YZ plane
634            Point = new Vector3D(pos.X + (pos.Rotated ? item.Depth : item.Width), pos.Y, pos.Z);
635            Direction1 = new Vector3D(0, item.Height, 0);
636            Direction2 = new Vector3D(0, 0, pos.Rotated ? item.Width : item.Depth);
637            break;
638          case Side.Front: // XY plane
639            Point = new Vector3D(pos.X, pos.Y, pos.Z + (pos.Rotated ? item.Width : item.Depth));
640            Direction1 = new Vector3D(pos.Rotated ? item.Depth : item.Width, 0, 0);
641            Direction2 = new Vector3D(0, item.Height, 0);
642            break;
643          case Side.Back: // YX plane
644            Point = new Vector3D(pos.X, pos.Y, pos.Z);
645            Direction1 = new Vector3D(0, item.Height, 0);
646            Direction2 = new Vector3D(pos.Rotated ? item.Depth : item.Width, 0, 0);
647            break;
648        }
649        Normal = Direction1.Cross(Direction2);
650        rhs = Normal.X * Point.X + Normal.Y * Point.Y + Normal.Z * Point.Z;
651      }
652
653      public bool IsElementOf(Vector3D vector) {
654        return Normal.X * vector.X + Normal.Y * vector.Y + Normal.Z * vector.Z == rhs;
655      }
656
657      public bool Intersects(Line3D line) {
658        return Intersect(line) != null;
659      }
660
661      public Vector3D Intersect(Line3D line) {
662        var denom = Normal * line.Direction;
663        if (denom == 0) {
664          // dir is perpendicular to the normal vector of the plane
665          // this means they intersect if p1 is element of the plane
666          // also the plane does not stretch infinitely, but is bound
667          // to the parallelogram spanned by the point and the two
668          // directing vectors
669          if (IsElementOf(line.Point) && IsWithinDirectionalVectors(line.Point))
670            return line.Point;
671          else return null;
672        }
673        var intersect = line.Point + ((Normal * (Point - line.Point)) / denom) * line.Direction;
674        if (IsWithinDirectionalVectors(intersect)) return intersect;
675        return null;
676      }
677
678      public bool IsWithinDirectionalVectors(Vector3D point) {
679        return point.X >= Point.X && (point.X <= Point.X + Direction1.X || point.X <= Point.X + Direction2.X)
680            && point.Y >= Point.Y && (point.Y <= Point.Y + Direction1.Y || point.Y <= Point.Y + Direction2.Y)
681            && point.Z >= Point.Z && (point.Z <= Point.Z + Direction1.Z || point.Z <= Point.Z + Direction2.Z);
682      }
683    }
684  }
685}
Note: See TracBrowser for help on using the repository browser.