Exemple #1
0
        static public PartList Pack(PartList parts, BoardList boards, double sawkerf = 4, double boardMarginLength = 0, double boardMarginWidth = 0, double partPaddingLength = 0, double partPaddingWidth = 0)
        {
            // order the parts by Area, Ascending
            var orderredParts = parts.OrderredByArea();
            var oderredBoards = boards.OrderredByArea();

            // add padding to parts
            orderredParts.InflateAll(partPaddingWidth, partPaddingLength);

            // init the bag for the solution
            PartList completeSolution = new PartList();

            // repeat until all parts are placed, or boards used up
            int iteration = 0;

            while (orderredParts.Count > 0 && oderredBoards.Count > 0)
            {
                // for this iteration, prepare to hold the best board's packing solution
                PartList bestsolution         = null;
                double   bestsolutioncoverage = 0;

                // we will pack each board in its own thread, so we need to track the threads
                List <Task> threads = new List <Task>();

                // loop through the available board sections
                for (BoardNode iBoard = oderredBoards.Head; iBoard != null; iBoard = iBoard.Next)
                {
                    threads.Add(
                        Task.Factory.StartNew((o) =>
                    {
                        Thread.CurrentThread.Priority = ThreadPriority.Highest;
                        // for every board
                        BoardNode tiBoard = new BoardNode((BoardNode)o);
                        // subtract the margin from the board
                        tiBoard.Inflate(-boardMarginWidth, -boardMarginLength);

                        // init a packer object
                        Packer iPacker = new Packer()
                        {
                            boardArea = tiBoard.Area,
                            sawkerf   = sawkerf
                        };

                        // pack the board recursively, starting at the first part and an empty solution
                        iPacker.Pack_recursive(new PartList(orderredParts), new BoardList(tiBoard), new PartList(), 0);

                        //Trace.WriteLine($"......in iteration {iteration+1}: Board {iPacker.currentSolution.Head.Container} packed to {iPacker.currentSolutionArea/iPacker.boardArea:0 %} :\r\n{iPacker.currentSolution.ToString()}");

                        // replace the best solution if this one is better
                        lock (lck)
                            if (iPacker.currentSolutionArea / iPacker.boardArea > bestsolutioncoverage)
                            {
                                bestsolutioncoverage = iPacker.currentSolutionArea / iPacker.boardArea;
                                bestsolution         = iPacker.currentSolution;
                            }
                    }, iBoard));
                }
                Task.WaitAll(threads.ToArray());

                // if no board could be packed, stop
                if (bestsolutioncoverage == 0)
                {
                    Trace.WriteLine("STOPPING: Non of the parts left to place would fit any of the available boards...");
                    break;
                }

                boards[bestsolution.Head.Container].Solution = new PartList(bestsolution);
                // report the best packking for this iteration
                Trace.WriteLine($"Best solution for iteration {++iteration}: Board {bestsolution.Head.Container} packed to {bestsolutioncoverage:0 %} :\r\n{bestsolution.ToString()}");

                // remove best packed board from the list of available boards
                oderredBoards.Remove(bestsolution.Head.Container);

                // remove the parts packed from the list of required parts
                for (PartNode iPart = bestsolution.Head; iPart != null; iPart = iPart.Next)
                {
                    orderredParts.Remove(iPart.ID);
                }

                // add this partial solution to the complete solution...
                completeSolution.Append(bestsolution);
            }

            // return the solution
            return(completeSolution);
        }
