public static void Verify(BinPackParameter parameter, string algorithmName, IList <IList <Cuboid> > result)
 {
     //       o--------o
     //      /|       /|
     //     / |      / |
     //  h o--------o  |
     //  e |  o-----|--o h
     //y i | /      | / t
     //  g |/       |/ p z
     //  h o--------o e
     //  t | width   d
     //    |  x
     // (0, 0, 0)
     foreach (var cuboids in result)
     {
         for (int a = 0; a < cuboids.Count; ++a)
         {
             // check if cuboid out of bin
             var cuboid = cuboids[a];
             if (cuboid.X < 0 || cuboid.Y < 0 || cuboid.Z < 0)
             {
                 throw new ArithmeticException(
                           $"verify cuboid failed: negative position, algorithm: {algorithmName}, cuboid: {cuboid}");
             }
             if (cuboid.X + cuboid.Width > parameter.BinWidth ||
                 cuboid.Y + cuboid.Height > parameter.BinHeight ||
                 cuboid.Z + cuboid.Depth > parameter.BinDepth)
             {
                 throw new ArithmeticException(
                           $"verify cuboid failed: out of bin, algorithm: {algorithmName}, cuboid: {cuboid}");
             }
             // check if this cuboid intersects others
             for (int b = a + 1; b < cuboids.Count; ++b)
             {
                 var otherCuboid = cuboids[b];
                 if (cuboid.X < otherCuboid.X + otherCuboid.Width &&
                     otherCuboid.X < cuboid.X + cuboid.Width &&
                     cuboid.Y < otherCuboid.Y + otherCuboid.Height &&
                     otherCuboid.Y < cuboid.Y + cuboid.Height &&
                     cuboid.Z < otherCuboid.Z + otherCuboid.Depth &&
                     otherCuboid.Z < cuboid.Z + cuboid.Depth)
                 {
                     throw new ArithmeticException(
                               $"verify cuboid failed: cuboid intersects others, algorithm: {algorithmName}, cuboid a: {cuboid}, cuboid b: {otherCuboid}");
                 }
             }
         }
         // check is cuboids overweight
         if (cuboids.Sum(c => c.Weight) > parameter.BinWeight)
         {
             throw new ArithmeticException(
                       $"verify cuboid failed: cuboids overweight, algorithm: {algorithmName}");
         }
     }
 }
        public static decimal GetVolumeRate(BinPackParameter parameter, IList <IList <Cuboid> > result)
        {
            var volumeRates = result.Select(x => GetVolumeRate(parameter, x)).ToList();

            if (volumeRates.Count > 1)
            {
                // ignore last bin
                volumeRates.RemoveAt(volumeRates.Count - 1);
            }
            return(volumeRates.Average());
        }
        public BinPackResult Pack(BinPackParameter parameter)
        {
            // [ [ cuboid in bin a, cuboid in bin a, ... ], [ cuboid in bin b, ... ] ]
            IList <IList <Cuboid> > bestResult = null;
            var    allResults        = new List <IList <IList <Cuboid> > >();
            string bestAlgorithmName = null;

            foreach (var factory in _factories)
            {
                foreach (var cuboids in GetCuboidsPermutations(parameter.Cuboids))
                {
                    // reset cuboids state
                    var unpackedCuboids = cuboids.Select(c => c.CloneWithoutPlaceInformation()).ToList();
                    var result          = new List <IList <Cuboid> >();
                    var algorithmName   = "";
                    while (unpackedCuboids.Count > 0)
                    {
                        // pack single bin
                        var algorithm = factory(parameter);
                        algorithmName = algorithm.ToString();
                        algorithm.Insert(unpackedCuboids);
                        // find out which cuboids are placed
                        var packedCuboids = unpackedCuboids.Where(c => c.IsPlaced).ToList();
                        if (packedCuboids.Count == 0)
                        {
                            break;
                        }
                        result.Add(packedCuboids);
                        // pack remain cuboids
                        unpackedCuboids = unpackedCuboids.Where(c => !c.IsPlaced).ToList();
                    }
                    // verify this result
                    if (_verifyOption == BinPackerVerifyOption.All)
                    {
                        Verify(parameter, algorithmName, result);
                    }
                    // add to all results
                    allResults.Add(result);
                    // update best result if all cuboids is placed and uses less bins
                    if (unpackedCuboids.Count == 0 &&
                        (bestResult == null || result.Count < bestResult.Count))
                    {
                        bestResult        = result;
                        bestAlgorithmName = algorithmName;
                    }
                }
            }
            if (bestResult == null)
            {
                throw new InvalidOperationException(
                          "no algorithm can pack these cuboids\n" +
                          $"binWidth: {parameter.BinWidth}, binHeight: {parameter.BinHeight}, " +
                          $"binDepth: {parameter.BinDepth}, binWeight: {parameter.BinWeight}\n" +
                          $"cuboids: {string.Join("\n", parameter.Cuboids.Select(x => x.ToString()))}");
            }
            // verify the best result
            if (_verifyOption == BinPackerVerifyOption.BestOnly)
            {
                Verify(parameter, bestAlgorithmName, bestResult);
            }
            return(new BinPackResult(bestResult, allResults, bestAlgorithmName));
        }
        public BinPackResult Pack(BinPackParameter parameter)
        {
            // [ [ cuboid in bin a, cuboid in bin a, ... ], [ cuboid in bin b, ... ] ]
            var            bestResult     = new List <IList <Cuboid> >();
            IList <Cuboid> pendingCuboids = parameter.Cuboids.ToList();

            while (pendingCuboids.Count > 0)
            {
                // pack a single bin
                // find the best volume rate from the combination of algorithms and permutations
                IList <Cuboid> singleBestResult     = null;
                IList <Cuboid> singleBestRemain     = null;
                decimal        singleBestVolumeRate = 0;
                string         singleBestAlgorihm   = null;
                foreach (var factory in _factories)
                {
                    foreach (var cuboids in GetCuboidsPermutations(pendingCuboids, parameter.ShuffleCount))
                    {
                        var targetCuboids = cuboids.Select(c => c.CloneWithoutPlaceInformation()).ToList();
                        var algorithm     = factory(parameter);
                        var algorithmName = algorithm.ToString();
                        algorithm.Insert(targetCuboids);
                        var packedCuboids = targetCuboids.Where(c => c.IsPlaced).ToList();
                        if (packedCuboids.Count == 0)
                        {
                            break;
                        }
                        // verify this result
                        if (_verifyOption == BinPackerVerifyOption.All)
                        {
                            Verify(parameter, algorithmName, packedCuboids);
                        }
                        // compare with the best result
                        var volumeRate = GetVolumeRate(parameter, packedCuboids);
                        if (singleBestResult == null || volumeRate > singleBestVolumeRate)
                        {
                            // update the best result
                            singleBestResult     = packedCuboids;
                            singleBestRemain     = targetCuboids.Where(c => !c.IsPlaced).ToList();
                            singleBestVolumeRate = volumeRate;
                            singleBestAlgorihm   = algorithmName;
                        }
                    }
                }
                if (singleBestResult == null)
                {
                    throw new InvalidOperationException(
                              "no algorithm can pack these cuboids\n" +
                              $"binWidth: {parameter.BinWidth}, binHeight: {parameter.BinHeight}, " +
                              $"binDepth: {parameter.BinDepth}, binWeight: {parameter.BinWeight}\n" +
                              $"cuboids: {string.Join("\n", parameter.Cuboids.Select(x => x.ToString()))}");
                }
                // verify the best result
                if (_verifyOption == BinPackerVerifyOption.BestOnly)
                {
                    Verify(parameter, singleBestAlgorihm, singleBestResult);
                }
                // update the best result of multiple bins
                bestResult.Add(singleBestResult);
                pendingCuboids = singleBestRemain;
            }
            return(new BinPackResult(bestResult));
        }
 public static decimal GetVolumeRate(BinPackParameter parameter, IList <Cuboid> result)
 {
     return(result.Sum(x => x.Width * x.Height * x.Depth) /
            (parameter.BinWidth * parameter.BinHeight * parameter.BinDepth));
 }