Exemple #1
0
        internal static void FromCSV(string path, out PartList parts, out BoardList boards)
        {
            CsvFileDescription inputFileDescription = new CsvFileDescription
            {
                SeparatorChar               = ',',
                FirstLineHasColumnNames     = false,
                EnforceCsvColumnAttribute   = true,
                UseFieldIndexForReadingData = true
            };
            CsvContext cc = new CsvContext();

            IEnumerable <CSVRecord> records = new List <CSVRecord>(
                cc.Read <CSVRecord>(path, inputFileDescription));

            parts  = new PartList();
            boards = new BoardList();
            foreach (var iline in records.Where(t => !t.ItemType.StartsWith("#")))
            {
                if (iline.ItemType.ToLower() == "board")
                {
                    boards.Append(new BoardNode(iline.PartID, double.Parse(iline.Length), double.Parse(iline.Width)));
                }
                else
                {
                    parts.Append(new PartNode(iline.PartID, double.Parse(iline.Length), double.Parse(iline.Width)));
                }
            }
        }
Exemple #2
0
        public static void FromCutlistPlusCSV(string filePath, out PartList parts, out BoardList boards)
        {
            CsvFileDescription inputFileDescription = new CsvFileDescription
            {
                SeparatorChar               = ',',
                FirstLineHasColumnNames     = false,
                EnforceCsvColumnAttribute   = true,
                UseFieldIndexForReadingData = true
            };
            CsvContext cc = new CsvContext();

            IEnumerable <CutListPlusCSVRecord> records = new List <CutListPlusCSVRecord>(
                cc.Read <CutListPlusCSVRecord>(filePath, inputFileDescription));

            parts  = new PartList();
            boards = new BoardList();
            foreach (var iline in records.Where(t => t.PartNumber != "Part #"))
            {
                if (iline.MaterialName == "Stock")
                {
                    boards.Append(new BoardNode(iline.PartName, double.Parse(iline.Length.Replace("mm", "")), double.Parse(iline.Width.Replace("mm", ""))));
                }
                else
                {
                    parts.Append(new PartNode(iline.PartName, double.Parse(iline.Length.Replace("mm", "")), double.Parse(iline.Width.Replace("mm", ""))));
                }
            }
        }
Exemple #3
0
        private async static Task <Solution> Pack_async(PartList parts, BoardList stock)
        {
            List <Task> threads    = new List <Task>();
            int         stockcount = stock.Count;

            Solution[] solutions = new Solution[stockcount];

            for (int i = 0; i < stockcount; i++)
            {
                threads.Add(
                    Task.Factory.StartNew(async(o) =>
                {
                    int ii                 = (int)o;
                    Item iStock            = stock[ii];
                    PartList mycopyofParts = parts.Copy();
                    Part iPart             = mycopyofParts[0];
                    mycopyofParts.RemoveAt(0);

                    if (!iStock.TrySplit(iPart, out Item H1, out Item H2, out Item V1, out Item V2))
                    {
                        return;
                    }

                    if (mycopyofParts.Count == 0)
                    {
                        solutions[ii] = new Solution(iPart, iStock);
                        return;
                    }

                    BoardList myVstock = stock.Copy();
                    myVstock.Remove(iStock);
                    myVstock.AddRange(V2, V1);
                    Solution solV = PackALL(mycopyofParts, myVstock);
                    if (solV != null)
                    {
                        solutions[ii] = new Solution(iPart, iStock);
                        solutions[ii].AddRange(solV);
                        return;
                    }

                    BoardList myHstock = stock.Copy();
                    myHstock.Remove(iStock);
                    myHstock.AddRange(H2, H1);
                    Solution solH = PackALL(mycopyofParts, myHstock);
                    if (solH != null)
                    {
                        solutions[ii] = new Solution(iPart, iStock);
                        solutions[ii].AddRange(solH);
                        return;
                    }
                }, i));
            }
            Task.WaitAll(threads.ToArray());


            return(solutions.FirstOrDefault(t => t != null));
        }
