示例#1
0
        public void Write(string filename, double DX, double DY, double DXp, double DYp, double AngleInDeg = 0)
        {
            double Angle = AngleInDeg * (Math.PI * 2.0) / 360.0;
            double CA    = Math.Cos(Angle);
            double SA    = Math.Sin(Angle);

            List <string> lines = new List <string>();

            lines.Add("%");
            lines.Add("M48");
            lines.Add("METRIC,000.000");
            //lines.Add("M71");
            foreach (var a in Tools)
            {
                lines.Add(String.Format("T{0}C{1}", a.Key.ToString("D2"), (a.Value.Radius * 2).ToString("N2").Replace(',', '.')));
            }
            lines.Add("%");
            GerberNumberFormat GNF = new GerberNumberFormat();

            GNF.SetMetricMode();
            GNF.OmitLeading  = true;
            GNF.DigitsAfter  = 3;
            GNF.DigitsBefore = 3;
            foreach (var a in Tools)
            {
                lines.Add(String.Format("T{0}", a.Key.ToString("D2")));
                double coordmultiplier = 1;

                foreach (var d in a.Value.Drills)
                {
                    double X = (d.X * coordmultiplier + DXp) / coordmultiplier;
                    double Y = (d.Y * coordmultiplier + DYp) / coordmultiplier;
                    if (Angle != 0)
                    {
                        double nX = X * CA - Y * SA;
                        double nY = X * SA + Y * CA;
                        X = nX;
                        Y = nY;
                    }
                    X = (X * coordmultiplier + DX) / coordmultiplier;
                    Y = (Y * coordmultiplier + DY) / coordmultiplier;

                    lines.Add(string.Format("X{0}Y{1}", GNF.Format(X), GNF.Format(Y).Replace(',', '.')));
                }

                foreach (var s in a.Value.Slots)
                {
                    double XS = (s.Start.X * coordmultiplier + DXp) / coordmultiplier;
                    double YS = (s.Start.Y * coordmultiplier + DYp) / coordmultiplier;
                    double XE = (s.End.X * coordmultiplier + DXp) / coordmultiplier;
                    double YE = (s.End.Y * coordmultiplier + DYp) / coordmultiplier;
                    if (Angle != 0)
                    {
                        double nX = XS * CA - YS * SA;
                        double nY = XS * SA + YS * CA;
                        XS = nX;
                        YS = nY;

                        double neX = XE * CA - YE * SA;
                        double neY = XE * SA + YE * CA;
                        XE = neX;
                        YE = neY;
                    }
                    XS = (XS * coordmultiplier + DX) / coordmultiplier;
                    YS = (YS * coordmultiplier + DY) / coordmultiplier;
                    XE = (XE * coordmultiplier + DX) / coordmultiplier;
                    YE = (YE * coordmultiplier + DY) / coordmultiplier;

                    lines.Add(string.Format("X{0}Y{1}G85X{2}Y{3}", GNF.Format(XS), GNF.Format(YS).Replace(',', '.'), GNF.Format(XE), GNF.Format(YE).Replace(',', '.')));
                }
            }
            lines.Add("M30");
            Gerber.WriteAllLines(filename, lines);
        }
