@@ -22,60 +22,67 @@ public BinPacker(BinPackerVerifyOption verifyOption, params BinPackAlgorithmFact
2222 public BinPackResult Pack ( BinPackParameter parameter )
2323 {
2424 // [ [ cuboid in bin a, cuboid in bin a, ... ], [ cuboid in bin b, ... ] ]
25- IList < IList < Cuboid > > bestResult = null ;
26- var allResults = new List < IList < IList < Cuboid > > > ( ) ;
27- string bestAlgorithmName = null ;
28- foreach ( var factory in _factories )
25+ var bestResult = new List < IList < Cuboid > > ( ) ;
26+ IList < Cuboid > pendingCuboids = parameter . Cuboids . ToList ( ) ;
27+ while ( pendingCuboids . Count > 0 )
2928 {
30- foreach ( var cuboids in GetCuboidsPermutations ( parameter . Cuboids ) )
29+ // pack a single bin
30+ // find the best volume rate from the combination of algorithms and permutations
31+ IList < Cuboid > singleBestResult = null ;
32+ IList < Cuboid > singleBestRemain = null ;
33+ decimal singleBestVolumeRate = 0 ;
34+ string singleBestAlgorihm = null ;
35+ foreach ( var factory in _factories )
3136 {
32- // reset cuboids state
33- var unpackedCuboids = cuboids . Select ( c => c . CloneWithoutPlaceInformation ( ) ) . ToList ( ) ;
34- var result = new List < IList < Cuboid > > ( ) ;
35- var algorithmName = "" ;
36- while ( unpackedCuboids . Count > 0 )
37+ foreach ( var cuboids in GetCuboidsPermutations ( pendingCuboids , parameter . ShuffleCount ) )
3738 {
38- // pack single bin
39+ var targetCuboids = cuboids . Select ( c => c . CloneWithoutPlaceInformation ( ) ) . ToList ( ) ;
3940 var algorithm = factory ( parameter ) ;
40- algorithmName = algorithm . ToString ( ) ;
41- algorithm . Insert ( unpackedCuboids ) ;
42- // find out which cuboids are placed
43- var packedCuboids = unpackedCuboids . Where ( c => c . IsPlaced ) . ToList ( ) ;
41+ var algorithmName = algorithm . ToString ( ) ;
42+ algorithm . Insert ( targetCuboids ) ;
43+ var packedCuboids = targetCuboids . Where ( c => c . IsPlaced ) . ToList ( ) ;
4444 if ( packedCuboids . Count == 0 )
45+ {
4546 break ;
46- result . Add ( packedCuboids ) ;
47- // pack remain cuboids
48- unpackedCuboids = unpackedCuboids . Where ( c => ! c . IsPlaced ) . ToList ( ) ;
49- }
50- // verify this result
51- if ( _verifyOption == BinPackerVerifyOption . All )
52- Verify ( parameter , algorithmName , result ) ;
53- // add to all results
54- allResults . Add ( result ) ;
55- // update best result if all cuboids is placed and uses less bins
56- if ( unpackedCuboids . Count == 0 &&
57- ( bestResult == null || result . Count < bestResult . Count ) )
58- {
59- bestResult = result ;
60- bestAlgorithmName = algorithmName ;
47+ }
48+ // verify this result
49+ if ( _verifyOption == BinPackerVerifyOption . All )
50+ {
51+ Verify ( parameter , algorithmName , packedCuboids ) ;
52+ }
53+ // compare with the best result
54+ var volumeRate = GetVolumeRate ( parameter , packedCuboids ) ;
55+ if ( singleBestResult == null || volumeRate > singleBestVolumeRate )
56+ {
57+ // update the best result
58+ singleBestResult = packedCuboids ;
59+ singleBestRemain = targetCuboids . Where ( c => ! c . IsPlaced ) . ToList ( ) ;
60+ singleBestVolumeRate = volumeRate ;
61+ singleBestAlgorihm = algorithmName ;
62+ }
6163 }
6264 }
65+ if ( singleBestResult == null )
66+ {
67+ throw new InvalidOperationException (
68+ "no algorithm can pack these cuboids\n " +
69+ $ "binWidth: { parameter . BinWidth } , binHeight: { parameter . BinHeight } , " +
70+ $ "binDepth: { parameter . BinDepth } , binWeight: { parameter . BinWeight } \n " +
71+ $ "cuboids: { string . Join ( "\n " , parameter . Cuboids . Select ( x => x . ToString ( ) ) ) } ") ;
72+ }
73+ // verify the best result
74+ if ( _verifyOption == BinPackerVerifyOption . BestOnly )
75+ {
76+ Verify ( parameter , singleBestAlgorihm , singleBestResult ) ;
77+ }
78+ // update the best result of multiple bins
79+ bestResult . Add ( singleBestResult ) ;
80+ pendingCuboids = singleBestRemain ;
6381 }
64- if ( bestResult == null )
65- {
66- throw new InvalidOperationException (
67- "no algorithm can pack these cuboids\n " +
68- $ "binWidth: { parameter . BinWidth } , binHeight: { parameter . BinHeight } , " +
69- $ "binDepth: { parameter . BinDepth } , binWeight: { parameter . BinWeight } \n " +
70- $ "cuboids: { string . Join ( "\n " , parameter . Cuboids . Select ( x => x . ToString ( ) ) ) } ") ;
71- }
72- // verify the best result
73- if ( _verifyOption == BinPackerVerifyOption . BestOnly )
74- Verify ( parameter , bestAlgorithmName , bestResult ) ;
75- return new BinPackResult ( bestResult , allResults , bestAlgorithmName ) ;
82+ return new BinPackResult ( bestResult ) ;
7683 }
7784
78- public static void Verify ( BinPackParameter parameter , string algorithmName , IList < IList < Cuboid > > result )
85+ public static void Verify ( BinPackParameter parameter , string algorithmName , IList < Cuboid > cuboids )
7986 {
8087 // o--------o
8188 // /| /|
@@ -88,54 +95,76 @@ public static void Verify(BinPackParameter parameter, string algorithmName, ILis
8895 // t | width d
8996 // | x
9097 // (0, 0, 0)
91- foreach ( var cuboids in result )
98+ for ( int a = 0 ; a < cuboids . Count ; ++ a )
9299 {
93- for ( int a = 0 ; a < cuboids . Count ; ++ a )
100+ // check if cuboid out of bin
101+ var cuboid = cuboids [ a ] ;
102+ if ( cuboid . X < 0 || cuboid . Y < 0 || cuboid . Z < 0 )
94103 {
95- // check if cuboid out of bin
96- var cuboid = cuboids [ a ] ;
97- if ( cuboid . X < 0 || cuboid . Y < 0 || cuboid . Z < 0 )
98- {
99- throw new ArithmeticException (
100- $ "verify cuboid failed: negative position, algorithm: { algorithmName } , cuboid: { cuboid } ") ;
101- }
102- if ( cuboid . X + cuboid . Width > parameter . BinWidth ||
103- cuboid . Y + cuboid . Height > parameter . BinHeight ||
104- cuboid . Z + cuboid . Depth > parameter . BinDepth )
105- {
106- throw new ArithmeticException (
107- $ "verify cuboid failed: out of bin, algorithm: { algorithmName } , cuboid: { cuboid } ") ;
108- }
109- // check if this cuboid intersects others
110- for ( int b = a + 1 ; b < cuboids . Count ; ++ b )
111- {
112- var otherCuboid = cuboids [ b ] ;
113- if ( cuboid . X < otherCuboid . X + otherCuboid . Width &&
114- otherCuboid . X < cuboid . X + cuboid . Width &&
115- cuboid . Y < otherCuboid . Y + otherCuboid . Height &&
116- otherCuboid . Y < cuboid . Y + cuboid . Height &&
117- cuboid . Z < otherCuboid . Z + otherCuboid . Depth &&
118- otherCuboid . Z < cuboid . Z + cuboid . Depth )
119- {
120- throw new ArithmeticException (
121- $ "verify cuboid failed: cuboid intersects others, algorithm: { algorithmName } , cuboid a: { cuboid } , cuboid b: { otherCuboid } ") ;
122- }
123- }
104+ throw new ArithmeticException (
105+ $ "verify cuboid failed: negative position, algorithm: { algorithmName } , cuboid: { cuboid } ") ;
124106 }
125- // check is cuboids overweight
126- if ( cuboids . Sum ( c => c . Weight ) > parameter . BinWeight )
107+ if ( cuboid . X + cuboid . Width > parameter . BinWidth ||
108+ cuboid . Y + cuboid . Height > parameter . BinHeight ||
109+ cuboid . Z + cuboid . Depth > parameter . BinDepth )
127110 {
128111 throw new ArithmeticException (
129- $ "verify cuboid failed: cuboids overweight, algorithm: { algorithmName } ") ;
112+ $ "verify cuboid failed: out of bin, algorithm: { algorithmName } , cuboid: { cuboid } ") ;
113+ }
114+ // check if this cuboid intersects others
115+ for ( int b = a + 1 ; b < cuboids . Count ; ++ b )
116+ {
117+ var otherCuboid = cuboids [ b ] ;
118+ if ( cuboid . X < otherCuboid . X + otherCuboid . Width &&
119+ otherCuboid . X < cuboid . X + cuboid . Width &&
120+ cuboid . Y < otherCuboid . Y + otherCuboid . Height &&
121+ otherCuboid . Y < cuboid . Y + cuboid . Height &&
122+ cuboid . Z < otherCuboid . Z + otherCuboid . Depth &&
123+ otherCuboid . Z < cuboid . Z + cuboid . Depth )
124+ {
125+ throw new ArithmeticException (
126+ $ "verify cuboid failed: cuboid intersects others, algorithm: { algorithmName } , cuboid a: { cuboid } , cuboid b: { otherCuboid } ") ;
127+ }
130128 }
131129 }
130+ // check is cuboids overweight
131+ if ( cuboids . Sum ( c => c . Weight ) > parameter . BinWeight )
132+ {
133+ throw new ArithmeticException (
134+ $ "verify cuboid failed: cuboids overweight, algorithm: { algorithmName } ") ;
135+ }
132136 }
133137
134- public static IEnumerable < IEnumerable < Cuboid > > GetCuboidsPermutations ( IEnumerable < Cuboid > cuboids )
138+ public static decimal GetVolumeRate ( BinPackParameter parameter , IList < Cuboid > result )
139+ {
140+ return result . Sum ( x => x . Width * x . Height * x . Depth ) /
141+ ( parameter . BinWidth * parameter . BinHeight * parameter . BinDepth ) ;
142+ }
143+
144+ public static decimal GetVolumeRate ( BinPackParameter parameter , IList < IList < Cuboid > > result )
145+ {
146+ var volumeRates = result . Select ( x => GetVolumeRate ( parameter , x ) ) . ToList ( ) ;
147+ if ( volumeRates . Count > 1 ) {
148+ // ignore last bin
149+ volumeRates . RemoveAt ( volumeRates . Count - 1 ) ;
150+ }
151+ return volumeRates . Average ( ) ;
152+ }
153+
154+ public static IEnumerable < IEnumerable < Cuboid > > GetCuboidsPermutations (
155+ IEnumerable < Cuboid > cuboids , int shuffleCount )
135156 {
136157 yield return cuboids ;
137158 yield return cuboids . OrderByDescending ( x => Math . Max ( Math . Max ( x . Width , x . Height ) , x . Depth ) ) ;
138159 yield return cuboids . OrderByDescending ( x => x . Width * x . Height * x . Depth ) ;
160+ if ( shuffleCount > 0 )
161+ {
162+ var random = new Random ( ) ;
163+ for ( var x = 0 ; x < shuffleCount ; ++ x )
164+ {
165+ yield return cuboids . OrderBy ( _ => random . Next ( int . MaxValue ) ) ;
166+ }
167+ }
139168 }
140169
141170 public static BinPackAlgorithmFactory [ ] GetDefaultAlgorithmFactories ( )
0 commit comments