Exemple #4
0
        /*
         *          for the first part,
         *          loop through all stock
         *              if fits
         *                  put part on stock and devide remaining stock
         *                  place remaining parts on remainig stock
         *
         *
         *          for the first board,
         *          loop through all parts
         *              if part fits on stock
         *                  put part on stock and devide remaining stock
         *                  place remaining parts on remaining stock
         *
         *
         *
         *
         * public static Solution PackALL2(PartList parts, BoardList stock, int level=0)
         * {
         *  Trace.WriteLine($"{level}:started with {parts.Count} parts:");
         *  parts.ForEach(t => Trace.WriteLine($"{level}:   {t},"));
         *  Trace.WriteLine($"{level}: ....and {stock.Count} boards:");
         *  stock.ForEach(t => Trace.WriteLine($"{level}:   {t},"));
         *
         *  if (parts.Count == 0)
         *  {
         *      Trace.WriteLine($"{level}:no parts to place. returning empty solution");
         *      return new Solution();
         *  }
         *
         *
         *  Solution sol = new Solution();
         *
         *  //Trace.WriteLine($"{level}:Making local copy of stocklist");
         *  BoardList mycopyofStock = stock.Copy();
         *
         *  Board iBoard = mycopyofStock[0];
         *  Trace.WriteLine($"{level}:remove board {iBoard} from locl copy of stocklist");
         *  mycopyofStock.RemoveAt(0);
         *
         *  foreach (Part iPart in parts)
         *  {
         *      if (!iBoard.TrySplit(iPart, out Board H1, out Board H2, out Board V1, out Board V2))
         *      {
         *          Trace.WriteLine($"{level}:part {iPart} does not fit on board {iBoard}");
         *          continue;
         *      }
         *      Trace.WriteLine($"{level}:part {iPart} fits on board {iBoard} with remainders:");
         *      Trace.WriteLine($"{level}:   {H1},");
         *      Trace.WriteLine($"{level}:   {H2},");
         *      Trace.WriteLine($"{level}:   {V1},");
         *      Trace.WriteLine($"{level}:   {V2},");
         *
         *      Trace.WriteLine($"{level}:packing remaining parts on remaining boards plus V2 and V1");
         *      BoardList myVstock = mycopyofStock.Copy();
         *      myVstock.AddRange(V2, V1);
         *      PartList partsV = parts.Copy();
         *      partsV.Remove(iPart);
         *      Solution solV = PackALL2(partsV, myVstock,level+1);
         *      if (solV != null)
         *      {
         *          Trace.WriteLine($"{level}:succesfully packed {solV.Count} parts using V2 & V1");
         *          Trace.WriteLine($"{level}:merging solution for part {iPart} and remaining parts and returning");
         *          sol.Add(iPart, iBoard);
         *          sol.AddRange(solV);
         *          return sol;
         *      }
         *
         *      Trace.WriteLine($"{level}:packing remaining parts on remaining boards plus H2 and H1");
         *      BoardList myHstock = mycopyofStock.Copy();
         *      myHstock.AddRange(H2, H1);
         *
         *      PartList partsH = parts.Copy();
         *      partsH.Remove(iPart);
         *      Solution solH = PackALL2(partsH, myHstock,level+1);
         *
         *      if (solH != null)
         *      {
         *          Trace.WriteLine($"{level}:succesfully packed {solH.Count} parts using H2 & H1");
         *          Trace.WriteLine($"{level}:merging solution for part {iPart} and remaining parts and returning");
         *          sol.Add(iPart, iBoard);
         *          sol.AddRange(solH);
         *          return sol;
         *      }
         *
         *      Trace.WriteLine($"{level}:neither H or V remainder variants procuced succesfull packings...lets try another part in this place in stead...");
         *  }
         *  Trace.WriteLine($"{level}:packing failed - returning null");
         *  return null;
         * }
         *
         */



        public async static Task <Solution> Pack_async(PartList parts, Item stock)
        {
            Solution t = await Pack_async(parts, new BoardList { stock });

            if (t != null)
            {
                t.UsedStockArea = stock.Area;
            }
            return(t);
        }
Exemple #5
0
        public PartList OrderredByArea()
        {
            PartList orderredList = new PartList();

            for (PartNode iPart = Head; iPart != null; iPart = iPart.Next)
            {
                orderredList.InsertItemSortedbyAreaAsc(new PartNode(iPart));
            }

            return(orderredList);
        }
Exemple #6
0
        public async static Task <Solution> Pack(PartList parts, Item board)
        {
            ComboFit.parts = parts.Where(t => int.Parse(t.Name) < 25).ToArray();
            partcount      = ComboFit.parts.Length;
            Stopwatch sw = new Stopwatch();

            sw.Start();
            DoWork(0, 0);
            sw.Stop();
            Trace.WriteLine($"{count} in {sw.ElapsedMilliseconds} ms");
            return(null);
        }
