Example #1
0
        internal void ScaleToMM(GerberNumberFormat gerberNumberFormat)
        {
            foreach (var a in Pairs)
            {
                switch (a.Value.Command)
                {
                case "X":
                case "Y":
                case "I":
                case "J":
                    a.Value.Number = gerberNumberFormat.ScaleFileToMM(a.Value.Number);

                    break;
                }
            }
        }
Example #2
0
        bool ParseExcellon(List <string> lines, double drillscaler, ProgressLog log)
        {
            log.PushActivity("Parse Excellon");
            Tools.Clear();
            bool               headerdone  = false;
            int                currentline = 0;
            ExcellonTool       CurrentTool = null;
            GerberNumberFormat GNF         = new GerberNumberFormat();

            GNF.DigitsBefore = 3;
            GNF.DigitsAfter  = 3;
            GNF.OmitLeading  = true;
            double Scaler          = 1.0f;
            bool   FormatSpecified = false;
            bool   NumberSpecHad   = false;
            double LastX           = 0;
            double LastY           = 0;

            while (currentline < lines.Count)
            {
                switch (lines[currentline])
                {
                //  case "M70":  GNF.Multiplier = 25.4; break; // inch mode
                case "INCH":
                    if (Gerber.ExtremelyVerbose)
                    {
                        log.AddString("Out of header INCH found!");
                    }
                    GNF.SetImperialMode();

                    break;     // inch mode

                case "METRIC":
                    if (Gerber.ExtremelyVerbose)
                    {
                        log.AddString("Out of header METRIC found!");
                    }

                    GNF.SetMetricMode();
                    break;

                case "M72":
                    if (Gerber.ExtremelyVerbose)
                    {
                        log.AddString("Out of header M72 found!");
                    }
                    GNF.SetImperialMode();
                    break;     // inch mode

                case "M71":
                    if (Gerber.ExtremelyVerbose)
                    {
                        log.AddString("Out of header M71 found!");
                    }
                    GNF.SetMetricMode();
                    break;     // metric mode
                }
                if (lines[currentline] == "M48")
                {
                    //Console.WriteLine("Excellon header starts at line {0}", currentline);
                    currentline++;
                    while ((lines[currentline] != "%" && lines[currentline] != "M95"))
                    {
                        headerdone = true;
                        //double InchMult = 1;// 0.010;
                        switch (lines[currentline])
                        {
                        //  case "M70":  GNF.Multiplier = 25.4; break; // inch mode
                        case "INCH":
                            GNF.SetImperialMode();

                            //Scaler = 0.01;
                            break;     // inch mode

                        case "METRIC":
                            GNF.SetMetricMode();
                            break;

                        case "M72":
                            //GNF.Multiplier = 25.4 * InchMult;
                            GNF.SetImperialMode();
                            //  Scaler = 0.01;
                            break;     // inch mode

                        case "M71":
                            //GNF.Multiplier = 1.0;
                            GNF.SetMetricMode();
                            break;     // metric mode

                        default:
                        {
                            var S = lines[currentline].Split(',');
                            if (S[0].IndexOf("INCH") == 0 || S[0].IndexOf("METRIC") == 0)
                            {
                                if (S[0].IndexOf("INCH") == 0)
                                {
                                    GNF.SetImperialMode();
                                }
                                else
                                {
                                    GNF.SetMetricMode();
                                }
                                if (S.Count() > 1)
                                {
                                    for (int i = 1; i < S.Count(); i++)
                                    {
                                        if (S[i][0] == '0')
                                        {
                                            log.AddString(String.Format("Number spec reading!: {0}", S[i]));
                                            var A = S[i].Split('.');
                                            if (A.Length == 2)
                                            {
                                                GNF.DigitsBefore = A[0].Length;
                                                GNF.DigitsAfter  = A[1].Length;
                                                NumberSpecHad    = true;
                                            }
                                        }
                                        if (S[i] == "LZ")
                                        {
                                            GNF.OmitLeading = false;
                                        }
                                        if (S[i] == "TZ")
                                        {
                                            GNF.OmitLeading = true;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if (lines[currentline][0] == ';')
                                {
                                    if (Gerber.ShowProgress)
                                    {
                                        log.AddString(lines[currentline]);
                                    }

                                    if (lines[currentline].Contains(";FILE_FORMAT="))
                                    {
                                        var N = lines[currentline].Substring(13).Split(':');
                                        GNF.DigitsBefore = int.Parse(N[0]);
                                        GNF.DigitsAfter  = int.Parse(N[1]);
                                        FormatSpecified  = true;
                                    }
                                }
                                else
                                {
                                    GCodeCommand GCC = new GCodeCommand();
                                    GCC.Decode(lines[currentline], GNF);
                                    if (GCC.charcommands.Count > 0)
                                    {
                                        switch (GCC.charcommands[0])
                                        {
                                        case 'T':
                                        {
                                            ExcellonTool ET = new ExcellonTool();


                                            ET.ID = (int)GCC.numbercommands[0];

                                            ET.Radius    = GNF.ScaleFileToMM(GCC.GetNumber('C')) / 2.0f;
                                            Tools[ET.ID] = ET;
                                        }
                                        break;
                                        }
                                    }
                                }
                            }
                        }
                        break;
                        }
                        currentline++;
                    }
                    //           Console.WriteLine("Excellon header stops at line {0}", currentline);
                    if (FormatSpecified == false && NumberSpecHad == false)
                    {
                        if (GNF.CurrentNumberScale == GerberNumberFormat.NumberScale.Imperial)
                        {
                            //  GNF.OmitLeading = true;
                            GNF.DigitsBefore = 2;
                            GNF.DigitsAfter  = 4;
                        }
                        else
                        {
                            GNF.DigitsAfter  = 3;
                            GNF.DigitsBefore = 3;
                        }
                    }
                }
                else
                {
                    if (headerdone)
                    {
                        GCodeCommand GCC = new GCodeCommand();
                        GCC.Decode(lines[currentline], GNF);
                        if (GCC.charcommands.Count > 0)
                        {
                            switch (GCC.charcommands[0])
                            {
                            case 'T':
                                if ((int)GCC.numbercommands[0] > 0)
                                {
                                    CurrentTool = Tools[(int)GCC.numbercommands[0]];
                                }
                                else
                                {
                                    CurrentTool = null;
                                }
                                break;

                            case 'M':

                            default:
                            {
                                GerberSplitter GS = new GerberSplitter();
                                GS.Split(GCC.originalline, GNF, true);
                                if (GS.Has("G") && GS.Get("G") == 85 && (GS.Has("X") || GS.Has("Y")))
                                {
                                    GerberListSplitter GLS = new GerberListSplitter();
                                    GLS.Split(GCC.originalline, GNF, true);

                                    double x1 = LastX;
                                    double y1 = LastY;

                                    if (GLS.HasBefore("G", "X"))
                                    {
                                        x1 = GNF.ScaleFileToMM(GLS.GetBefore("G", "X") * Scaler); LastX = x1;
                                    }
                                    if (GLS.HasBefore("G", "Y"))
                                    {
                                        y1 = GNF.ScaleFileToMM(GLS.GetBefore("G", "Y") * Scaler); LastY = y1;
                                    }


                                    double x2 = LastX;
                                    double y2 = LastY;

                                    if (GLS.HasAfter("G", "X"))
                                    {
                                        x2 = GNF.ScaleFileToMM(GLS.GetAfter("G", "X") * Scaler); LastX = x2;
                                    }
                                    if (GLS.HasAfter("G", "Y"))
                                    {
                                        y2 = GNF.ScaleFileToMM(GLS.GetAfter("G", "Y") * Scaler); LastY = y2;
                                    }

                                    CurrentTool.Slots.Add(new ExcellonTool.SlotInfo()
                                        {
                                            Start = new PointD(x1 * drillscaler, y1 * drillscaler), End = new PointD(x2 * drillscaler, y2 * drillscaler)
                                        });

                                    LastX = x2;
                                    LastY = y2;
                                }
                                else if (GS.Has("G") && GS.Get("G") == 00 && (GS.Has("X") || GS.Has("Y")))
                                {
                                    GerberListSplitter GLS = new GerberListSplitter();
                                    GLS.Split(GCC.originalline, GNF, true);

                                    double x1 = LastX;
                                    double y1 = LastY;

                                    if (GLS.HasAfter("G", "X"))
                                    {
                                        x1 = GNF.ScaleFileToMM(GLS.GetAfter("G", "X") * Scaler); LastX = x1;
                                    }
                                    if (GLS.HasAfter("G", "Y"))
                                    {
                                        y1 = GNF.ScaleFileToMM(GLS.GetAfter("G", "Y") * Scaler); LastY = y1;
                                    }
                                }
                                else if (GS.Has("G") && GS.Get("G") == 01 && (GS.Has("X") || GS.Has("Y")))
                                {
                                    GerberListSplitter GLS = new GerberListSplitter();
                                    GLS.Split(GCC.originalline, GNF, true);

                                    double x1 = LastX;
                                    double y1 = LastY;
                                    double x2 = LastX;
                                    double y2 = LastY;

                                    if (GLS.HasAfter("G", "X"))
                                    {
                                        x2 = GNF.ScaleFileToMM(GLS.GetAfter("G", "X") * Scaler); LastX = x2;
                                    }
                                    if (GLS.HasAfter("G", "Y"))
                                    {
                                        y2 = GNF.ScaleFileToMM(GLS.GetAfter("G", "Y") * Scaler); LastY = y2;
                                    }

                                    CurrentTool.Slots.Add(new ExcellonTool.SlotInfo()
                                        {
                                            Start = new PointD(x1 * drillscaler, y1 * drillscaler), End = new PointD(x2 * drillscaler, y2 * drillscaler)
                                        });

                                    LastX = x2;
                                    LastY = y2;
                                }
                                else
                                {
                                    if (GS.Has("X") || GS.Has("Y"))
                                    {
                                        double X = LastX;
                                        if (GS.Has("X"))
                                        {
                                            X = GNF.ScaleFileToMM(GS.Get("X") * Scaler);
                                        }
                                        double Y = LastY;
                                        if (GS.Has("Y"))
                                        {
                                            Y = GNF.ScaleFileToMM(GS.Get("Y") * Scaler);
                                        }
                                        CurrentTool.Drills.Add(new PointD(X * drillscaler, Y * drillscaler));
                                        LastX = X;
                                        LastY = Y;
                                    }
                                }
                            }
                            break;
                            }
                        }
                    }
                }
                currentline++;
            }
            log.PopActivity();
            return(headerdone);
        }
Example #3
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="lines"></param>
        /// <param name="drillscaler"></param>
        /// <param name="log"></param>
        /// <param name="radiusAdjust">半径修正系数,用于实现电镀孔厚度,单位mm</param>
        /// <returns></returns>
        bool ParseExcellon(List <string> lines, double drillscaler, ProgressLog log, double radiusScaler = 1.0f)
        {
            var LogID = log.PushActivity("Parse Excellon");

            Tools.Clear();
            bool               headerdone  = false;
            int                currentline = 0;
            ExcellonTool       CurrentTool = null;
            GerberNumberFormat GNF         = new GerberNumberFormat();

            GNF.DigitsBefore = 3;
            GNF.DigitsAfter  = 3;
            GNF.OmitLeading  = true;
            double             Scaler            = 1.0f;
            bool               FormatSpecified   = false;
            bool               NumberSpecHad     = false;
            double             LastX             = 0;
            double             LastY             = 0;
            CutterCompensation Compensation      = CutterCompensation.None;
            List <PointD>      PathCompensation  = new List <PointD>();
            bool               WarnIntersections = true;

            while (currentline < lines.Count)
            {
                switch (lines[currentline])
                {
                //  case "M70":  GNF.Multiplier = 25.4; break; // inch mode
                case "INCH":
                    if (Gerber.ExtremelyVerbose)
                    {
                        log.AddString("Out of header INCH found!");
                    }
                    GNF.SetImperialMode();

                    break;     // inch mode

                case "METRIC":
                    if (Gerber.ExtremelyVerbose)
                    {
                        log.AddString("Out of header METRIC found!");
                    }

                    GNF.SetMetricMode();
                    break;

                case "M72":
                    if (Gerber.ExtremelyVerbose)
                    {
                        log.AddString("Out of header M72 found!");
                    }
                    GNF.SetImperialMode();
                    break;     // inch mode

                case "M71":
                    if (Gerber.ExtremelyVerbose)
                    {
                        log.AddString("Out of header M71 found!");
                    }
                    GNF.SetMetricMode();
                    break;     // metric mode
                }
                if (lines[currentline] == "M48")
                {
                    //Console.WriteLine("Excellon header starts at line {0}", currentline);
                    currentline++;
                    while ((lines[currentline] != "%" && lines[currentline] != "M95"))
                    {
                        headerdone = true;
                        //double InchMult = 1;// 0.010;
                        switch (lines[currentline])
                        {
                        //  case "M70":  GNF.Multiplier = 25.4; break; // inch mode
                        case "INCH":
                            GNF.SetImperialMode();

                            //Scaler = 0.01;
                            break;     // inch mode

                        case "METRIC":
                            GNF.SetMetricMode();
                            break;

                        case "M72":
                            //GNF.Multiplier = 25.4 * InchMult;
                            GNF.SetImperialMode();
                            //  Scaler = 0.01;
                            break;     // inch mode

                        case "M71":
                            //GNF.Multiplier = 1.0;
                            GNF.SetMetricMode();
                            break;     // metric mode

                        default:
                        {
                            var S = lines[currentline].Split(',');
                            if (S[0].IndexOf("INCH") == 0 || S[0].IndexOf("METRIC") == 0)
                            {
                                if (S[0].IndexOf("INCH") == 0)
                                {
                                    GNF.SetImperialMode();
                                }
                                else
                                {
                                    GNF.SetMetricMode();
                                }
                                if (S.Count() > 1)
                                {
                                    for (int i = 1; i < S.Count(); i++)
                                    {
                                        if (S[i][0] == '0')
                                        {
                                            log.AddString(String.Format("Number spec reading!: {0}", S[i]));
                                            var A = S[i].Split('.');
                                            if (A.Length == 2)
                                            {
                                                GNF.DigitsBefore = A[0].Length;
                                                GNF.DigitsAfter  = A[1].Length;
                                                NumberSpecHad    = true;
                                            }
                                        }
                                        if (S[i] == "LZ")
                                        {
                                            GNF.OmitLeading = false;
                                        }
                                        if (S[i] == "TZ")
                                        {
                                            GNF.OmitLeading = true;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if (lines[currentline][0] == ';')
                                {
                                    if (Gerber.ShowProgress)
                                    {
                                        log.AddString(lines[currentline]);
                                    }

                                    if (lines[currentline].Contains(";FILE_FORMAT="))
                                    {
                                        var N = lines[currentline].Substring(13).Split(':');
                                        GNF.DigitsBefore = int.Parse(N[0]);
                                        GNF.DigitsAfter  = int.Parse(N[1]);
                                        FormatSpecified  = true;
                                    }
                                }
                                else
                                {
                                    GCodeCommand GCC = new GCodeCommand();
                                    GCC.Decode(lines[currentline], GNF);
                                    if (GCC.charcommands.Count > 0)
                                    {
                                        switch (GCC.charcommands[0])
                                        {
                                        case 'T':
                                        {
                                            ExcellonTool ET = new ExcellonTool();


                                            ET.ID = (int)GCC.numbercommands[0];

                                            ET.Radius    = GNF.ScaleFileToMM(GCC.GetNumber('C')) / 2.0f * radiusScaler;
                                            Tools[ET.ID] = ET;
                                        }
                                        break;
                                        }
                                    }
                                }
                            }
                        }
                        break;
                        }
                        currentline++;
                    }
                    //           Console.WriteLine("Excellon header stops at line {0}", currentline);
                    if (FormatSpecified == false && NumberSpecHad == false)
                    {
                        if (GNF.CurrentNumberScale == GerberNumberFormat.NumberScale.Imperial)
                        {
                            //  GNF.OmitLeading = true;
                            GNF.DigitsBefore = 2;
                            GNF.DigitsAfter  = 4;
                        }
                        else
                        {
                            GNF.DigitsAfter  = 3;
                            GNF.DigitsBefore = 3;
                        }
                    }
                }
                else
                {
                    if (headerdone)
                    {
                        GCodeCommand GCC = new GCodeCommand();
                        GCC.Decode(lines[currentline], GNF);
                        if (GCC.charcommands.Count > 0)
                        {
                            switch (GCC.charcommands[0])
                            {
                            case 'T':
                                if ((int)GCC.numbercommands[0] > 0)
                                {
                                    CurrentTool = Tools[(int)GCC.numbercommands[0]];
                                }
                                else
                                {
                                    CurrentTool = null;
                                }
                                break;

                            case 'M':

                            default:
                            {
                                GerberSplitter GS = new GerberSplitter();
                                GS.Split(GCC.originalline, GNF, true);
                                if (GS.Has("G") && GS.Get("G") == 85 && (GS.Has("X") || GS.Has("Y")))
                                {
                                    GerberListSplitter GLS = new GerberListSplitter();
                                    GLS.Split(GCC.originalline, GNF, true);

                                    double x1 = LastX;
                                    double y1 = LastY;

                                    if (GLS.HasBefore("G", "X"))
                                    {
                                        x1 = GNF.ScaleFileToMM(GLS.GetBefore("G", "X") * Scaler); LastX = x1;
                                    }
                                    if (GLS.HasBefore("G", "Y"))
                                    {
                                        y1 = GNF.ScaleFileToMM(GLS.GetBefore("G", "Y") * Scaler); LastY = y1;
                                    }


                                    double x2 = LastX;
                                    double y2 = LastY;

                                    if (GLS.HasAfter("G", "X"))
                                    {
                                        x2 = GNF.ScaleFileToMM(GLS.GetAfter("G", "X") * Scaler); LastX = x2;
                                    }
                                    if (GLS.HasAfter("G", "Y"))
                                    {
                                        y2 = GNF.ScaleFileToMM(GLS.GetAfter("G", "Y") * Scaler); LastY = y2;
                                    }

                                    CurrentTool.Slots.Add(new ExcellonTool.SlotInfo()
                                        {
                                            Start = new PointD(x1 * drillscaler, y1 * drillscaler), End = new PointD(x2 * drillscaler, y2 * drillscaler)
                                        });

                                    LastX = x2;
                                    LastY = y2;
                                }
                                else if (GS.Has("G") && GS.Get("G") == 00 && (GS.Has("X") || GS.Has("Y")))
                                {
                                    GerberListSplitter GLS = new GerberListSplitter();
                                    GLS.Split(GCC.originalline, GNF, true);

                                    double x1 = LastX;
                                    double y1 = LastY;

                                    if (GLS.HasAfter("G", "X"))
                                    {
                                        x1 = GNF.ScaleFileToMM(GLS.GetAfter("G", "X") * Scaler); LastX = x1;
                                    }
                                    if (GLS.HasAfter("G", "Y"))
                                    {
                                        y1 = GNF.ScaleFileToMM(GLS.GetAfter("G", "Y") * Scaler); LastY = y1;
                                    }

                                    /* cancel cutter compensation */
                                    Compensation = CutterCompensation.None;
                                    PathCompensation.Clear();
                                }
                                else if (GS.Has("G") && GS.Get("G") == 01 && (GS.Has("X") || GS.Has("Y")))
                                {
                                    GerberListSplitter GLS = new GerberListSplitter();
                                    GLS.Split(GCC.originalline, GNF, true);

                                    double x1 = LastX;
                                    double y1 = LastY;
                                    double x2 = LastX;
                                    double y2 = LastY;

                                    if (GLS.HasAfter("G", "X"))
                                    {
                                        x2 = GNF.ScaleFileToMM(GLS.GetAfter("G", "X") * Scaler); LastX = x2;
                                    }
                                    if (GLS.HasAfter("G", "Y"))
                                    {
                                        y2 = GNF.ScaleFileToMM(GLS.GetAfter("G", "Y") * Scaler); LastY = y2;
                                    }
                                    if (Compensation == CutterCompensation.None)
                                    {
                                        CurrentTool.Slots.Add(new ExcellonTool.SlotInfo()
                                            {
                                                Start = new PointD(x1 * drillscaler, y1 * drillscaler), End = new PointD(x2 * drillscaler, y2 * drillscaler)
                                            });
                                    }
                                    else
                                    {
                                        PathCompensation.Add(new PointD(x2 * drillscaler, y2 * drillscaler));
                                    }

                                    LastX = x2;
                                    LastY = y2;
                                }
                                else if (GS.Has("G") && GS.Get("G") == 40)         /* cutter compensation off */
                                {
                                    var comp = CutCompensation(PathCompensation, Compensation, CurrentTool.Radius * drillscaler);

                                    if (WarnIntersections)
                                    {
                                        /* warn about path intersections */
                                        for (int i = 0; i < comp.Count - 1; i++)
                                        {
                                            for (int j = i + 2; j < comp.Count - 1; j++)
                                            {
                                                var intersection = Helpers.SegmentSegmentIntersect(comp[i], comp[i + 1], comp[j], comp[j + 1]);
                                                if (intersection != null)
                                                {
                                                    log.AddString("Path with intersections found on cut compensation! Inspect output for accuracy!");
                                                    WarnIntersections = false;
                                                    break;
                                                }
                                            }

                                            if (!WarnIntersections)
                                            {
                                                break;
                                            }
                                        }
                                    }

                                    /* create line segments from set of points */
                                    var array = comp.Zip(comp.Skip(1), Tuple.Create);
                                    CurrentTool.Slots.AddRange(array.Select(i => new ExcellonTool.SlotInfo()
                                        {
                                            Start = i.Item1, End = i.Item2
                                        }));

                                    Compensation = CutterCompensation.None;
                                    PathCompensation.Clear();
                                }
                                else if (GS.Has("G") && GS.Get("G") == 41)         /* cutter compensation left: offset of the cutter radius is to the LEFT of contouring direction */
                                {
                                    if (Compensation != CutterCompensation.None)
                                    {
                                        log.AddString("Unterminated cutter compensation block found! Inspect output for accuracy!");
                                    }

                                    Compensation = CutterCompensation.Left;
                                    PathCompensation.Clear();
                                    PathCompensation.Add(new PointD(LastX * drillscaler, LastY * drillscaler));
                                }
                                else if (GS.Has("G") && GS.Get("G") == 42)         /* cutter compensation right: offset of the cutter radius is to the RIGHT of contouring direction */
                                {
                                    if (Compensation != CutterCompensation.None)
                                    {
                                        log.AddString("Unterminated cutter compensation block found! Inspect output for accuracy!");
                                    }

                                    Compensation = CutterCompensation.Right;
                                    PathCompensation.Clear();
                                    PathCompensation.Add(new PointD(LastX * drillscaler, LastY * drillscaler));
                                }
                                else
                                {
                                    //Deal with the repeat code
                                    if (GS.Has("R") && (GS.Has("X") || GS.Has("Y")))
                                    {
                                        double repeatX = 0;
                                        double repeatY = 0;

                                        if (GS.Has("X"))
                                        {
                                            repeatX = GNF.ScaleFileToMM(GS.Get("X") * Scaler);
                                        }
                                        if (GS.Has("Y"))
                                        {
                                            repeatY = GNF.ScaleFileToMM(GS.Get("Y") * Scaler);
                                        }

                                        for (int repeatIndex = 1; repeatIndex <= GS.Get("R"); repeatIndex++)
                                        {
                                            double X = LastX;
                                            if (GS.Has("X"))
                                            {
                                                X += repeatX;
                                            }

                                            double Y = LastY;
                                            if (GS.Has("Y"))
                                            {
                                                Y += repeatY;
                                            }

                                            CurrentTool.Drills.Add(new PointD(X * drillscaler, Y * drillscaler));
                                            LastX = X;
                                            LastY = Y;
                                        }
                                    }
                                    else if (GS.Has("X") || GS.Has("Y"))
                                    {
                                        double X = LastX;
                                        if (GS.Has("X"))
                                        {
                                            X = GNF.ScaleFileToMM(GS.Get("X") * Scaler);
                                        }
                                        double Y = LastY;
                                        if (GS.Has("Y"))
                                        {
                                            Y = GNF.ScaleFileToMM(GS.Get("Y") * Scaler);
                                        }
                                        if (Compensation == CutterCompensation.None)
                                        {
                                            CurrentTool.Drills.Add(new PointD(X * drillscaler, Y * drillscaler));
                                        }
                                        else
                                        {
                                            PathCompensation.Add(new PointD(X * drillscaler, Y * drillscaler));
                                        }
                                        LastX = X;
                                        LastY = Y;
                                    }
                                }
                            }
                            break;
                            }
                        }
                    }
                }
                currentline++;
            }
            log.PopActivity(LogID);
            return(headerdone);
        }