コード例 #1
0
        public void TestTop()
        {
            var box   = Factory.CreateBox("Box", 10, 10, 10, 0, 10, 10, 10, 100);
            var itemA = Factory.CreateItem("Item A", 5, 10, 10, 10, true);
            var itemB = Factory.CreateItem("Item B", 5, 10, 10, 20, true);

            var packedItemA = new PackedItem(itemA, 0, 0, 0, 5, 10, 10);
            var packedItemB = new PackedItem(itemB, 0, 0, 0, 5, 10, 10);

            var packedItemListA = new PackedItemList();

            packedItemListA.Insert(packedItemA);
            var packedBoxA = new PackedBox(box, packedItemListA);

            var packedItemListB = new PackedItemList();

            packedItemListB.Insert(packedItemB);
            var packedBoxB = new PackedBox(box, packedItemListB);

            var pBoxArray = new PackedBox[] { packedBoxA, packedBoxB };

            var packedBoxList = new PackedBoxList();

            packedBoxList.Insert(packedBoxA);
            packedBoxList.Insert(packedBoxB);

            Assert.Equal(packedBoxA, packedBoxList.Top());
        }
コード例 #2
0
        public void TestInsertAndCount()
        {
            var box   = Factory.CreateBox("Box", 10, 10, 10, 0, 10, 10, 10, 100);
            var itemA = Factory.CreateItem("Item A", 5, 10, 10, 10, true);
            var itemB = Factory.CreateItem("Item B", 5, 10, 10, 20, true);

            var packedItemA = new PackedItem(itemA, 0, 0, 0, 5, 10, 10);
            var packedItemB = new PackedItem(itemB, 0, 0, 0, 5, 10, 10);

            var packedItemListA = new PackedItemList();

            packedItemListA.Insert(packedItemA);
            var packedBoxA = new PackedBox(box, packedItemListA);

            var packedItemListB = new PackedItemList();

            packedItemListB.Insert(packedItemB);
            var packedBoxB = new PackedBox(box, packedItemListB);

            var packedBoxList = new PackedBoxList();

            packedBoxList.Insert(packedBoxA);
            packedBoxList.Insert(packedBoxB);

            Assert.Equal(2, packedBoxList.Count);
        }
コード例 #3
0
        /// <summary>
        /// Given a solution set of packed boxes, repack them to achieve optimum weight distribution.
        /// </summary>
        /// <param name="originalBoxes"></param>
        /// <returns></returns>
        public PackedBoxList RedistributeWeight(PackedBoxList originalBoxes)
        {
            var targetWeight = originalBoxes.GetMeanWeight();

            var redistrebutedBoxes = originalBoxes.ToList();

            redistrebutedBoxes.OrderByDescending(box => box.TotalWeight);

            var iterationSuccessful = false;

            do
            {
                iterationSuccessful = false;

                var a = redistrebutedBoxes.Count;
                while (a - 1 >= 0)
                {
                    a -= 1;
                    var boxA = redistrebutedBoxes[a];

                    var b = redistrebutedBoxes.Count;
                    while (b - 1 >= 0)
                    {
                        b -= 1;
                        var boxB = redistrebutedBoxes[b];

                        if (b <= a || boxA.TotalWeight == boxB.TotalWeight)
                        {
                            continue; //no need to evaluate
                        }

                        iterationSuccessful   = EqualiseWeight(ref boxA, ref boxB, targetWeight);
                        redistrebutedBoxes[a] = boxA;
                        redistrebutedBoxes[b] = boxB;

                        if (iterationSuccessful)
                        {
                            //remove any now-empty boxes from the list
                            redistrebutedBoxes = redistrebutedBoxes
                                                 .Where(box => box != null)
                                                 .OrderByDescending(box => box.TotalWeight)
                                                 .ToList();

                            goto LEAVE_LOOPS;
                        }
                    }
                }

                LEAVE_LOOPS :;
            } while (iterationSuccessful);

            //Combine back into a single list
            var packedBoxes = new PackedBoxList();

            packedBoxes.InsertFromArray(redistrebutedBoxes.ToArray());

            return(packedBoxes);
        }