Exemple #7
0
        public static Solution PackALL(PartList parts, Item stock)
        {
            Solution t = PackALL(parts, new BoardList {
                stock
            });

            if (t != null)
            {
                t.UsedStockArea = stock.Area;
            }
            return(t);
        }
Exemple #8
0
 public PartList(PartList original)
 {
     Head  = new PartNode(original.Head);
     Tail  = Head;
     Count = original.Count;
     for (PartNode iPart = original.Head.Next; iPart != null; iPart = iPart.Next)
     {
         var newPart = new PartNode(iPart);
         newPart.Prev = Tail;
         Tail.Next    = newPart;
         Tail         = newPart;
     }
 }
Exemple #9
0
        /// <summary>
        /// ALL parts must fit for an acceptable solution - we use this to check if a given combination of parts will fit on a board.
        /// </summary>
        /// <param name="parts"></param>
        /// <param name="stock"></param>
        /// <returns></returns>
        private static Solution PackALL(PartList parts, BoardList stock)
        {
            Solution sol           = new Solution();
            PartList mycopyofParts = parts.Copy();

            Part iPart = mycopyofParts[0];

            mycopyofParts.RemoveAt(0);

            int stockcount = stock.Count;

            for (int i = 0; i < stockcount; i++)
            {
                Item iStock = stock[i];
                if (!iStock.TrySplit(iPart, out Item H1, out Item H2, out Item V1, out Item V2))
                {
                    continue;
                }

                if (mycopyofParts.Count == 0)
                {
                    sol.Add(iPart, iStock);
                    return(sol);
                }

                BoardList myVstock = stock.Copy();
                myVstock.Remove(iStock);
                myVstock.AddRange(V2, V1);
                Solution solV = PackALL(mycopyofParts, myVstock);
                if (solV != null)
                {
                    sol.Add(iPart, iStock);
                    sol.AddRange(solV);
                    return(sol);
                }

                BoardList myHstock = stock.Copy();
                myHstock.Remove(iStock);
                myHstock.AddRange(H2, H1);
                Solution solH = PackALL(mycopyofParts, myHstock);
                if (solH != null)
                {
                    sol.Add(iPart, iStock);
                    sol.AddRange(solH);
                    return(sol);
                }
            }
            return(null);
        }
Exemple #10
0
        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);
        }
Exemple #11
0
 public void Append(PartList list)
 {
     if (Head == null)
     {
         Head  = list.Head;
         Tail  = list.Tail;
         Count = list.Count;
     }
     else
     {
         Tail.Next      = list.Head;
         list.Head.Prev = Tail;
         Tail           = list.Tail;
         Count         += list.Count;
     }
 }
