Skip to content

Commit 7b60136

Browse files
committed
use volume rate to determine the best result,
it will increase the volume rate for the beginning bins, but also make the packing slower
1 parent 4356aff commit 7b60136

File tree

5 files changed

+124
-106
lines changed

5 files changed

+124
-106
lines changed

Sharp3DBinPacking.RandomTest/Program.cs

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,25 @@ static class Program
1212
static void Main(string[] args)
1313
{
1414
var binPacker = BinPacker.GetDefault(BinPackerVerifyOption.All);
15-
var bestAlgorithmRecords = new Dictionary<string, int>();
15+
var averageVolumeRate = 0m;
1616
var round = 0;
1717
while (true)
1818
{
19-
var result = TestBinPacker(binPacker);
20-
if (bestAlgorithmRecords.ContainsKey(result.BestAlgorithmName))
21-
bestAlgorithmRecords[result.BestAlgorithmName]++;
22-
else
23-
bestAlgorithmRecords[result.BestAlgorithmName] = 1;
19+
var tuple = TestBinPacker(binPacker);
20+
var result = tuple.Item1;
21+
var volumeRate = tuple.Item2;
22+
averageVolumeRate = (averageVolumeRate * round + volumeRate) / (round + 1);
2423
round++;
2524
var binCount = result.BestResult.Count;
2625
var cuboidCount = result.BestResult.Sum(x => x.Count);
27-
Console.WriteLine($"Round {round} finished, {binCount} bins contains {cuboidCount} cuboids");
28-
Console.WriteLine($"Best algorithm records:");
29-
foreach (var pair in bestAlgorithmRecords.OrderByDescending(x => x.Value))
30-
{
31-
Console.WriteLine($"{pair.Key}: {pair.Value}");
32-
}
33-
Console.WriteLine();
26+
Console.WriteLine(
27+
$"Round {round} finished, {binCount} bins contains {cuboidCount} cuboids, " +
28+
$"average volume rate {averageVolumeRate.ToString("0.0000")}");
3429
Thread.Sleep(1);
3530
}
3631
}
3732

38-
static BinPackResult TestBinPacker(IBinPacker binPacker)
33+
static Tuple<BinPackResult, decimal> TestBinPacker(IBinPacker binPacker)
3934
{
4035
var binWidth = RandomInstance.Next(100, 5001);
4136
var binHeight = RandomInstance.Next(100, 5001);
@@ -54,7 +49,9 @@ static BinPackResult TestBinPacker(IBinPacker binPacker)
5449
}
5550
var parameter = new BinPackParameter(
5651
binWidth, binHeight, binDepth, binWeight, allowRotateVertically, cuboids);
57-
return binPacker.Pack(parameter);
52+
var result = binPacker.Pack(parameter);
53+
var volumeRate = BinPacker.GetVolumeRate(parameter, result.BestResult);
54+
return Tuple.Create(result, volumeRate);
5855
}
5956
}
6057
}

Sharp3DBinPacking/BinPackParameter.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ public class BinPackParameter
1212
public decimal BinWeight { get; private set; }
1313
public bool AllowRotateVertically { get; private set; }
1414
public IEnumerable<Cuboid> Cuboids { get; private set; }
15+
public int ShuffleCount { get; set; }
1516

1617
public BinPackParameter(
1718
decimal binWidth, decimal binHeight, decimal binDepth, IEnumerable<Cuboid> cuboids) :
1819
this(binWidth, binHeight, binDepth, 0, true, cuboids) { }
20+
1921
public BinPackParameter(
2022
decimal binWidth, decimal binHeight, decimal binDepth, decimal binWeight,
2123
bool allowRotateVertically, IEnumerable<Cuboid> cuboids)
@@ -26,6 +28,7 @@ public BinPackParameter(
2628
BinWeight = binWeight;
2729
AllowRotateVertically = allowRotateVertically;
2830
Cuboids = cuboids;
31+
ShuffleCount = 5;
2932
}
3033
}
3134
}

Sharp3DBinPacking/BinPackResult.cs

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,10 @@ namespace Sharp3DBinPacking
77
public class BinPackResult
88
{
99
public IList<IList<Cuboid>> BestResult { get; private set; }
10-
public IList<IList<IList<Cuboid>>> AllResults { get; private set; }
11-
public string BestAlgorithmName { get; private set; }
1210

13-
public BinPackResult(IList<IList<Cuboid>> bestResult, string bestAlgorithmName) :
14-
this(bestResult, new List<IList<IList<Cuboid>>>(), bestAlgorithmName)
15-
{ }
16-
17-
public BinPackResult(
18-
IList<IList<Cuboid>> bestResult,
19-
IList<IList<IList<Cuboid>>> allResults,
20-
string bestAlgorithmName)
11+
public BinPackResult(IList<IList<Cuboid>> bestResult)
2112
{
2213
BestResult = bestResult;
23-
AllResults = allResults;
24-
BestAlgorithmName = bestAlgorithmName;
2514
}
2615
}
2716
}

Sharp3DBinPacking/BinPacker.cs

Lines changed: 107 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -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()

Sharp3DBinPacking/Sharp3DBinPacking.nuspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
33
<metadata>
44
<id>Sharp3DBinPacking</id>
5-
<version>1.2.0</version>
5+
<version>2.0.0</version>
66
<authors>303248153</authors>
77
<owners>303248153</owners>
88
<requireLicenseAcceptance>true</requireLicenseAcceptance>

0 commit comments

Comments
 (0)