Exemple #2
0
        private void Pack_recursive(PartList parts, BoardList boards, PartList TemporarySolution, double tempSolutionArea)
        {
            // loop through remaining parts
            for (PartNode iPart = parts.Tail; iPart != null; iPart = iPart.Prev)
            {
                #region // check if the part is a viable candidate ...
                // if this part has bigger area than the largest board segment available, go to next part
                if (iPart.Area > boards.Tail.Area)
                {
                    continue;
                }
                // if the previous part was the same size, pass this one - we already completed this iteration
                if (iPart != parts.Tail && iPart.Length == iPart.Next.Length && iPart.Width == iPart.Next.Width)
                {
                    continue;
                }
                #endregion

                #region // Find first board that will fit the part ...
                // find first board that will accomodate the part
                BoardNode iBoard = boards.Head;

                // if even the last (bigest) board had a smaller area than the part, non of the rest will fit
                while (iPart.Area > iBoard.Area)
                {
                    iBoard = iBoard.Next;
                }
                while (iBoard != null && (iPart.Length > iBoard.Length || iPart.Width > iBoard.Width))
                {
                    iBoard = iBoard.Next;
                }
                if (iBoard == null)
                {
                    continue; // if this part cannot fit any board, continue to next part
                }
                #endregion

                #region // place the part ...
                double newPackedPartsArea = tempSolutionArea + iPart.Area;
                //append the part to the list of packed parts
                TemporarySolution.Append(new PartNode(iPart)
                {
                    Container = iBoard.ID,
                    dWidth    = iBoard.dWidth,
                    dLength   = iBoard.dLength
                });
                #endregion

                #region // store best solution ...
                //if this is a better solution than the current best one ... replace the current best one
                if (newPackedPartsArea > currentSolutionArea)
                {
                    currentSolutionArea = newPackedPartsArea;
                    currentSolution     = new PartList(TemporarySolution);
                }
                #endregion

                // if there are no more parts, break out of the loop
                if (parts.Count == 1)
                {
                    break;
                }

                #region // Break association and adjust associate if a board is used that is associated with another to prevent overlapping placements ...
                BoardNode iAssocBoard = iBoard.AssociatedBoard;
                double    oAssocLength = 0, oAssocWidth = 0;
                // if the board section used has a buddy from a previous placement, adjust the buddy and break the association
                if (iAssocBoard != null)
                {
                    oAssocLength = iAssocBoard.Length;
                    oAssocWidth  = iAssocBoard.Width;;

                    //we have to adjust the buddy, so as not to place another part on the overlapping area
                    if (iBoard.dWidth < iAssocBoard.dWidth)
                    {
                        //if this is Rem1
                        //if the part is wider than the left portion of Rem1
                        if (iBoard.dWidth + iPart.Width + sawkerf > iAssocBoard.dWidth)
                        {
                            iAssocBoard.Length -= (iBoard.Length + sawkerf);
                        }
                        else
                        {
                            iBoard.Width -= (iAssocBoard.Width + sawkerf);
                        }
                    }
                    else
                    {
                        //if this is Rem2
                        if (iBoard.dLength + iPart.Length + sawkerf > iAssocBoard.dLength)
                        {
                            iAssocBoard.Width -= (iBoard.Width + sawkerf);
                        }
                        else
                        {
                            iBoard.Length -= (iAssocBoard.Length + sawkerf);
                        }
                    }

                    //then break the pair
                    iAssocBoard.AssociatedBoard = null;
                    iBoard.AssociatedBoard      = null;
                }
                #endregion

                #region // remove the current part from the parts list ...
                parts.Remove(iPart);
                #endregion

                #region // replace the used board with 2 overlapping remainder pieces after subtracting the part ...
                // divide the board into two overlapping remainder sections
                boards.Remove(iBoard);
                BoardNode boardSection1 = null;
                double    l             = iBoard.Length - iPart.Length - sawkerf;
                if (l * iBoard.Width >= parts.Head.Area)
                {
                    boardSection1 = new BoardNode(iBoard.ID, l, iBoard.Width, iBoard.dLength + iPart.Length + sawkerf, iBoard.dWidth);
                    boards.InsertItemSortedbyAreaAsc(boardSection1);
                }
                BoardNode boardSection2 = null;
                double    w             = iBoard.Width - iPart.Width - sawkerf;
                if (w * iBoard.Length >= parts.Head.Area)
                {
                    boardSection2 = new BoardNode(iBoard.ID, iBoard.Length, w, iBoard.dLength, iBoard.dWidth + iPart.Width + sawkerf);
                    boards.InsertItemSortedbyAreaAsc(boardSection2);
                    boardSection2.AssociatedBoard = boardSection1;
                    if (boardSection1 != null)
                    {
                        boardSection1.AssociatedBoard = boardSection2;
                    }
                }
                #endregion

                if (boards.Count > 0)
                {
                    #region // pack the remaining parts on the remaining boards ...
                    // pack the remaining parts on the remaining boards
                    Pack_recursive(parts, boards, TemporarySolution, newPackedPartsArea);
                    #endregion
                }

                #region // undo the placement so we can iterate to the next part and test with it ...
                // place the current part back in its exact place ...
                parts.Return(iPart);

                // remove the remainder board sections we added...
                if (boardSection1 != null)
                {
                    boards.Remove(boardSection1);
                }
                if (boardSection2 != null)
                {
                    boards.Remove(boardSection2);
                }

                // restore associations, and the original associated board's size
                if (iAssocBoard != null)
                {
                    iBoard.AssociatedBoard      = iAssocBoard;
                    iAssocBoard.AssociatedBoard = iBoard;
                    iAssocBoard.Length          = oAssocLength;
                    iAssocBoard.Width           = oAssocWidth;
                }

                // place the board back
                if (iBoard.Prev == null)
                {
                    boards.Head = iBoard;
                }
                else
                {
                    iBoard.Prev.Next = iBoard;
                }
                if (iBoard.Next == null)
                {
                    boards.Tail = iBoard;
                }
                else
                {
                    iBoard.Next.Prev = iBoard;
                }
                boards.Count++;

                // remove the part from the temporary solution
                TemporarySolution.Tail = TemporarySolution.Tail.Prev;
                if (TemporarySolution.Tail != null)
                {
                    TemporarySolution.Tail.Next = null;
                }
                else
                {
                    TemporarySolution.Head = null;
                }
                TemporarySolution.Count--;

                #endregion
            }
        }