Exemple #12
0
        public static Bitmap Draw(BoardList boards, PartList parts, bool usedstockonly = true)
        {
            double xOffset      = 0;
            double imageHeight  = 0;
            double boardSpacing = 70;
            double xMargin      = 50;
            double yMargin      = 50;
            double imageWidth   = 2 * xMargin - boardSpacing;
            Font   boardFont    = new Font(new FontFamily("Consolas"), 15.0f);

            // create list of boards to draw
            List <BoardNode> boardsToDraw = new List <BoardNode>(boards.ToArray);

            if (usedstockonly)
            {
                List <string> usedboardnames = parts.ToArray.Select(t => t.Container).Distinct().ToList();
                boardsToDraw.RemoveAll(t => !usedboardnames.Contains(t.ID));
            }

            // calculate width & height required for the bitmap
            foreach (var iBoard in boardsToDraw)
            {
                if (iBoard.Length > imageHeight)
                {
                    imageHeight = iBoard.Length;
                }
                imageWidth += iBoard.Width + boardSpacing;
            }
            imageHeight += 2 * yMargin;

            // create bitmap
            Bitmap   bitmap = new Bitmap((int)imageWidth, (int)imageHeight);
            Graphics g      = Graphics.FromImage(bitmap);

            // fill the background with black
            g.FillRectangle(Brushes.Black, 0, 0, (int)imageWidth, (int)imageHeight);

            // loop through all the boards to be drawn
            xOffset = xMargin;
            foreach (var iBoard in boardsToDraw)
            {
                // draw the board
                g.FillRectangle(Brushes.DarkRed, (float)(xOffset), (float)yMargin, (float)iBoard.Width, (float)iBoard.Length);
                string boardheader   = $"{iBoard.ID} [{iBoard.Length}x{iBoard.Width}]";
                SizeF  textSizeBoard = g.MeasureString(boardheader, boardFont);
                g.DrawString(boardheader, boardFont, Brushes.White, (float)(xOffset + iBoard.Width / 2 - textSizeBoard.Width / 2), (float)(yMargin / 2 - textSizeBoard.Height / 2));

                // loop through all the parts and draw the ones on the current board
                string overflowtext = "";
                for (PartNode iPlacement = parts.Head; iPlacement != null; iPlacement = iPlacement.Next)
                {
                    // continue with next part if this part was placed on another board
                    if (iPlacement.Container != iBoard.ID)
                    {
                        continue;
                    }

                    // draw the part
                    g.FillRectangle(Brushes.Green, (float)(xOffset + iPlacement.dWidth), (float)(iPlacement.dLength + yMargin), (float)iPlacement.Width, (float)iPlacement.Length);

                    // print the part text
                    string text1  = $"{iPlacement.ID} [{iPlacement.Length} x {iPlacement.Width}]";
                    string text2a = $"{iPlacement.ID}";
                    string text2b = $"[{iPlacement.Length} x {iPlacement.Width}]";
                    g.TranslateTransform((float)(xOffset + iPlacement.dWidth + iPlacement.Width / 2), (float)(iPlacement.dLength + iPlacement.Length / 2 + yMargin));
                    g.RotateTransform(-90);

                    int sz = 16;
                    do
                    {
                        Font  partFont = new Font(new FontFamily("Consolas"), --sz);
                        SizeF textSize = g.MeasureString(text1, partFont);
                        if (textSize.Width < iPlacement.Length && textSize.Height < iPlacement.Width)
                        {
                            g.DrawString(text1, partFont, Brushes.White, -(textSize.Width / 2), -(textSize.Height / 2));
                            break;
                        }
                        textSize = g.MeasureString(text2a, partFont);
                        SizeF textSize2 = g.MeasureString(text2b, partFont);
                        if (Math.Max(textSize.Width, textSize2.Width) < iPlacement.Length && textSize.Height + textSize2.Height < iPlacement.Width)
                        {
                            g.DrawString(text2a, partFont, Brushes.White, -(textSize.Width / 2), -textSize.Height);
                            g.DrawString(text2b, partFont, Brushes.White, -(textSize2.Width / 2), 0);
                            break;
                        }
                        if (textSize.Width < iPlacement.Length && textSize.Height < iPlacement.Width)
                        {
                            g.DrawString(text2a, partFont, Brushes.White, -(textSize.Width / 2), -(textSize.Height / 2));
                            overflowtext += text1 + ", ";
                            break;
                        }
                    } while (sz > 1);


                    g.RotateTransform(90);
                    g.TranslateTransform(-((float)xOffset + (float)(iPlacement.dWidth + iPlacement.Width / 2)), -((float)(iPlacement.dLength + iPlacement.Length / 2 + yMargin)));
                }

                g.TranslateTransform((float)(xOffset + iBoard.Width), (float)(iBoard.Length + yMargin));
                g.RotateTransform(-90);
                g.DrawString(overflowtext.TrimEnd(',', ' '), boardFont, Brushes.White, 0, 0);
                g.RotateTransform(90);
                g.TranslateTransform(-(float)(xOffset + iBoard.Width), -(float)(iBoard.Length + yMargin));

                xOffset += iBoard.Width + boardSpacing;
            }

            g.Flush();
            bitmap.RotateFlip(RotateFlipType.Rotate90FlipNone);
            return(bitmap);
        }
