private static ComboList GetCombos(PartList parts, Item board, double cumilativeArea = 0, double minimumArea = 0) { PartList mycopyofParts = parts.Copy(); // create a copy of the list, because we will be changing the list, but not the contents ComboList myCombos = new ComboList(); // collection of all the combinations we could construct // loop though all the parts ... foreach (var ipart in parts) { if (!board.BiggerThan(ipart)) // rule out parts that are too long/wide { mycopyofParts.Remove(ipart); continue; } double ncumArea = ipart.Area + cumilativeArea; if (ncumArea > board.Area) // dont add parts that would push the cumilative area of the combination over that of the board { continue; } if (ncumArea > minimumArea) { myCombos.Add(new Combo(ipart)); } mycopyofParts.Remove(ipart); if (mycopyofParts.Count == 0) { continue; // no need to try call myself again if there is no parts left } ComboList sublist = GetCombos(mycopyofParts, board, ncumArea, minimumArea); foreach (var icombo in sublist) { double cumsubArea = icombo.CumalativeArea + ncumArea; icombo.Add(ipart); if (icombo.Count > 1 && icombo[0].Width + icombo[1].Width > board.Width && icombo[0].Length + icombo[1].Length > board.Length) { continue; } myCombos.Add(icombo); } } return(myCombos); }
public async static Task <Solution> Packold(PartList parts, BoardList boards) { #region // Algorithm : ... /* * for each board * remove all parts that will not fit on board * do * Get the combos of parts that have cumalative area smaller than board, but bigger than 90% of board (we aim high...) * level=level*level * while combos.count = 0 * sort combos by cum area desc * foreach combo * if can fit on board, break * * end for each board * keep board with leaste waste * * repeat for boards and parts left * */ #endregion if (parts.Count * boards.Count == 0) { return(null); } if (!parts.All(t => boards.Any(q => q.BiggerThan(t)))) { return(null); } PartList mycopyofParts = parts.Copy(); BoardList mycopyofBoards = boards.Copy(); mycopyofParts.Sort(Part.CompareByAreaDecending); Solution CompleteSolution = new Solution() { TotalStockArea = boards.Sum(t => t.Area) }; do { double minCoverageRatio = 0.9; int boardcount = mycopyofBoards.Count; List <Solution> iSolutionSet = new List <Solution>(); object lockobj = new object(); do { List <Task> threads = new List <Task>(); Trace.WriteLine($"Getting combinations for {boardcount} boards [{string.Join(",", mycopyofBoards.Select(t => t.Name))}] and {mycopyofParts.Count} parts with at least {minCoverageRatio * 100} % coverage"); for (int j = 0; j < boardcount; j++) { // get the best solution for every board threads.Add( Task.Factory.StartNew((o) => { Item iBoard = mycopyofBoards[(int)o]; var iCombos = GetCombos(mycopyofParts, iBoard, 0, minCoverageRatio * iBoard.Area); iCombos.Sort(Combo.CompareByCumAreaDesc); Trace.WriteLine($"Finding combinations for board {iBoard.Name}"); Solution topSolution = null; Combo topCombo = iCombos.FirstOrDefault(q => (topSolution = BruteForce.Pack_async(q.AsPartList(), iBoard).Result) != null); if (topSolution != null) { Trace.WriteLine($"Best satisfactory combination for board {iBoard.Name}: [{string.Join(",", topCombo.Select(q => q.Name))}] ; coverage = {(topSolution.PlacedArea / iBoard.Area * 100):0.0} %, waste = {topSolution.Waste / 1000000} m\u00b2"); iSolutionSet.Add(topSolution); } else { Trace.WriteLine($"No satisfactory combination for board {iBoard.Name}"); } }, j) ); } Task.WaitAll(threads.ToArray()); minCoverageRatio -= minCoverageRatio * minCoverageRatio; if (minCoverageRatio < 0.5) { minCoverageRatio = 0; } } while (iSolutionSet.Count == 0); // keep the best solution iSolutionSet.Sort(Solution.CompareByWasteAscending); // check if there is another solution that did not include any of these ... foreach (var topSol in iSolutionSet) { // if none of the parts in this solution is already part of the kept solutions if (!topSol.Any(t => CompleteSolution.FirstOrDefault(q => q.Part.Name == t.Part.Name) != null)) { // add this solution to the kept solutions Item topBoard = topSol.First().Stock; Trace.WriteLine($"Keeping solution for board {topBoard.Name}: [{string.Join(",", topSol.Select(q => q.Part.Name))}] ; coverage = {(topSol.PlacedArea / topBoard.Area * 100):0.0} %, waste = {topSol.Waste / 1000000} m\u00b2"); CompleteSolution.AddRange(topSol); // remove the parts and boards topSol.ForEach(t => mycopyofParts.Remove(t.Part)); mycopyofBoards.Remove(topBoard); } } Trace.WriteLine($"{mycopyofBoards.Count} boards and {mycopyofParts.Count} parts remain"); Trace.WriteLine(""); if (mycopyofBoards.Count == 0 && mycopyofParts.Count > 0) { return(null); } } while (mycopyofParts.Count > 0); return(CompleteSolution); }