コード例 #4
0
        /// <summary>
        /// Not every attempted repack is actually helpful - sometimes moving an item between two
        /// otherwise identical boxes, or sometimes the box used for the now lighter set of items
        /// actually weighs more when empty causing an increase in total weight.
        /// </summary>
        /// <returns></returns>
        private bool DidRepackActuallyHelp(PackedBox oldBoxA, PackedBox oldBoxB, PackedBox newBoxA, PackedBox newBoxB)
        {
            PackedBoxList oldList = new PackedBoxList();

            oldList.InsertFromArray(new PackedBox[] { oldBoxA, oldBoxB });

            PackedBoxList newList = new PackedBoxList();

            newList.InsertFromArray(new PackedBox[] { newBoxA, newBoxB });

            return(newList.GetWeightVariance() < oldList.GetWeightVariance());
        }
コード例 #5
0
        public PackedBoxList DoVolumePacking()
        {
            var packedBoxes = new PackedBoxList();

            //Keep going until everything packed
            while (_items.Count > 0)
            {
                var packedBoxesIteration = new List <PackedBox>();

                //Loop through boxes starting with smallest, see what happens
                foreach (var box in _boxes)
                {
                    var volumePacker = new VolumePacker(box, _items.Clone());
                    var packedBox    = volumePacker.Pack();

                    if (packedBox.PackedItems.Count != 0)
                    {
                        packedBoxesIteration.Add(packedBox);

                        //Have we found a single box that contains everything?
                        if (packedBox.PackedItems.Count == _items.Count)
                        {
                            break;
                        }
                    }
                }
                // if any items is packed, then any chanses for this in next iteration
                if (packedBoxesIteration.Count == 0)
                {
                    var ex = new ItemTooLargeException();
                    ex.Data.Add("item", _items.Top());
                    throw ex;
                }
                //Find best box of iteration, and remove packed items from unpacked list
                var bestBox = FindBestBoxFromIteration(packedBoxesIteration);

                foreach (var itemToRemove in bestBox.PackedItems)
                {
                    _items.Remove(itemToRemove.Item);
                }

                packedBoxes.Insert(bestBox);
            }

            return(packedBoxes);
        }
コード例 #6
0
        public void TestVolumeUtilisation()
        {
            var box  = Factory.CreateBox("Box", 10, 10, 10, 0, 10, 10, 10, 10);
            var item = Factory.CreateItem("Item", 5, 10, 10, 10, true);

            var packedItem = new PackedItem(item, 0, 0, 0, 5, 10, 10);

            var packedItemList = new PackedItemList();

            packedItemList.Insert(packedItem);
            var packedBox = new PackedBox(box, packedItemList);

            var packedBoxList = new PackedBoxList();

            packedBoxList.Insert(packedBox);

            Assert.Equal(50f, packedBoxList.GetVolumeUtilisation());
        }