示例#2
0
        public static bool SaveDebugImage(string GerberFilename, string BitmapFilename, float dpi, Color Foreground, Color Background)
        {
            ParsedGerber      PLS;
            GerberParserState State = new GerberParserState()
            {
                PreCombinePolygons = false
            };

            var FileType = Gerber.FindFileType(GerberFilename);

            Gerber.DetermineBoardSideAndLayer(GerberFilename, out State.Side, out State.Layer);
            bool forcezero = false;

            if (State.Layer == BoardLayer.Outline)
            {
                //    PLS.PreCombinePolygons = true;

                //    forcezero = true;
            }
            if (FileType == BoardFileType.Drill)
            {
                PLS = PolyLineSet.LoadExcellonDrillFile(GerberFilename);
            }
            else
            {
                PLS = PolyLineSet.LoadGerberFile(GerberFilename, forcezero, Gerber.WriteSanitized, State);
            }
            double WidthInMM  = PLS.BoundingBox.BottomRight.X - PLS.BoundingBox.TopLeft.X;
            double HeightInMM = PLS.BoundingBox.BottomRight.Y - PLS.BoundingBox.TopLeft.Y;
            int    Width      = (int)(Math.Ceiling((WidthInMM) * (dpi / 25.4)));
            int    Height     = (int)(Math.Ceiling((HeightInMM) * (dpi / 25.4)));

            Console.WriteLine("Progress: Exporting {0} ({2},{3}mm) to {1} ({4},{5})", GerberFilename, BitmapFilename, WidthInMM, HeightInMM, Width, Height);
            GerberImageCreator GIC = new GerberImageCreator();

            GIC.scale = dpi / 25.4f; // dpi
            GIC.BoundingBox.AddBox(PLS.BoundingBox);

            var Tr = GIC.BuildMatrix(Width, Height);

            Bitmap B2 = GIC.RenderToBitmap(Width, Height, Tr, Foreground, Background, PLS, true);

            if (B2 == null)
            {
                return(false);
            }

            var    GerberLines = PolyLineSet.SanitizeInputLines(System.IO.File.ReadAllLines(GerberFilename).ToList());
            double LastX       = 0;
            double LastY       = 0;



            Graphics G2 = Graphics.FromImage(B2);

            GerberImageCreator.ApplyAASettings(G2);
            //G2.Clear(Background);
            G2.Transform = Tr.Clone();



            foreach (var L in GerberLines)
            {
                if (L[0] != '%')
                {
                    GerberSplitter GS = new GerberSplitter();
                    GS.Split(L, PLS.State.CoordinateFormat);

                    if (GS.Has("G") && (int)GS.Get("G") == 3)
                    {
                        double X = PLS.State.CoordinateFormat.ScaleFileToMM(GS.Get("X"));
                        double Y = PLS.State.CoordinateFormat.ScaleFileToMM(GS.Get("Y"));

                        double I = PLS.State.CoordinateFormat.ScaleFileToMM(GS.Get("I"));
                        double J = PLS.State.CoordinateFormat.ScaleFileToMM(GS.Get("J"));

                        //Console.WriteLine("Counterclockwise Curve {0},{1} -> {2},{3}", LastX, LastY, X, Y);
                        DrawCross(G2, X, Y, Color.Blue);
                        DrawCross(G2, LastX, LastY, Color.Red);

                        DrawCross(G2, LastX + I, LastY - J, Color.Yellow);
                        DrawCross(G2, LastX + I, LastY + J, Color.Purple);
                        DrawCross(G2, LastX - I, LastY - J, Color.Green);
                        DrawCross(G2, LastX - I, LastY + J, Color.Orange);
                    }

                    if (GS.Has("X"))
                    {
                        LastX = PLS.State.CoordinateFormat.ScaleFileToMM(GS.Get("X"));
                    }
                    if (GS.Has("Y"))
                    {
                        LastY = PLS.State.CoordinateFormat.ScaleFileToMM(GS.Get("Y"));
                    }
                }
            }


            B2.Save(BitmapFilename);
            return(true);
        }
