/// <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); }