コード例 #7
0
        /// <summary>
        /// Given a solution set of packed boxes, repack them to achieve optimum weight distribution
        /// </summary>
        /// <param name="originalPackedBoxList"></param>
        /// <returns></returns>
        public PackedBoxList RedistributeWeight(PackedBoxList originalPackedBoxList)
        {
            var targetWeight = originalPackedBoxList.GetMeanWeight();

            _logger.Log(LogLevel.Debug, "Repacking for weight distribution, weight variance {0}, target weight {1}", originalPackedBoxList.GetWeightVariance(), targetWeight);

            var packedBoxes      = new PackedBoxList();
            var overWeightBoxes  = new List <PackedBox>();
            var underWeightBoxes = new List <PackedBox>();

            var originalPackedBoxes = originalPackedBoxList.ShallowCopy().GetContent().Cast <PackedBox>();

            foreach (var originalPackedBox in originalPackedBoxes)
            {
                var boxWeight = originalPackedBox.GetWeight();

                if (boxWeight > targetWeight)
                {
                    overWeightBoxes.Add(originalPackedBox);
                }
                else if (boxWeight < targetWeight)
                {
                    underWeightBoxes.Add(originalPackedBox);
                }
                else
                {
                    packedBoxes.Insert(originalPackedBox); // Target weight, so we'll keep these
                }
            }


            // Keep moving items from most overweight box to most underweight box
            var tryRepack = false;

            do
            {
                _logger.Log(LogLevel.Debug, "Boxes under/over target: {0}/{1}", underWeightBoxes.Count, overWeightBoxes.Count);

                for (var underWeightBoxIndex = 0; underWeightBoxIndex < underWeightBoxes.Count; underWeightBoxIndex++)
                {
                    var underWeightBox = underWeightBoxes[underWeightBoxIndex];

                    for (var overWeightBoxIndex = 0; overWeightBoxIndex < overWeightBoxes.Count; overWeightBoxIndex++)
                    {
                        var overWeightBox      = overWeightBoxes[overWeightBoxIndex];
                        var overWeightBoxItems = overWeightBox.GetItems().GetContent().Cast <Item>().ToList();

                        foreach (var overWeightBoxItem in overWeightBoxItems)
                        {
                            // If over target weight, just continue as it would hinder rather than help weight distribution
                            var overTargetWeight = (underWeightBox.GetWeight() + overWeightBoxItem.Weight) > targetWeight;
                            if (overTargetWeight)
                            {
                                continue;
                            }

                            var newItemsForLighterBox = underWeightBox.GetItems().ShallowCopy();
                            newItemsForLighterBox.Insert(overWeightBoxItem);

                            // We may need a bigger box
                            var newLighterBoxPacker = new Packer();
                            newLighterBoxPacker.AddBoxes(Boxes);
                            newLighterBoxPacker.AddItems(newItemsForLighterBox);
                            var newLighterBox = newLighterBoxPacker.PackByVolume().ExtractMin();

                            // New item fits!
                            if (newLighterBox.GetItems().GetCount() == newItemsForLighterBox.GetCount())
                            {
                                // Remove from overWeightBoxItems as it is now packed in a different box
                                overWeightBoxItems.Remove(overWeightBoxItem);

                                // We may be able to use a smaller box
                                var newHeavierBoxPacker = new Packer();
                                newHeavierBoxPacker.AddBoxes(Boxes);
                                newHeavierBoxPacker.AddItems(overWeightBoxItems);

                                var newHeavierBoxes = newHeavierBoxPacker.PackByVolume();

                                // Found an edge case in packing algorithm that *increased* box count
                                if (newHeavierBoxes.GetCount() > 1)
                                {
                                    _logger.Log(LogLevel.Info, "[REDISTRIBUTING WEIGHT] Abandoning redistribution, because new packing is less effciient than original");
                                    return(originalPackedBoxList);
                                }

                                // TODO: INDEX BASED ARRAY INSERTION FOR BOTH
                                overWeightBoxes[overWeightBoxIndex]   = newHeavierBoxes.ExtractMin();
                                underWeightBoxes[underWeightBoxIndex] = newLighterBox;

                                // We did some work, so see if we can do even better
                                tryRepack = true;
                                overWeightBoxes.Sort(packedBoxes.ReverseCompareTo);
                                underWeightBoxes.Sort(packedBoxes.ReverseCompareTo);

                                // The devil, but ported from PHP, was originally break 3, but .NET doesn't have a break x command
                                // so we're using a goto statement. It is, however, more readable than break 3.
                                goto MOVINGON;
                            }
                        }
                    }

                    MOVINGON : _logger.Log(LogLevel.Info, "Trying to repack");
                }
            } while (tryRepack);

            packedBoxes.InsertAll(overWeightBoxes);
            packedBoxes.InsertAll(underWeightBoxes);

            return(packedBoxes);
        }
