Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2817:

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