Exemple #13
0
        static void Main(string[] args)
        {
            #region // Gather the inputs to the solution ...
            double boardMargins_length = 0;
            double boardMargins_Width  = 0;
            double PartPadding_Length  = 0;
            double PartPadding_Width   = 0;
            double SawKerf             = 3.2;


            BoardList boards = new BoardList(
                //new BoardNode("A", 2100, 193),
                //new BoardNode("B", 2100, 150),
                //new BoardNode("C", 2100, 143),
                //new BoardNode("D", 2100, 170),
                //new BoardNode("E", 2100, 185),
                new BoardNode("F", 2100, 210),
                //new BoardNode("G", 2100, 135),
                //new BoardNode("H", 2100, 225),
                null
                );

            PartList parts = new PartList(
                new PartNode("001", 1721.7, 100.0),
                new PartNode("002", 284.5, 100.0),
                new PartNode("003", 1721.7, 100.0),
                new PartNode("004", 284.5, 100.0),
                new PartNode("005", 955.0, 69.3),
                new PartNode("006", 955.0, 60.0),
                new PartNode("007", 955.0, 69.6),
                new PartNode("008", 955.0, 80.0),
                new PartNode("009", 955.0, 60.0),
                new PartNode("010", 955.0, 60.0),
                new PartNode("011", 310.0, 100.0),
                new PartNode("012", 310.0, 100.0),
                new PartNode("013", 310.0, 36.0),
                new PartNode("014", 310.0, 36.0),
                new PartNode("015", 354.5, 36.0),
                new PartNode("016", 354.5, 36.0),
                new PartNode("017", 299.0, 20.0),
                new PartNode("018", 299.0, 20.0),
                new PartNode("019", 299.0, 20.0),
                new PartNode("020", 299.0, 20.0),
                new PartNode("021", 327.5, 20.0),
                new PartNode("022", 327.5, 20.0),
                //new PartNode("023", 955.0, 80.0),
                //new PartNode("024", 310.0, 100.0),
                //new PartNode("025", 310.0, 100.0),
                //new PartNode("026", 310.0, 36.0),
                //new PartNode("027", 310.0, 36.0),
                //new PartNode("028", 354.5, 36.0),
                //new PartNode("029", 354.5, 36.0),
                //new PartNode("030", 299.0, 20.0),
                //new PartNode("031", 327.5, 20.0),
                //new PartNode("032", 327.5, 20.0),
                //new PartNode("033", 955.0, 80.0),
                //new PartNode("034", 310.0, 100.0),
                //new PartNode("035", 310.0, 100.0),
                //new PartNode("036", 310.0, 36.0),
                //new PartNode("037", 310.0, 36.0),
                //new PartNode("038", 354.5, 36.0),
                //new PartNode("039", 354.5, 36.0),
                //new PartNode("040", 299.0, 20.0),
                //new PartNode("041", 327.5, 20.0),
                //new PartNode("042", 327.5, 20.0),
                //new PartNode("043", 955.0, 80.0),
                //new PartNode("044", 310.0, 100.0),
                //new PartNode("045", 310.0, 100.0),
                //new PartNode("046", 310.0, 36.0),
                //new PartNode("047", 310.0, 36.0),
                //new PartNode("048", 354.5, 36.0),
                //new PartNode("049", 354.5, 36.0),
                //new PartNode("050", 299.0, 20.0),
                null
                );
            if (args[0].StartsWith("-clp:"))
            {
                string path = args[0].Replace("-clp:", "");
                if (System.IO.File.Exists(path))
                {
                    Import.FromCutlistPlusCSV(path, out parts, out boards);
                }
            }
            if (args[0].StartsWith("-csv:"))
            {
                string path = args[0].Replace("-csv:", "");
                if (System.IO.File.Exists(path))
                {
                    Import.FromCSV(path, out parts, out boards);
                }
            }
            #endregion

            //PartNode[] partsarray = parts.ToArray;
            //List<PartNode> partsList = new List<PartNode>(partsarray);
            //Stopwatch swt = new Stopwatch();
            //swt.Start();
            //for (int i = 0; i < 120000000; i++)
            //{
            //    //Array
            //    PartNode iPart;
            //    for (int j = 0; j < partsarray.Length; j++) { iPart = partsarray[j]; }

            //    //linked list
            //    //for (var iPart = parts.Head; iPart != null; iPart = iPart.Next) ;

            //    //List
            //    //foreach (var iPart in partsList) ;
            //    //PartNode iPart;
            //    //for (int j = 0; j < partsList.Count; j++) iPart = partsList[j];
            //}
            //swt.Stop();
            //Trace.WriteLine(swt.ElapsedMilliseconds);


            #region // Print starting parameters ...
            Trace.WriteLine($"Packing started @ {DateTime.Now} with the following:");
            Trace.WriteLine($"------------------------------------------------");
            Trace.WriteLine($"  Board margins (w x l) : {boardMargins_Width} mm x {boardMargins_length} mm");
            Trace.WriteLine($"  Part padding  (w x l) : {PartPadding_Width} mm x {PartPadding_Length} mm");
            Trace.WriteLine($"  Saw blade kerf        : {SawKerf} mm");
            Trace.WriteLine($"  {boards.Count} Boards:");
            for (BoardNode iBoard = boards.Head; iBoard != null; iBoard = iBoard.Next)
            {
                Trace.WriteLine($"{iBoard.ID,6} [{iBoard.Length,7:0.0} x {iBoard.Width,5:0.0}]");
            }
            Trace.WriteLine($"  {parts.Count} Parts:");
            for (PartNode iPart = parts.Head; iPart != null; iPart = iPart.Next)
            {
                Trace.WriteLine($"{iPart.ID,6} [{iPart.Length,7:0.0} x {iPart.Width,5:0.0}]");
            }
            #endregion

            #region // Find the solution ...
            Stopwatch sw = new Stopwatch();
            sw.Start();
            var solution = Packer.Pack(parts, boards,
                                       sawkerf: SawKerf,
                                       boardMarginLength: boardMargins_length,
                                       boardMarginWidth: boardMargins_Width,
                                       partPaddingLength: PartPadding_Length,
                                       partPaddingWidth: PartPadding_Width);
            sw.Stop();
            #endregion

            #region // Print the solution ...
            // calculate the total area of all used boards
            List <string> usedBoardIDs  = solution.ToArray.Select(t => t.Container).Distinct().ToList();
            double        UsedStockArea = boards.ToArray.Where(q => usedBoardIDs.Contains(q.ID)).Sum(t => t.Area);

            Trace.WriteLine("Solution:");
            Trace.WriteLine("----------------");
            if (solution.Count < parts.Count)
            {
                Trace.WriteLine("WARNING: All parts could not be placed!\r\n");
            }

            for (var iBoard = boards.Head; iBoard != null; iBoard = iBoard.Next)
            {
                if (iBoard.Solution == null)
                {
                    Trace.WriteLine($"   Board {iBoard.ID} [{iBoard.Length,6:0.0} x {iBoard.Width,5:0.0}] : not used.");
                }
                else
                {
                    Trace.WriteLine($"   Board {iBoard.ID} [{iBoard.Length,6:0.0} x {iBoard.Width,5:0.0}] ({(iBoard.Solution == null ? 0 : iBoard.Solution.TotalArea / iBoard.Area):00.0 %}) :\r\n{iBoard.Solution?.ToString()}");
                }
            }
            Trace.WriteLine("===========================================================");
            Trace.WriteLine("Solution summary");
            Trace.WriteLine("----------------");
            Trace.WriteLine($"   Processing time: {sw.ElapsedMilliseconds,5:0} ms");
            Trace.WriteLine($"   Boards         : {boards.Count,5:0}    ({boards.TotalArea / 1000000,6:0.000} m\u00b2)");
            Trace.WriteLine($"   Used boards    : {usedBoardIDs.Count,5:0}    ({UsedStockArea / 1000000,6:0.000} m\u00b2)");
            Trace.WriteLine($"   Parts          : {parts.Count,5:0}    ({parts.TotalArea / 1000000,6:0.000} m\u00b2)");
            Trace.WriteLine($"   Placed parts   : {solution.Count,5:0}    ({solution.TotalArea / 1000000,6:0.000} m\u00b2)");
            Trace.WriteLine($"   Waste          : {(UsedStockArea - solution.TotalArea) / UsedStockArea,7:0.0 %}  ({(UsedStockArea - solution.TotalArea) / 1000000,6:0.000} m\u00b2)");
            Trace.WriteLine($"   Coverage       : {solution.TotalArea / UsedStockArea,7:0.0 %}  ({(solution.TotalArea) / 1000000,6:0.000} m\u00b2)");
            #endregion

            #region // Draw solution to an image ...
            Bitmap bmp = Draw(boards, solution);
            bmp.Save("out.bmp");
            Console.WriteLine("Launch output image (Y/N):");
            string s = Console.ReadLine();
            if (s.ToLower() == "y")
            {
                Process.Start("out.bmp");
            }
            #endregion
        }
Exemple #14
0
        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);
        }