コード例 #8
0
        /// <summary>
        /// Pack items into boxes using the principle of largest volume item first
        /// </summary>
        /// <returns></returns>
        public PackedBoxList PackByVolume()
        {
            var packedBoxes = new PackedBoxList();

            while (Items.GetCount() > 0)
            {
                var boxesToEvaluate      = Boxes.ShallowCopy();
                var boxListToEvaluate    = Boxes.ShallowCopy().GetContent().Cast <Box>().ToList();
                var packedBoxesIteration = new PackedBoxList();

                if (!StartWithLargerBoxes)
                {
                    while (!boxesToEvaluate.IsEmpty())
                    {
                        var box       = boxesToEvaluate.ExtractMin();
                        var packedBox = PackIntoBox(box, Items.ShallowCopy());

                        if (packedBox.GetItems().GetCount() > 0)
                        {
                            packedBoxesIteration.Insert(packedBox);
                            if (packedBox.GetItems().GetCount() == Items.GetCount())
                            {
                                break;
                            }
                        }
                    }
                }
                else
                {
                    boxListToEvaluate.Sort(boxesToEvaluate.ReverseCompareTo);
                    foreach (var box in boxListToEvaluate)
                    {
                        var packedBox = PackIntoBox(box, Items.ShallowCopy());
                        if (packedBox.GetItems().GetCount() > 0)
                        {
                            packedBoxesIteration.Insert(packedBox);
                        }
                    }
                }

                // Check iteration was productive
                if (packedBoxesIteration.IsEmpty() && !CreateBoxesForOversizedItems)
                {
                    var oversizedItem = Items.GetMax();
                    throw new ItemTooLargeException($"Item {oversizedItem.Description} (id: {oversizedItem.Id}) is too large to fit into any box.");
                }

                // 1. Create box, add to boxes list
                // 2. Run through loop to add that product to that box
                if (packedBoxesIteration.IsEmpty() && CreateBoxesForOversizedItems)
                {
                    // TODO: What should the empty box weight be?
                    var oversizedItem = Items.GetMax();
                    var box           = new Box()
                    {
                        Description = String.Format("Custom box for {0}", oversizedItem.Description),
                        GeneratedId = Guid.NewGuid().ToString(),
                        EmptyWeight = 0,
                        InnerDepth  = oversizedItem.Depth,
                        InnerLength = oversizedItem.Length,
                        InnerWidth  = oversizedItem.Width,
                        MaxWeight   = oversizedItem.Weight,
                        OuterDepth  = oversizedItem.Depth,
                        OuterLength = oversizedItem.Length,
                        OuterWidth  = oversizedItem.Width
                    };
                    Boxes.Insert(box);
                    _logger.Log(LogLevel.Debug, "Item {0} (id: {1}) is too large to fit into any box, creating custom box for it.",
                                oversizedItem.Description, oversizedItem.Id);
                }
                else
                {
                    PackedBox bestBox;

                    if (!StartWithLargerBoxes)
                    {
                        bestBox = packedBoxesIteration.GetMin();
                    }
                    else
                    {
                        var packedBoxListToEvaluate = packedBoxesIteration.GetContent().Cast <PackedBox>().ToList();
                        packedBoxListToEvaluate.Sort(packedBoxesIteration.ReverseCompareTo);
                        bestBox = packedBoxListToEvaluate.FirstOrDefault();
                    }

                    var bestBoxItems  = bestBox.GetItems().ShallowCopy();
                    var unpackedItems = Items.GetContent().Cast <Item>().ToList();

                    foreach (var packedItem in bestBoxItems.GetContent())
                    {
                        foreach (var unpackedItem in unpackedItems)
                        {
                            if (packedItem == unpackedItem)
                            {
                                unpackedItems.Remove(unpackedItem);
                                break;
                            }
                        }
                    }

                    var unpackedItemList = new ItemList();
                    foreach (var unpackedItem in unpackedItems)
                    {
                        unpackedItemList.Insert(unpackedItem);
                    }

                    Items = unpackedItemList;
                    packedBoxes.Insert(bestBox);
                }
            }

            return(packedBoxes);
        }