Beispiel #1
0
        /// <summary>
        /// Pack the list of boxes into a pallet. In other words put small boxes into a big one.
        /// Function packs pallet in layers and tries all pallet rotations and all possible
        /// starting layer sizes.
        /// Returned pallet could be null if the list of boxes was empty or packed pallet that may
        /// contains all or only some boxes.
        /// Packing is done in the loop each time incrementing IterationCount field.
        /// Packing stops when either all boxes fit in the pallet, packing times out or the number of packing iterations
        /// is above the specified limit.
        /// </summary>
        /// <param name="boxList">The list of boxes to pack.</param>
        /// <param name="palletSize">Pallet dimensions.</param>
        /// <returns>Null if box list is empty or packed pallet</returns>
        public PackedPallet Pack(List <Box> boxList, Point3D palletSize)
        {
            if (boxList == null || boxList.Count == 0)
            {
                return(null);
            }
            IterationCount = 0;
            // Start a timer if needed for timeout
            if (TimeoutMilliseconds.HasValue)
            {
                stopwatch = Stopwatch.StartNew();
            }
            else
            {
                stopwatch = null;
            }

            PackedPallet bestPackedPallet = null;

            // Try packing pallet in all orientations
            foreach (var rotatedPallet in palletSize.AllRotations)
            {
                layers = CreateLayers(boxList, rotatedPallet);
                // Try packing pallet starting with different layer
                foreach (var layer in layers)
                {
                    if (ShouldCancelPacking())
                    {
                        break;
                    }
                    ++IterationCount;
                    var packedPallet = PackPallet(layer, boxList, rotatedPallet);
                    if (ShouldCancelPacking())
                    {
                        break;
                    }
                    if (bestPackedPallet == null ||
                        packedPallet.PackedVolume > bestPackedPallet.PackedVolume)
                    {
                        bestPackedPallet = packedPallet;
                    }
                    if (bestPackedPallet.AllBoxesPacked)
                    {
                        return(bestPackedPallet);
                    }
                }

                // If pallet is a cube then rotating it makes no sense.
                // Just exit after the first packing attempt
                if (palletSize.IsCube)
                {
                    break;
                }
            }
            return(bestPackedPallet);
        }
Beispiel #2
0
        /// <summary>
        /// Pack boxes into a pallet starting with specified layer thickness.
        /// The minor "optimization" here is sub-layer packing that is performed
        /// if not enough boxes are available in the layer and layer has to grow to
        /// accomodate bigger available boxes
        /// </summary>
        /// <param name="startLayer">The layer to start with</param>
        /// <param name="boxList">All boxes to pack</param>
        /// <param name="palletDimensions">Dimensions of the pallet</param>
        /// <returns>Packed pallet object</returns>
        PackedPallet PackPallet(Layer startLayer, List <Box> boxList, Point3D palletDimensions)
        {
            var pallet = new PackedPallet()
            {
                PalletDimensions = palletDimensions,
                NotPackedBoxes   = boxList.Select(box => box.Clone()).ToList(),
                PackedBoxes      = new List <Box>()
            };

            // Start building layers at Y=0
            var layerBottomY   = 0L;
            var layerThickness = startLayer.LayerThickness;

            do
            {
                subLayerThickness = 0;
                var oldLayerThickness = layerThickness;
                layerThickness = PackLayer(pallet, layerBottomY, layerThickness, palletDimensions.Y - layerBottomY, palletDimensions.Z);
                if (pallet.AllBoxesPacked)
                {
                    break;
                }
                if (subLayerThickness != 0 && !ShouldCancelPacking())
                {
                    // Pack the special case where layer starts at oldLayerThickness but later
                    // becomes thicker to accomodate taller boxes like this:
                    //             [XX]
                    // [X] [X] [X] [XX]
                    // This call would pack the part of the layer marked by *
                    // ************[XX]
                    // [X] [X] [X] [XX]
                    PackLayer(pallet, layerBottomY + oldLayerThickness,
                              subLayerThickness,
                              layerThickness - oldLayerThickness,
                              sublayerZlimit);
                    if (pallet.AllBoxesPacked)
                    {
                        break;
                    }
                }
                // This layer was packed. Raise the level by packed layer thickness
                layerBottomY += layerThickness;
                // Find the next layer to pack
                layerThickness = FindLayer(pallet.NotPackedBoxes, palletDimensions.SubtractY(layerBottomY));
                if (layerThickness == 0)
                {
                    break;
                }
            } while (!ShouldCancelPacking());
            return(pallet);
        }
Beispiel #3
0
        /// <summary>
        /// Pack one layer. If during packing some boxes become taller than layerThickness then
        /// increase the layer thickness to accomodate them.
        /// </summary>
        /// <param name="pallet">Pallet that is being packed</param>
        /// <param name="layerBottomY">The bottom Y coordinate of this layer</param>
        /// <param name="layerThickness">The thickness (Y) of this layer</param>
        /// <param name="maxThickness">The max thickness possible for this layer in the pallet</param>
        /// <param name="layerZlimit">The Z limit for this layer. Usually the pallet size but in case of
        /// sub-layer it can be less</param>
        /// <returns></returns>
        long PackLayer(PackedPallet pallet, long layerBottomY, long layerThickness, long maxThickness, long layerZlimit)
        {
            if (layerThickness == 0)
            {
                return(layerThickness);
            }
            // Create 2d packing line of the layer.
            // It grows in a horizontal plane with X being width and Z height of this line.
            var packLine = new PackLine(pallet.PalletDimensions.X);

            while (!ShouldCancelPacking())
            {
                var valley = packLine.FindValley();

                // Calculate ideal space and maximum space
                var topZ = valley.NoSegmentsOnSides ? layerZlimit :
                           (valley.NoLeftSegment ? valley.Right.Z : valley.Left.Z);
                var idealBox = new Point3D()
                {
                    X = valley.Width, Y = layerThickness, Z = topZ - valley.Z
                };
                var maximumBox = idealBox.WithYZ(maxThickness, layerZlimit - valley.Z);

                // Find the box that fits in the remaining space of the pallet
                var selected = FindBox(pallet.NotPackedBoxes, idealBox, maximumBox);
                if (selected == null || !selected.FitsInLayer)
                {
                    if (selected != null &&
                        (subLayerThickness != 0 || valley.NoSegmentsOnSides))
                    {
                        // We have a box but it is too thick for the current layer
                        if (subLayerThickness == 0)
                        {
                            // sub-layer will be from 0 to valleyZ
                            sublayerZlimit = valley.Z;
                        }
                        // sub-layer thickness
                        subLayerThickness = subLayerThickness + selected.PackedDimensions.Y - layerThickness;
                        // Now thicken the current layer since we have no more small boxes
                        layerThickness = selected.PackedDimensions.Y;
                    }
                    else if (valley.NoSegmentsOnSides)
                    {
                        // No more boxes for this layer
                        break;
                    }
                    else
                    {
                        valley.FillValley();
                        continue;
                    }
                }

                // Record box location in the pallet and pack the box
                selected.Box.PackingData = new PackingData()
                {
                    PackedDimensions = selected.PackedDimensions,
                    PackedLocation   = new Point3D()
                    {
                        X = valley.LeftX, Y = layerBottomY, Z = valley.Z
                    }
                };
                pallet.PackBox(selected.Box);

                // Add the box dimensions to the pack line
                packLine.AddZ(valley, selected.PackedDimensions);

                if (pallet.AllBoxesPacked)
                {
                    break;
                }
            }
            return(layerThickness);
        }