示例#3
0
        public static void ZipGerbers(string a, bool frame, int framemm, string framename)
        {
            string BaseFolder = Path.GetDirectoryName(a);
            string Name       = Path.GetFileNameWithoutExtension(a);

            string GerbersFolder = Path.Combine(BaseFolder, "Gerbers");

            string BoardGerbersFolder = Path.Combine(GerbersFolder, Name);

            string BoardGerbersMergedFolder = Path.Combine(GerbersFolder, Name + "_MERGED");

            string FactoryFolder = Path.Combine(BaseFolder, "Factory");

            if (Directory.Exists(FactoryFolder) == false)
            {
                Directory.CreateDirectory(FactoryFolder);
            }

            string BoardFactoryFolder = Path.Combine(FactoryFolder, Name);


            if (Directory.Exists(BoardFactoryFolder) == false)
            {
                Directory.CreateDirectory(BoardFactoryFolder);
            }
            Gerber.ZipGerberFolderToFactoryFolder(MakeJLCName(Name), BoardGerbersFolder, BoardFactoryFolder);
            Gerber.ZipGerberFolderToFactoryFolder(MakeJLCName(Name) + "_MERGED", BoardGerbersMergedFolder, BoardFactoryFolder);

            if (frame)
            {
                string FrameFactoryFolder = Path.Combine(FactoryFolder, Name + "_Frame");
                if (Directory.Exists(FrameFactoryFolder) == false)
                {
                    Directory.CreateDirectory(FrameFactoryFolder);
                }

                string MergedFrameFactoryFolder = Path.Combine(FactoryFolder, Name + "_Framed");
                if (Directory.Exists(MergedFrameFactoryFolder) == false)
                {
                    Directory.CreateDirectory(MergedFrameFactoryFolder);
                }

                string MergedFrameFactoryFolderMerged = Path.Combine(FactoryFolder, Name + "_MERGED_Framed");
                if (Directory.Exists(MergedFrameFactoryFolderMerged) == false)
                {
                    Directory.CreateDirectory(MergedFrameFactoryFolderMerged);
                }

                GerberFrameWriter.FrameSettings FS = new GerberFrameWriter.FrameSettings();
                FS.FrameTitle           = framename;
                FS.RenderSample         = false;
                FS.margin               = 3;
                FS.roundedOuterCorners  = 0;
                FS.topEdge              = FS.leftEdge = framemm;
                FS.RenderDirectionArrow = true;
                FS.DirectionArrowSide   = GerberLibrary.Core.BoardSide.Both;
                FS.DefaultFiducials     = true;
                FS.FiducialSide         = BoardSide.Both;
                FS.HorizontalTabs       = true;
                FS.VerticalTabs         = true;
                FS.InsideEdgeMode       = GerberFrameWriter.FrameSettings.InsideMode.FormFitting;

                PolyLine PL = Gerber.FindAndLoadOutlineFile(BoardGerbersFolder);
                if (PL != null)
                {
                    FS.PositionAround(PL);
                    GerberFrameWriter.WriteSideEdgeFrame(PL, FS, FrameFactoryFolder + "\\" + Name);
                    GerberFrameWriter.MergeFrameIntoGerberSet(FrameFactoryFolder, BoardGerbersFolder, MergedFrameFactoryFolder, FS, new StandardConsoleLog(), Name);
                    GerberFrameWriter.MergeFrameIntoGerberSet(FrameFactoryFolder, BoardGerbersMergedFolder, MergedFrameFactoryFolderMerged, FS, new StandardConsoleLog(), Name + "Merged");

                    Gerber.ZipGerberFolderToFactoryFolder(MakeJLCName(Name) + "_FRAMED", MergedFrameFactoryFolder, BoardFactoryFolder);
                    Gerber.ZipGerberFolderToFactoryFolder(MakeJLCName(Name) + "_MERGED_FRAMED", MergedFrameFactoryFolderMerged, BoardFactoryFolder);
                }
            }
        }
示例#4
0
        public static string WriteMacroPartVertices(List <PointD> Vertices, GerberNumberFormat format)
        {
            string res = "";

            res += String.Format("4,1,{0}," + Gerber.LineEnding, (Vertices.Count - 2));
            for (int i = 0; i < Vertices.Count - 1; i++)
            {
                res += String.Format("{0},{1}," + Gerber.LineEnding, Gerber.ToFloatingPointString(format._ScaleMMToFile(Vertices[i].X)).Replace(',', '.'), Gerber.ToFloatingPointString(format._ScaleMMToFile(Vertices[i].Y)).Replace(',', '.'));
            }
            res += "0*";
            return(res);
        }
