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