示例#5
0
        public static string BuildOutlineApertureMacro(string name, List <PointD> Vertices, GerberNumberFormat format)
        {
            string res = "%AM" + name + "*" + Gerber.LineEnding;

            res += String.Format("4,1,{0}," + Gerber.LineEnding, (Vertices.Count - 2));
            for (int i = 0; i < Vertices.Count - 1; i++)
            {
                res += String.Format("{0},{1}," + Gerber.LineEnding, Gerber.ToFloatingPointString(format._ScaleMMToFile(Vertices[i].X)).Replace(',', '.'), Gerber.ToFloatingPointString(format._ScaleMMToFile(Vertices[i].Y)).Replace(',', '.'));
            }

            res += "0*" + Gerber.LineEnding + "%" + Gerber.LineEnding;
            return(res);
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="sourcefile"></param>
        /// <param name="destfile"></param>
        /// <param name="DX">MM</param>
        /// <param name="DY">MM</param>
        /// <param name="Angle">Degrees</param>
        public static void Transform(ProgressLog log, string sourcefile, string destfile, double DX, double DY, double DXp, double DYp, double AngleInDeg = 0)
        {
            log.PushActivity("Gerber Transform");
            List <String> lines    = new List <string>();
            List <String> outlines = new List <string>();


            bool   WriteMove    = false;
            int    moveswritten = 0;
            double Angle        = AngleInDeg * (Math.PI * 2.0) / 360.0;
            double CA           = Math.Cos(Angle);
            double SA           = Math.Sin(Angle);

            using (StreamReader sr = new StreamReader(sourcefile))
            {
                while (sr.EndOfStream == false)
                {
                    String line = sr.ReadLine();
                    if (line.Length > 0)
                    {
                        lines.Add(line);
                    }
                }
            }
            lines = PolyLineSet.SanitizeInputLines(lines);

            if (Gerber.WriteSanitized)
            {
                Gerber.WriteAllLines(sourcefile + ".sanitized.txt", lines);
            }
            //   PolyLineSet Parsed = new PolyLineSet("parsed gerber");
            ParsedGerber Parsed = PolyLineSet.ParseGerber274x(log, lines, true, false, new GerberParserState()
            {
                GenerateGeometry = false
            });

            if (Gerber.ShowProgress)
            {
                log.AddString("found apertures: ");
                foreach (var a in Parsed.State.Apertures)
                {
                    log.AddString(a.Value.ToString());
                }
            }



            GerberNumberFormat CoordinateFormat = new GerberNumberFormat();

            CoordinateFormat.SetImperialMode();
            //   CoordinateFormat = Parsed.State.CoordinateFormat;

            int  cur          = 0;
            bool formatparsed = false;

            while (cur < lines.Count && formatparsed == false)
            {
                if (lines[cur].Length >= 2 && lines[cur].Substring(0, 2) == "%F")
                {
                    CoordinateFormat.Parse(lines[cur]);
                    formatparsed = true;
                }
                cur++;
            }

            //  double coordmultiplier = 1.0;
            double LastX = 0;
            double LastY = 0;

            for (int i = 0; i < lines.Count; i++)
            {
                GerberSplitter GS        = new GerberSplitter();
                string         FinalLine = lines[i].Replace("%", "").Replace("*", "").Trim();

                bool DumpToOutput = false;
                bool metaopen     = false;
                if (lines[i][0] == '%')
                {
                    DumpToOutput = true;
                    metaopen     = true;
                }
                else
                {
                    GS.Split(lines[i], CoordinateFormat);
                }


                switch (FinalLine)
                {
                case "G71": CoordinateFormat.SetMetricMode(); break;

                case "G70": CoordinateFormat.SetImperialMode(); break;

                case "MOIN":
                {
                    CoordinateFormat.SetImperialMode();
                    //CoordinateFormat.Multiplier  = 25.4f;
                }
                break;

                case "MOMM":
                {
                    CoordinateFormat.SetMetricMode();
                    //CoordinateFormat.Multiplier = 1.0f;
                }
                break;
                }
                if (lines[i].Length > 3 && lines[i].Substring(0, 3) == "%AM")
                {
                    string name = lines[i].Substring(3).Split('*')[0];

                    var M = Parsed.State.ApertureMacros[name];
                    M.Written = true;
                    var gerb = M.BuildGerber(CoordinateFormat, AngleInDeg).Split('\n');
                    foreach (var l in gerb)
                    {
                        if (l.Trim().Length > 0)
                        {
                            outlines.Add(l.Trim());
                        }
                    }
                    //    outlines.Add(lines[i]);
                    //  if (lines[i][lines[i].Length - 1] != '%')
                    ///  {
                    //   i++;
                    while (lines[i][lines[i].Length - 1] != '%')
                    {
                        //     outlines.Add(lines[i]);
                        i++;
                    }
//                       outlines.Add(lines[i]);
                    //     }
                }
                else

                if (lines[i].Length > 3 && lines[i].Substring(0, 3) == "%AD")
                {
                    GCodeCommand GCC = new GCodeCommand();
                    GCC.Decode(lines[i], CoordinateFormat);
                    if (GCC.numbercommands.Count < 1)
                    {
                        log.AddString(String.Format("Skipping bad aperture definition: {0}", lines[i]));
                    }
                    else
                    {
                        int ATID     = (int)GCC.numbercommands[0];
                        var Aperture = Parsed.State.Apertures[ATID];
                        if (Gerber.ShowProgress)
                        {
                            log.AddString(String.Format("found " + Aperture.ToString()));
                        }
                        string gerb = Aperture.BuildGerber(CoordinateFormat, AngleInDeg);

                        if ((Aperture.ShapeType == GerberApertureShape.Compound || Aperture.ShapeType == GerberApertureShape.Macro) && Parsed.State.ApertureMacros[Aperture.MacroName].Written == false)
                        {
                            log.AddString(String.Format("Macro type defined - skipping"));
                        }
                        else
                        {
                            outlines.Add(gerb);
                        }

                        //                   outlines.Add(lines[i]);
                        if (lines[i][lines[i].Length - 1] != '%')
                        {
                            i++;
                            while (lines[i] != "%")
                            {
                                //                         outlines.Add(lines[i]);
                                i++;
                            }
                            //                   outlines.Add(lines[i]);
                        }
                    }
                }
                else
                {
                    bool PureD = false;
                    if (GS.Has("G"))
                    {
                        int GCode = (int)GS.Get("G");
                        switch (GCode)
                        {
                        case 4: DumpToOutput = true; break;

                        case 90: CoordinateFormat.Relativemode = false; break;

                        case 91: CoordinateFormat.Relativemode = true; break;

                        case 71: CoordinateFormat.Multiplier = 1.0d; break;

                        case 70: CoordinateFormat.Multiplier = 25.4d; break;
                        }
                    }
                    if (DumpToOutput)
                    {
                        outlines.Add(lines[i]);
                        if (lines[i].Contains("LNData"))
                        {
                            log.AddString(String.Format(" heh"));
                        }
                        if (lines[i][0] == '%')
                        {
                            int starti = i;
                            if (lines[i].Length == 1)
                            {
                                i++;
                            }
                            while (lines[i][lines[i].Length - 1] != '%')
                            {
                                if (i > starti)
                                {
                                    outlines.Add(lines[i]);
                                }
                                i++;
                            }
                            if (i > starti)
                            {
                                outlines.Add(lines[i]);
                            }
                        }
                    }
                    else
                    {
                        bool translate = true;
                        if (CoordinateFormat.Relativemode)
                        {
                            translate = false;
                        }
                        if (GS.Has("X") == false && GS.Has("Y") == false && (GS.Has("D") && GS.Get("D") < 10))
                        {
                            PureD = true;
                            int Dcode = (int)GS.Get("D");
                            if (Dcode == 1 || Dcode == 3)
                            {
                                if (moveswritten == 0)
                                {
                                    WriteMove = true;
                                }
                            }
                            moveswritten++;
                            log.AddString(String.Format("Pure D Code: {0}", lines[i]));
                        }
                        else
                        if (GS.Has("X") || GS.Has("Y") || (GS.Has("D") && GS.Get("D") < 10))
                        {
                            int Dcode = (int)GS.Get("D");
                            if (Dcode == 1 || Dcode == 3)
                            {
                                if (moveswritten == 0)
                                {
                                    WriteMove = true;
                                }
                            }
                            moveswritten++;
                            double X = LastX;
                            if (GS.Has("X"))
                            {
                                X = GS.Get("X");
                            }
                            double Y = LastY;
                            if (GS.Has("Y"))
                            {
                                Y = GS.Get("Y");
                            }
                            LastX = X;
                            LastY = Y;
                            GetTransformedCoord(DX, DY, DXp, DYp, Angle, CA, SA, CoordinateFormat, translate, ref X, ref Y);
                            if ((GS.Has("I") || GS.Has("J")) && Angle != 0)
                            {
                                //   int g = (int)GS.Get("G");
                                //  if (g == 2 || g == 3)
                                {
                                    double I   = 0;
                                    double J   = 0;
                                    bool   arc = false;
                                    if (GS.Has("I"))
                                    {
                                        I = GS.Get("I"); arc = true;
                                    }
                                    ;
                                    if (GS.Has("J"))
                                    {
                                        J = GS.Get("J"); arc = true;
                                    }
                                    ;
                                    if (arc)
                                    {
                                        double nJ = J * CA + I * SA;
                                        double nI = -J * SA + I * CA;
                                        I = nI;
                                        J = nJ;
                                        //  GS.Set("I", Math.Abs(I));
                                        //  GS.Set("J", Math.Abs(J));
                                        GS.Set("I", I);
                                        GS.Set("J", J);
                                    }
                                }
                            }
                            GS.Set("X", X);
                            GS.Set("Y", Y);
                        }

                        if (WriteMove)
                        {
                            GerberSplitter GS2 = new GerberSplitter();
                            GS2.Set("D", 2);
                            double X0 = 0;
                            double Y0 = 0;
                            GetTransformedCoord(DX, DY, DXp, DYp, Angle, CA, SA, CoordinateFormat, translate, ref X0, ref Y0);
                            GS2.Set("X", X0);
                            GS2.Set("Y", Y0);
                            WriteMove = false;
                            outlines.Add(GS2.Rebuild(CoordinateFormat));
                        }
                        outlines.Add(GS.Rebuild(CoordinateFormat));
                        if (PureD)
                        {
                            log.AddString(String.Format("pureD"));
                        }
                    }
                }
            }
            try
            {
                List <String> PostProcLines = new List <string>();
                foreach (var a in outlines)
                {
                    if (a == "%")
                    {
                        PostProcLines[PostProcLines.Count - 1] += "%";
                    }
                    else
                    {
                        PostProcLines.Add(a);
                    }
                }
                Gerber.WriteAllLines(destfile, PolyLineSet.SanitizeInputLines(PostProcLines));
            }
            catch (Exception E)
            {
                log.AddString(String.Format(E.Message));
            }
            log.PopActivity();
        }
示例#7
0
        private List <PointD> CutCompensation(List <PointD> path, CutterCompensation compensation, double offset)
        {
            if (compensation == CutterCompensation.None)
            {
                return(path);
            }

            if (path.Count < 2)
            {
                return(path);
            }

            /* remove contiguous duplicates */
            var    unique = new List <PointD>(path.Count);
            PointD prev   = null;

            foreach (var point in path)
            {
                if (prev == point)
                {
                    continue;
                }

                prev = point;
                unique.Add(point);
            }
            path = unique;

            /* create offset segments */
            var SegmentsOffset = path.Zip(path.Skip(1), (A, B) =>
            {
                var angle = A.Angle(B);

                if (compensation == CutterCompensation.Left)
                {
                    angle += Math.PI / 2;
                }
                else
                {
                    angle -= Math.PI / 2;
                }

                A += new PointD(offset * Math.Cos(angle), offset * Math.Sin(angle));
                B += new PointD(offset * Math.Cos(angle), offset * Math.Sin(angle));

                return(new { A, B });
            });

            /* create segment pairs */
            var SegmentPairs = SegmentsOffset
                               .Zip(SegmentsOffset.Skip(1), (First, Second) => new { First, Second })
                               .Zip(path.Skip(1), (pair, Center) => new { pair.First, pair.Second, Center });

            var Path = new PolyLine();

            Path.Vertices.Add(SegmentsOffset.First().A);

            foreach (var segment in SegmentPairs)
            {
                /* segments are colinear */
                if (segment.First.B == segment.Second.A)
                {
                    continue;
                }

                var intersection = Helpers.SegmentSegmentIntersect(segment.First.A, segment.First.B, segment.Second.A, segment.Second.B);
                /* if segments intersect, */
                if (intersection != null)
                {
                    /* the intersection point is what connects first and second segments */
                    Path.Vertices.Add(intersection);
                }
                else
                {
                    /* otherwise connect segments with an arc */
                    var Center = segment.Center - segment.First.B;

                    var arc = Gerber.CreateCurvePoints(
                        segment.First.B.X, segment.First.B.Y,
                        segment.Second.A.X, segment.Second.A.Y,
                        Center.X, Center.Y,
                        compensation == CutterCompensation.Left ? InterpolationMode.ClockWise : InterpolationMode.CounterClockwise,
                        GerberQuadrantMode.Multi);

                    Path.Vertices.AddRange(arc);
                }
            }

            Path.Vertices.Add(SegmentsOffset.Last().B);

            return(Path.Vertices);
        }
示例#8
0
        public static void MergeFrameIntoGerberSet(string FrameFolder, string OutlineFolder, string OutputFolder, FrameSettings FS, ProgressLog log, string basename)
        {
            log.PushActivity("MergeFrame");
            // log.AddString(".....");
            if (Directory.Exists(FrameFolder) == false)
            {
                log.AddString(String.Format("Framefolder {0} does not exist?", FrameFolder));
            }
            if (Directory.Exists(OutlineFolder) == false)
            {
                log.AddString(String.Format("OutlineFolder {0} does not exist?", OutlineFolder));
            }
            if (Directory.Exists(OutputFolder) == false)
            {
                log.AddString(String.Format("OutputFolder {0} does not exist?", OutputFolder));
            }
            GerberPanel PNL = new GerberPanel();

            PNL.AddGerberFolder(log, FrameFolder);
            PNL.AddGerberFolder(log, OutlineFolder);
            PNL.TheSet.ClipToOutlines = false;



            var FrameInstance   = PNL.AddInstance(FrameFolder, new PointD(0, 0));
            var OutlineInstance = PNL.AddInstance(OutlineFolder, new PointD(0, 0));

            PNL.UpdateShape(log);

            var BB = OutlineInstance.BoundingBox;

            foreach (var s in OutlineInstance.TransformedOutlines)
            {
                bool ClockWise = s.ClockWise();

                if (s.Vertices.Count >= 2)
                {
                    for (int i = 0; i < s.Vertices.Count; i++)
                    {
                        PointD p1 = s.Vertices[i];
                        PointD p2 = s.Vertices[(i + 1) % s.Vertices.Count];

                        var D = p2 - p1;
                        if (Math.Abs(D.X) < 0.5 && Math.Abs(D.Y) >= FS.mmbetweentabs && FS.VerticalTabs)
                        {
                            // perfect vertical!
                            log.AddString(String.Format("vertical found: {0} -> {1}", p1, p2));

                            double dy = p2.Y - p1.Y;

                            double x         = 0;
                            double rad       = 0;
                            bool   rightside = (dy > 0);
                            if (ClockWise)
                            {
                                rightside = !rightside;
                            }

                            if (FS.InsideEdgeMode == FrameSettings.InsideMode.RegularEdge)
                            {
                                if (rightside)
                                {
                                    x   = (p1.X + (BB.BottomRight.X + FS.margin)) / 2;
                                    rad = Math.Abs((p1.X - (BB.BottomRight.X + FS.margin))) / 2.0 + FS.margin;
                                }
                                else
                                {
                                    x   = (p1.X + (BB.TopLeft.X - FS.margin)) / 2;
                                    rad = Math.Abs((p1.X - (BB.TopLeft.X - FS.margin))) / 2.0 + FS.margin;
                                }
                            }

                            if (FS.InsideEdgeMode == FrameSettings.InsideMode.FormFitting)
                            {
                                if (rightside)
                                {
                                    x   = p1.X + (FS.margin / 2);
                                    rad = FS.margin;
                                }
                                else
                                {
                                    x   = p1.X - (FS.margin / 2);
                                    rad = FS.margin;
                                }
                            }


                            int tabs = (int)Math.Floor(Math.Abs(dy) / FS.mmbetweentabs);
                            for (int j = 0; j < tabs; j++)
                            {
                                double y  = p1.Y + (dy / (float)tabs) * (j + 0.5);
                                var    BR = PNL.AddTab(new PointD(x, y));
                                log.AddString(String.Format("tab at {0} - radius {1}", BR.Center, rad));
                                BR.Radius = (float)rad;
                            }
                        }

                        if (Math.Abs(D.Y) < 0.5 && Math.Abs(D.X) >= FS.mmbetweentabs && FS.HorizontalTabs)
                        {
                            // perfect vertical!
                            log.AddString(String.Format("horizontal found: {0} -> {1}", p1, p2));

                            double dx = p2.X - p1.X;

                            double y         = 0;
                            double rad       = 0;
                            bool   rightside = (dx < 0);
                            if (ClockWise)
                            {
                                rightside = !rightside;
                            }

                            if (FS.InsideEdgeMode == FrameSettings.InsideMode.RegularEdge)
                            {
                                if (rightside)
                                {
                                    y   = (p1.Y + (BB.BottomRight.Y + FS.margin)) / 2;
                                    rad = Math.Abs((p1.Y - (BB.BottomRight.Y + FS.margin))) / 2.0 + FS.margin;
                                }
                                else
                                {
                                    y   = (p1.Y + (BB.TopLeft.Y - FS.margin)) / 2;
                                    rad = Math.Abs((p1.Y - (BB.TopLeft.Y - FS.margin))) / 2.0 + FS.margin;
                                }
                            }

                            if (FS.InsideEdgeMode == FrameSettings.InsideMode.FormFitting)
                            {
                                if (rightside)
                                {
                                    y   = p1.Y + (FS.margin / 2);
                                    rad = FS.margin;
                                }
                                else
                                {
                                    y   = p1.Y - (FS.margin / 2);
                                    rad = FS.margin;
                                }
                            }


                            int tabs = (int)Math.Floor(Math.Abs(dx) / FS.mmbetweentabs);
                            for (int j = 0; j < tabs; j++)
                            {
                                double x  = p1.X + (dx / (float)tabs) * (j + 0.5);
                                var    BR = PNL.AddTab(new PointD(x, y));
                                log.AddString(String.Format("tab at {0} - radius {1}", BR.Center, rad));
                                BR.Radius = (float)rad;
                            }
                        }
                    }
                }
            }


            PNL.UpdateShape(log);
            log.AddString("postupdateshape");
            try
            {
                Directory.CreateDirectory(OutputFolder);
                var PNLFiles = PNL.SaveGerbersToFolder("MergedFrame", OutputFolder, log, false, true, false, true, basename);
            }
            catch (Exception E)
            {
                log.AddString("save gerbers to folder Exceptions: " + E.ToString());
            }
            try
            {
                if (FS.RenderSample)
                {
                    GerberImageCreator GIC = new GerberImageCreator();
                    GIC.AddBoardsToSet(Directory.GetFiles(OutputFolder).ToList(), new SilentLog());
                    GIC.WriteImageFiles(basename, 200, true, false, true, null);
                }
            }
            catch (Exception E)
            {
                log.AddString("GIC Exceptions: " + E.ToString());
            }

            log.PopActivity();

            //            PNL.SaveOutlineTo(OutputFolder, "mergedframeblended");
            return;



            var           FrameFiles   = Directory.GetFiles(FrameFolder);
            var           OutlineFiles = Directory.GetFiles(OutlineFolder);
            List <String> AllFiles     = new List <string>();

            AllFiles.AddRange(FrameFiles);
            foreach (var a in OutlineFiles)
            {
                BoardLayer layer;
                BoardSide  Side;

                Gerber.DetermineBoardSideAndLayer(a, out Side, out layer);

                if (layer != BoardLayer.Outline)
                {
                    AllFiles.Add(a);
                }
            }

            //  AllFiles.AddRange(OutlineFiles);
            GerberMerger.MergeAllByFileType(AllFiles, OutputFolder, "MergedFrame", log);
        }