#usage "Export milling data for a board layout\n"
       " "
                 "Press button Reference to generate a reference Package to place
"
       "
Distance Copper/Dimension (see DRC) this is the distance between 
"
                 "the polygon and outline of the board. If the value for 'Copper/Dimension' 
"
                 "in the DRC settings is set higher than the radius of the mill tool#2, the 
"
                 "board dimensions will be milled larger by the difference of these two 
"
                 "respective values. For the best result, make sure the value for 
"
                 "Copper/Dimension in DRC Distance is set on 0.01 mm, or use the 
"
                 "same value in the Design Rule's settings as the radius of the milling tool#2 
"
                 "is, before running this ULP!
"
                 "refernece holes on PCB for exact mirroring the bottom side of PCB on milling machine.
"
                 "
" +
                       "REFERENCE circles in Layer Holes.
" +
                       "Place it in the layout for milling and 
" +
                       "mirroring (bottom side) with offset and 
" +
                       "start this ULP again.
";
                       "The position of the circles are defined
" +
                       "in the NC drill data file which comes
" +
                       "from the NC machine (eg. Excellon).
"; // // The various output devices // int defaultdevice = 2; // set default device int SelectedDevice = 0; string Device; enum { devScript = 1, devHPGL }; string DeviceNames[] = { "Select a device", "SCRIPT", "HPGL" }; string DeviceExt[] = { ".$$$" , ".scr", ".plt" }; string DrillExt[] = { ".$$$" , "_drl.scr" , "" }; string DefaultSuffix = DeviceExt[0]; string DrlDefaultSuffix = DrillExt[0]; string DrillLabel = "D&rill file"; string DrBrowse = "Bro&wse"; // // Parmameters // // *** The milling tool diameter *** real MillToolOutl = 0.2; real MillToolIsolate = 0.0; // int OverlapOutlPercent = 20; // percent, to avoid fine copper lines while milling out the area real MillToolFree = 0.8; // Blow up & free pouring int millfreeyes = 1; // free pouring on/off int OverlapRubOut = 20; // percent, to avoid fine copper lines while milling out the area real DrillPad = 0.8; real DrillVia = 0.8; real DrillHole = 0.8; int onlydrill = 0; // output only drill on HPGL int generatedrills = 1; // aktivate drills only in one export file // while drills crashed in 2. run. real DimensionMillTool = 2.0; // milling board outline from holder real Distance_Copper_Dimension = 1.016; // default value real Holder_Spacing = 10.0; // in mm, the distance to make a holder spacing for outline miling. // The HPGL PEN list enum { PadDrill = 1, ViaDrill, Contour, BlowUp_RubOut, HoleDrill, DimensionLine } // HPGL Pen select "SP.." string PenList[]; PenList[PadDrill] = "PadDrill"; PenList[ViaDrill] = "ViaDrill"; PenList[Contour] = "Contour"; PenList[BlowUp_RubOut] = "Blow-Up/Rub-Out"; //PenList[RubOut] = "Rub-Out"; PenList[HoleDrill] = "HoleDrill"; PenList[DimensionLine] = "DimensionLine"; string sToolValue[]; // Tool diameter as String enum { HPGLsolution = 1016 } // Inch/1016 = 0.025 mm real Mirror = 1.0; string ref_pac = "_REFERENCE_HOLE_"; // special package for mirror milling data int mirr_offsetx = 0; int ref_null_offsetX = 0; int ref_null_offsetY = 0; int ref_offsetX[]; int ref_offsetY[]; int ref_cnto = 0; int Layer = 0; string fillstyle = "Interleave"; // 9 Interleave string lOutl; // 1. milling tool string lOutl1; // 2. milling tool 26.07.2006 string lOutl2; // 3. milling tool string trueOutline_coordinate; // the generatet polygon outlines for the milling polygon string OutlinesSignalName = "_OUTLINES_"; // reserved Signal name for special polygon string OutlineMillSignal = "$_OUTLINEMILL_$"; string PassDimensionPoly = "PASSDIMESION"; // the true output line defined by Layer 20 Dimension string Pass2 = "PASS_2"; string PassPour = "PASS_POUR"; string PassOutmill = "PASS_OUTLINE"; string InPassDimensionPoly; string InPass2; string InPassPour; string InPassOutmill; int menu = 0; string showpic[]; string info; string infotext; string path; string File, FileName; string DrFile, DrillFile; int test = 0; // *** Functions *** void viewtest(string txt) { dlgDialog("test") { dlgHBoxLayout { dlgTextEdit(txt); dlgVBoxLayout dlgSpacing(150); } dlgHBoxLayout { dlgPushButton("OK") dlgAccept(); dlgPushButton("Cancel") { dlgReject(); exit(0); } dlgSpacing(400); } }; return; } void Warning(string Message, string Details) { dlgMessageBox("Warning: " + Message + " " + Details, "OK"); return; } void Fatal(string Message, string Details) { dlgMessageBox("ERROR: " + Message + "
\n" + Details); exit(1); } void Error(string Message, string Details) { dlgMessageBox("ERROR: " + Message + "
\n" + Details);
}
if (!board) Fatal("No board!", "This program can only work in the board editor.");
void dirtest(void) {
  string a[];
  int n = fileglob(a, "*.*");
  if (n) {
    dlgMessageBox("Working directory:\n" + filedir(a[0]), "OK");
  }
  return;
}
string vunit(int val, int unit) {
  string s;
  if (unit) sprintf(s, "%.4f", u2mm(val));
  else sprintf(s, "%.6f",u2inch(val));
  return s;
}
string getval(string line, real exf, real refholediameter) {
  int Y = strchr(line, 'Y');
  string s;
  sprintf(s, "(%.4f %.4f) (%.4f %.4f);\n",
                       strtol(strsub(line, 1)) * exf,
                       strtol(strsub(line, Y+1)) * exf,
                       strtol(strsub(line, 1)) * exf + refholediameter/2 * exf,
                       strtol(strsub(line,Y+1)) * exf );
  return s;
}
void setZerroReference(void) {
  int  refunit = 0;
  int  format  = 0;
  string ReferenceUnit[] = { "INCH", "MM", "INCH", "MM" };
  real   exf[]        = { 0.001, 0.0001, 0.001, 0.0001 };
            // Drillformat 2.3    2.4    2.3     2.4
            // Unit        Inch   Inch    MM     MM
  real refhdiameter[] = { 118,   1180,   3000,  30000 };
  int Result = dlgDialog(filename(argv[0]) + " - generate refernece and place on Board") {
    dlgLabel(showpic[17]);
    dlgLabel(infoREFERENCE);
    dlgHBoxLayout {
      dlgGroup("Unit") {
        dlgHBoxLayout {
          dlgRadioButton("INCH", refunit);
          dlgRadioButton("MM", refunit);
          dlgStretch(1);
        }
      }
      dlgStretch(1);
    }
    dlgGroup("Format") {
      dlgHBoxLayout {
        dlgRadioButton("2.3", format);
        dlgRadioButton("2.4", format);
        dlgStretch(1);
      }
    }
    dlgHBoxLayout {
      dlgPushButton("+OK") dlgAccept();
      dlgStretch(1);
      dlgPushButton("-Cancel") dlgReject();
    }
  };
  if (Result) {
    board(B) {
      string usedlibraries = "USE -*;\n";
      int u = 0;
      do {
        if (used_libraries[u]) {
          usedlibraries += "USE '" + used_libraries[u] + "';\n";
          u++;
        }
      } while(used_libraries[u]);
      string used_scr = filedir(B.name) + "used-lbrs-" + filesetext(filename(B.name), ".scr");
      output(used_scr, "wt") {
        printf("%s", usedlibraries);
      }
      dlgMessageBox(" " +
                         "Spacers will be set on vertical & horizontal and in HPGL-Format only. " +
                         "The width of the spacer depends on the diameter of tool#2.";
            }
          }
          dlgVBoxLayout dlgSpacing (400);  // high of Cell
          dlgStretch(0);
          dlgVBoxLayout {
            dlgStretch(0);
            dlgLabel(info, 1);
            dlgStretch(0);
            dlgLabel(infotext, 1);
            dlgStretch(1);
          }
          dlgStretch(1);
        }
        dlgStretch(1);
        dlgHBoxLayout {
          dlgStretch(0);
          dlgLabel("Mill fil&e");
          dlgStretch(0);
          dlgStringEdit(File);
          dlgStretch(0);
          dlgPushButton("&Browse") {
            string fn = dlgFileSave("Save Outlines file", File);
            if (fn) {
              File = fn;
              FileName = fn;
              info = showpic[1];
            }
          }
          dlgStretch(0);
        }
        dlgHBoxLayout {
          dlgStretch(0);
          dlgLabel(DrillLabel);
          dlgStretch(0);
          dlgStringEdit(DrFile);
          dlgStretch(0);
          dlgPushButton(DrBrowse) {
          string fd = dlgFileSave("Save Drill file", DrFile);
          if (fd) {
            DrFile = fd;
            DrillFile = fd;
            info = showpic[3];
          }
        }
        dlgStretch(0);
      }
      dlgStretch(1);
      dlgHBoxLayout dlgSpacing(600);
      dlgHBoxLayout {
        dlgStretch(0);
        dlgPushButton("+OK") {
          if (!SelectedDevice) Error("No device selected!", "Please select a device.");
          else {
            int fault = 0;
            real distanceDimension = DimensionMillTool / 2 - Distance_Copper_Dimension;
            if (DimensionMillTool) {          //  ****  if used ?  ****
              if (distanceDimension < 0) {
                string d;
                sprintf(d, "%.4f", distanceDimension * -1);
                fault = dlgMessageBox(" "
                                      "Check/change Distance Copper/Dimension in Design Rules DRC "
                                      "or accept a distance of " + d +
                                      "mm to board dimension.
" +
                     used_scr +
                    "
to set the used libraries back.", "OK");
      string cmd;
      string ref[];
      string reference_file = dlgFileOpen("Select drill reference file (Holes)", "", "*.ncd\n*.*");
      if (!reference_file) return;
      int unit_format = refunit * 2 + format;
      int    l;
      l = fileread(ref, reference_file);
      string lbrname;
      lbrname = filesetext(B.name, "$$$ref_tmp.lbr");
      sprintf( cmd, "OPEN '%s';\n", lbrname );
      cmd += "Edit '" + ref_pac + ".PAC';\nCHANGE LAYER 45;\nGRID " + ReferenceUnit[refunit] + ";\n";
      for (int n = 0; n <= l; n++) {
        string s = ref[n];
        if(s[0] == 'X') cmd += "CIRCLE 0 " + getval(ref[n], exf[unit_format], refhdiameter[unit_format]);
      }
      cmd += "WRITE;\n";
      cmd += "EDIT '" + B.name + "';\n";
      cmd += "DISPLAY 45;\n";
      cmd += "GRID " + ReferenceUnit[refunit] + ";\n";
      cmd += "WIN FIT; WIN (" +
              vunit((B.area.x1 + B.area.x2 )/2, refunit) + " "   +
              vunit((B.area.y1 + B.area.y2 )/2, refunit) + ") (" +
              vunit((B.area.x1 + B.area.x2 )/2 + 1000, refunit) + " "   +
              vunit((B.area.y1 + B.area.y2 )/2 + 1000, refunit) + ") (" +
              vunit((B.area.x1 + B.area.x2 )/2 + 150 , refunit) + " "   +
              vunit((B.area.y1 + B.area.y2 )/2 + 150 , refunit) + ");\n",
      cmd += "USE -*; USE '" + lbrname + "';\n";
      cmd += "ADD " + ref_pac + "\n";
      if (test) viewtest(cmd);
      exit(cmd);
    }
  }
  return;
}
// *** Tools in EXCELLON and GERBER (mm)
void toolFiles(void) {
  board(B) {
    output(filesetext(B.name, ".rac")) {
      printf("T01  %.1f mm\n", DrillPad);
      printf("T02  %.1f mm\n", DrillVia);
      printf("T03  %.1f mm\n", MillToolOutl);
      printf("T04  %.1f mm\n", MillToolFree);
      printf("T05  %.1f mm\n", DrillHole);
      printf("T06  %.1f mm\n", DimensionMillTool);
    }
    output(filesetext(B.name, ".whl")) {
      printf(";aperture wheel file generated by %s %s\n", EAGLE_SIGNATURE, filename(argv[0]) );
      printf(";remove the above line to prevent this file from being overwritten!\n");
      printf("D11      round    %6.4fmm\n", DrillPad);
      printf("D12      round    %6.4fmm\n", DrillVia);
      printf("D13      round    %6.4fmm\n", MillToolOutl);
      printf("D14      round    %6.4fmm\n", MillToolFree);
      printf("D15      round    %6.4fmm\n", DrillHole);
      printf("D16      round    %6.4fmm\n", DimensionMillTool);
    }
  }
  return;
}
void showRackFile(void) {
  board(B) {
    string text;
    int nChars = fileread(text, filesetext(B.name, ".rac"));
    dlgDialog("Show rack file") {
      dlgTextView(text);
      dlgHBoxLayout {
        dlgPushButton("+&OK") dlgAccept();
        dlgStretch(1);
      }
    };
  }
  return;
}
void showHPGLinfo(void) {
  string text;
  int nChars = fileread(text, filesetext(FileName, ".pli"));
  dlgDialog("Show plot info") {
    dlgTextView(text);
    dlgHBoxLayout {
      dlgPushButton("+&OK") dlgAccept();
      dlgStretch(1);
      dlgSpacing(200);
   }
  };
  return;
}
void ValueInit(void) {
  sprintf( sToolValue[PadDrill], "%.2f", DrillPad);
  sprintf( sToolValue[ViaDrill], "%.2f", DrillVia);
  sprintf( sToolValue[Contour], "%.2f", MillToolOutl);
  sprintf( sToolValue[BlowUp_RubOut], "%.2f", MillToolFree);
  sprintf( sToolValue[HoleDrill], "%.2f", DrillHole);
  sprintf( sToolValue[DimensionLine], "%.2f", DimensionMillTool);
  return;
}
void  startlineprint( int x1, int y1, int x2, int y2) {
  printf("\nPA%.0f,%.0f;PD;\nPA%.0f,%.0f;", Mirror * u2inch(x1 + mirr_offsetx)*HPGLsolution, u2inch(y1 + ref_null_offsetY)*HPGLsolution, Mirror * u2inch(x2 + mirr_offsetx)*HPGLsolution, u2inch(y2 + ref_null_offsetY)*HPGLsolution);
  return;
}
void nextlineprint( int x2, int y2) {
  printf("\nPA%.0f,%.0f;", Mirror * u2inch(x2 + mirr_offsetx)*HPGLsolution, u2inch(y2 + ref_null_offsetY)*HPGLsolution );
  return;
}
void endlineprint( int x2, int y2) {
  printf("\nPA%.0f,%.0f;PU;", Mirror * u2inch(x2 + mirr_offsetx)*HPGLsolution, u2inch(y2 + ref_null_offsetY)*HPGLsolution);
  return;
}
void fullineprint( int x1, int y1, int x2, int y2) {
  printf("\nPA%.0f,%.0f;PD;\nPA%.0f,%.0f;PU;", Mirror * u2inch(x1 + mirr_offsetx)*HPGLsolution, u2inch(y1 + ref_null_offsetY)*HPGLsolution, Mirror * u2inch(x2 + mirr_offsetx)*HPGLsolution, u2inch(y2 + ref_null_offsetY)*HPGLsolution );
  return;
}
real WireLength(real x1,real x2,real y1,real y2) {
  real WL = sqrt(pow(x2-x1,2) + pow(y2-y1,2));  // calculate Wire length WL
  return WL;
}
// if the wire longer as longdistance and vertical or horizontal
// then make a holder spacing
void  checkBridge(int x1, int y1, int x2, int y2, int state) {
  real WL = WireLength(u2mm(x2),u2mm(x1),u2mm(y2),u2mm(y1));
  if (WL >= Holder_Spacing && (x1 == x2 || y1 == y2) ) {
    int bridgewidth = DimensionMillTool * 10000;
    int xa, xb, ya, yb;
    switch (state) {
      case 0:
              startlineprint(x1, y1, x2, y2);
              break;
      case 1:
              if (x2 > x1 && x2 >= 0) {
                xa = x2 - (2.5 * bridgewidth);
                xb = x2 - bridgewidth;
              }
              if (x2 < x1 && x2 >= 0) {
                xa = x2 + (2.5 * bridgewidth);
                xb = x2 + bridgewidth;
              }
              if (x2 > x1 && x2 < 0) {
                xa = x2 - (2.5 * bridgewidth);
                xb = x2 - bridgewidth;
              }
              if (x2 < x1 && x2 < 0) {
                xa = x2 + (2.5 * bridgewidth);
                xb = x2 + bridgewidth;
              }
              if (y2 > y1 && y2 >= 0 ) {
                ya = y2 - (2.5 * bridgewidth);
                yb = y2 - bridgewidth;
              }
              if (y2 < y1 && y2 >= 0) {
                ya = y2 + (2.5 * bridgewidth);
                yb = y2 + bridgewidth;
              }
              if (y2 > y1 && y2 < 0 ) {
                ya = y2 - (2.5 * bridgewidth);
                yb = y2 - bridgewidth;
              }
              if (y2 < y1 && y2 < 0) {
                ya = y2 + (2.5 * bridgewidth);
                yb = y2 + bridgewidth;
              }
              if (x1 == x2) {
                endlineprint(x1, ya);
                startlineprint(x1, yb, x1, y2);
              }
              else {
                endlineprint(xa, y1);
                startlineprint(xb, y1, x2, y2);
              }
              break;
      case 2:
              endlineprint(x2, y2);
              break;
    }
    return;
  }
  else {
    switch (state) {
      case 0:
              startlineprint(x1, y1, x2, y2);
              break;
      case 1:
              nextlineprint(x2, y2);
              break;
      case 2:
              endlineprint(x2, y2);
              break;
    }
  return;
  }
}
void DeviceDraw(int x1, int y1, int x2, int y2, int state) {
  // Actually draw a line on the output device.
  // 'state' is defined as
  // 0 = this is the first line of a partial polygon
  // 1 = this is a "normal" line (neither the first nor the last one)
  // 2 = this is the last line of a partial polygon
  // 3 = this is a drill coordinate
  // 4 = this is one line
  switch (SelectedDevice) {
    case devScript:
      switch (state) {
        case 0:
                printf("WIRE (%.4f %.4f) (%.4f %.4f)\n", Mirror * u2mm(x1 + mirr_offsetx), u2mm(y1 + ref_null_offsetY), Mirror * u2mm(x2 + mirr_offsetx) , u2mm(y2 + ref_null_offsetY));
                break;
        case 1:
                printf("(%.4f %.4f)\n", Mirror * u2mm(x2 + mirr_offsetx), u2mm(y2 + ref_null_offsetY));
                break;
        case 2:
                printf("(%.4f %.4f);\n", Mirror * u2mm(x2 + mirr_offsetx), u2mm(y2 + ref_null_offsetY));
                break;
        case 3:
                printf("HOLE (%.4f %.4f);\n", Mirror * u2mm(x2 + mirr_offsetx), u2mm(y2 + ref_null_offsetY));
                break;
        case 4:
                printf("WIRE (%.4f %.4f) (%.4f %.4f)\n", Mirror * u2mm(x1 + mirr_offsetx), u2mm(y1 + ref_null_offsetY), Mirror * u2mm(x2 + mirr_offsetx), u2mm(y2 + ref_null_offsetY));
                break;
      }
      break;
    case devHPGL:
      switch (state) {
        case 0:
                if (InPassOutmill) checkBridge(x1, y1, x2, y2, state);
                else startlineprint(x1, y1, x2, y2);
                break;
        case 1:
                if (InPassOutmill) checkBridge(x1, y1, x2, y2, state);
                else nextlineprint(x2, y2);
                break;
        case 2:
                if (InPassOutmill) checkBridge(x1, y1, x2, y2, state);
                else endlineprint(x2, y2);
                break;
        case 3:      // drilling tool
                printf("\nPA%.0f,%.0f;PD;", Mirror * u2inch(x2 + mirr_offsetx)*HPGLsolution, u2inch(y2 + ref_null_offsetY)*HPGLsolution);
                printf("\nPA%.0f,%.0f;PU;", Mirror * u2inch(x2 + mirr_offsetx)*HPGLsolution, u2inch(y2 + ref_null_offsetY)*HPGLsolution);
                break;
        case 4:      // polygon filling
                fullineprint(x1, y1, x2, y2);
                break;
    }
    break;
  }
  return;
}
void CircleDraw(int centerx, int centery, int diam, int drilltool) {
  switch (SelectedDevice) {
    case devScript:
                    printf("CHANGE WIDTH %.4f;\n", DrillHole);
                    printf("CIRCLE (%.4f %.4f) (%.4f %.4f);\n", Mirror * u2mm(centerx + mirr_offsetx), u2mm(centery + ref_null_offsetY), Mirror * u2mm((centerx + diam / 2 - drilltool/2)  + mirr_offsetx), u2mm(centery + ref_null_offsetY));
                    break;
    case devHPGL:
                    // for HPGL 1. Start point arc,
                    //          2. Center point arc
                    //          3. angel
                    printf("\nPA%.0f,%.0f;PD;", Mirror * (u2inch((centerx + diam / 2 - drilltool / 2) + mirr_offsetx)) * HPGLsolution, u2inch(centery + ref_null_offsetY) * HPGLsolution);
                    printf("\nAA%.0f,%.0f,360;PU;", Mirror * u2inch(centerx + mirr_offsetx) * HPGLsolution, u2inch(centery + ref_null_offsetY) * HPGLsolution );
                    break;
  }
  return;
}
/*
void ArcDraw(int startarcx, int startarcy, int endarcx, int endarcy, int centerarcx, int centerarcy, real angle) {
  switch (SelectedDevice) {
    case devScript:
                    int rx = 2 * (startarcx - centerarcx);
                    int ry = 2 * (startarcy - centerarcy);
                    if (Mirror == 1) printf("ARC CCW ");
                    else printf("ARC CW ");
                    printf("(%.4f %.4f) (%.4f %.4f) (%.4f %.4f);\n",
                            Mirror * u2mm(startarcx + mirr_offsetx), u2mm(startarcy + ref_null_offsetY),
                            Mirror * u2mm(startarcx - rx + mirr_offsetx), u2mm(startarcy - ry + ref_null_offsetY),
                            Mirror * u2mm(endarcx + mirr_offsetx), u2mm(endarcy + ref_null_offsetY) );
                    break;
    case devHPGL:
                    printf("\nPA%.0f,%.0f;PD;", Mirror * u2inch(startarcx + mirr_offsetx)*HPGLsolution, u2inch(startarcy + ref_null_offsetY)*HPGLsolution);
                    printf("\nAA%.0f,%.0f,%.2f;PU;", Mirror * u2inch(centerarcx + mirr_offsetx)*HPGLsolution, u2inch(centerarcy + ref_null_offsetY)*HPGLsolution, Mirror * angle);
                    break;
  }
  return;
}
*/
void scriptHeader(void) {
  printf("GRID mm;\n");
  printf("SET OPTIMIZING OFF;\nSET UNDO_LOG OFF;\nSET WIRE_BEND 2;\n");
  printf("LAYER %d %s;\nSET FILL_LAYER %d %s;\n",
          Layer + 100, lOutl, Layer + 100, fillstyle);
  // 26.07.2006
  printf("LAYER %d %s;\nSET FILL_LAYER %d %s;\n",
          Layer + 101, lOutl1, Layer + 101, fillstyle);
  printf("LAYER %d %s;\nSET FILL_LAYER %d %s;\n",
          Layer + 102, lOutl2, Layer + 102, fillstyle);
  printf("CHANGE LAYER %d;\n", Layer + 100 );
  return;
}
void DeviceInit(int tool) {
  // Do anything necessary to initialize the output device
  switch (SelectedDevice) {
    case devScript:
                    // TODO make the layer user definable?
                    if (InPassPour) {
                      real overlap = MillToolFree * OverlapOutlPercent / 100;
                      printf("CHANGE WIDTH %.4f;\n", MillToolFree);
                      printf("SET FILL_LAYER %d %s;\n", Layer + 100, fillstyle);
                      printf("CHANGE LAYER %d;\n", Layer + 101);  // 26.07.2006
                    }
                    break;
    case devHPGL:
                    output(filesetext(FileName,".pli"), "at") {
                      // printf("Pen #%d = %s mm\t%s\n", tool, sToolValue[tool], PenList[tool]);
                    }
                    if (tool = Contour) {
                      printf ("IN; IP 0,0,100,100;SC 0,100,0,100;");
                    }
                    break;
  }
  return;
}
void DeviceReInit(int tool) {
  // Do anything necessary the secondary initialize the output device
  switch (SelectedDevice) {
    case devScript:
                    // TODO make the layer user definable?
                    switch (tool) {
                      case PadDrill:
                                          printf("\nCHANGE DRILL %.2f;\n", DrillPad);
                                          break;
                      case ViaDrill:
                                          printf("\nCHANGE DRILL %.2f;\n", DrillVia);
                                          break;
                      case HoleDrill:
                                          printf("\nCHANGE DRILL %.2f;\n", DrillHole );
                                          break;
                      case Contour:
                                          printf("\nCHANGE WIDTH %.4f;\n", MillToolOutl);
                                          break;
                      case BlowUp_RubOut:
                                          printf("\nCHANGE WIDTH %.4f;\n", MillToolFree);
                                          printf("\nchange layer %d;\n", Layer+101);     // 26.07.2006
                                          break;
                      case DimensionLine:
                                          printf("\nCHANGE WIDTH %.4f;\n", DimensionMillTool);
                                          printf("\nCHANGE LAYER %d;\n", Layer+102);     // 26.07.2006
                                          break;
                      default:
                                          break;
                    }
                    break;
      case devHPGL:
                    printf ("\nPU;\nSP%d;\nPA0,0;", tool); // pen select
                    if(test) printf(" # %s", PenList[tool]);
                    output(filesetext(FileName,".pli"), "at") {
                      if (onlydrill && tool == Contour);
                      printf("Pen #%d = %s mm\t%s\n", tool, sToolValue[tool], PenList[tool]);
                    }
                    break;
  }
  return;
}
void DeviceEnd(void) {
  // Do anything necessary to end output to the device
  switch (SelectedDevice) {
    case devScript:
                    break;
    case devHPGL:
                    printf("\nPU;\nSP0;");
                    break;
   }
  return;
}
// TRUE OUTLINE ***
void trueOutlineDraw(string SignalName) {
  board(B) {
    B.signals(S) {
      if (S.name == SignalName) {
        S.polygons(P) {
          int x1 = INT_MAX, y1 = INT_MAX, x2 = INT_MIN, y2 = INT_MIN;
          int x0, y0, first = 1;
          int FrameWire;
          string s;
          P.wires(W) {
            x1 = min(x1, W.x1);
            x2 = max(x2, W.x1);
            y1 = min(y1, W.y1);
            y2 = max(y2, W.y1);
          }
          string lasts;
          P.contours(W) {
            if (first) {
              // a new partial polygon is starting
              x0 = W.x1;
              y0 = W.y1;
              FrameWire = (x1 == x0 || x2 == x0) && (y1 == y0 || y2 == y0);
              sprintf(s, " (%.4f %.4f)", u2mm(W.x1), u2mm(W.y1) );
              trueOutline_coordinate = s;
              lasts = s;
              first = 0;
            }
            else if (W.x2 == x0 && W.y2 == y0) {
              // this was the last wire of the partial polygon,
              // so the next wire (if any) will be the first wire
              // of the next partial polygon
              sprintf(s, " (%.4f %.4f)", u2mm(W.x2), u2mm(W.y2) );
              if (lasts != s) trueOutline_coordinate += s;
              lasts = s;
              first = 1;
            }
            else ;
            if (!FrameWire) {
              sprintf(s, " (%.4f %.4f)", u2mm(W.x2), u2mm(W.y2) );
              if (lasts != s) trueOutline_coordinate += s;
              lasts = s;
            }
          }
        }
      }
    }
    return ;
  }
}
// *** the return string to start rekursiv ***
string RUN_pass(string run_Pass) {
  string s;
  sprintf(s, "RUN '%s' '%s' '%.4f' '%.4f' '%.4f' '%d' '%s' '%s' '%s' '%.1f' '%.2f' '%.2f' '%.2f' '%d' '%d' '%.2f' '%.4f' '%d' '%.4f' '%d' '%d' '%s';\n",
             argv[0], Device, MillToolOutl, MillToolIsolate, MillToolFree, Layer, FileName, DrillFile, run_Pass,
             Mirror, DrillPad, DrillVia, DrillHole, OverlapOutlPercent, OverlapRubOut, Distance_Copper_Dimension,
             DimensionMillTool, millfreeyes, Holder_Spacing, onlydrill, generatedrills, trueOutline_coordinate);
  return s;
}
// place a VIA outside the board with the same signal name
// for used polygon to calculate the filling with orpahns off
// and get no more polygon contours
void generateTruePolygonOutlines() { // the true outlines of board
  board(B) {
    real x1 = u2mm(B.area.x1) - DimensionMillTool, y1 = u2mm(B.area.y1) - DimensionMillTool,
         x2 = u2mm(B.area.x2) + DimensionMillTool, y2 = u2mm(B.area.y2) + DimensionMillTool;
    real distanceDimension = (DimensionMillTool / 2 - Distance_Copper_Dimension) * 2;
    if (distanceDimension < 0) distanceDimension = 0.001;
    string Cmd;
    sprintf(Cmd, "GRID mm FINEST;\n"
                 "CHANGE DRILL 0.3;\nCHANGE DIAMETER 0.5;\nCHANGE SHAPE ROUND;\n"
                 "SET POLYGON_RATSNEST ON;\n"
                 "SET WIRE_BEND 0;\n"
                 "CHANGE RANK 1;\n"           // 26.07.2006  to generate the true outline of dimension
                 "CHANGE POUR SOLID;\n"
                 "CHANGE THERMAL OFF;\n"
                 "CHANGE LAYER %d;\n"
                 "DISPLAY NONE 17 %d;\n",
                  Layer, Layer);
    // make the 1st virtual Net for Normal Polygon Orphens OFF
    // to generate the true outline for place the spacial polygon
    // make a partial WIA for generate Polygon with orphans off
    string millout;
    sprintf(millout, "VIA '%s' (%.4f %.4f);\n", OutlineMillSignal, x1 - Distance_Copper_Dimension - 1, y1 - Distance_Copper_Dimension - 1);
    Cmd += millout;
    sprintf(millout, "CHANGE ISOLATE 0;\n");
    Cmd += millout;
    sprintf(millout, "CHANGE ORPHANS OFF;\n");
    Cmd += millout;
    // Width = MillFree-Diameter/2 minus "Distance Copper/Dimension"
    sprintf(millout, "POLYGON '%s' %.4f (%.4f %.4f) (%.4f %.4f)(%.4f %.4f);\nRATSNEST;\n",
                        OutlineMillSignal, distanceDimension,
                        x1 - DimensionMillTool - Distance_Copper_Dimension - 2,  // 2mm outside
                        y1 - DimensionMillTool - Distance_Copper_Dimension - 2,
                        x2 + DimensionMillTool + Distance_Copper_Dimension + 2,
                        y2 + DimensionMillTool + Distance_Copper_Dimension + 2,
                        x1 - DimensionMillTool - Distance_Copper_Dimension - 2,
                        y1 - DimensionMillTool - Distance_Copper_Dimension - 2);
    Cmd += millout;
    Cmd += "WINDOW FIT;\n";
    Cmd += RUN_pass(PassDimensionPoly);
    if (test) output(filesetext(B.name, "-cmd.txt"), "wt") printf("%s", Cmd);
    exit (Cmd);
  }
}
//
// The actual outlines generator
void GenerateOutlines(void) {
  board(B) {
    trueOutlineDraw(OutlineMillSignal);
    string s;
    real x1 = u2mm(B.area.x1) - MillToolFree, y1 = u2mm(B.area.y1) - MillToolFree,
         x2 = u2mm(B.area.x2) + MillToolFree, y2 = u2mm(B.area.y2) + MillToolFree;
    real distanceDimension = MillToolFree / 2 - Distance_Copper_Dimension;
    if (distanceDimension < 0) distanceDimension = 0.001;
    string Cmd = "SET WIRE_BEND 2;\n";
    //  delete virtual Polygon and VIA
    B.signals(S) {
      if (S.name == OutlineMillSignal) {   // 2008.05.05
        S.vias(V) {
          sprintf( s, "RIPUP (%.4f %.4f);\n",
                   u2mm(V.x), u2mm(V.y) );
          Cmd += s;
          break;
        }
        S.polygons(POL) {
          POL.wires(W) {
            sprintf( s, "DELETE (S%.4f %.4f);\n",
                     u2mm(W.x1), u2mm(W.y1) );
            Cmd += s;
            break;
          }
          break;
        }
      }
    }
    sprintf(s, "CHANGE ISOLATE %.4f;\n", MillToolIsolate);  // 26.07.2006
    Cmd += s;
    sprintf(s, "CHANGE RANK 6;\n");  // 26.07.2006
    Cmd += s;
    sprintf(s, "POLYGOn %s %.4f %s;\nRATSNEST;\n",
                OutlinesSignalName, MillToolOutl, trueOutline_coordinate);
    Cmd += s;
    Cmd += RUN_pass(Pass2);
    if (test) output(filesetext(B.name, "-cmd.txt"), "at") printf("%s", Cmd);
    if (test) viewtest(Cmd);
    exit(Cmd);
  }
}
string WriteOutlines(string SignalName) { // OutlinesSignalName
  board(B) {
    if (InPassPour && MillToolFree == 0) return "";
    if (InPassOutmill && !DimensionMillTool) return "";  // 0 = generate no Dimension with spacing
    string Cmd;
    B.signals(S) {
      if (S.name == SignalName && !onlydrill) {
        S.polygons(P) {
          int x1 = INT_MAX, y1 = INT_MAX, x2 = INT_MIN, y2 = INT_MIN;
          int x0, y0, first = 1;
          int FrameWire;
          int State;
          P.wires(W) {
            x1 = min(x1, W.x1);
            x2 = max(x2, W.x1);
            y1 = min(y1, W.y1);
            y2 = max(y2, W.y1);
          }
          if (InPass2) {
            DeviceReInit(Contour);
          }
          if (InPassPour) DeviceReInit(BlowUp_RubOut);
          if (InPassOutmill) DeviceReInit(DimensionLine);
          P.contours(W) {
            if (first) {
              // a new partial polygon is starting
              x0 = W.x1;
              y0 = W.y1;
              FrameWire = (x1 == x0 || x2 == x0) && (y1 == y0 || y2 == y0);
              State = 0;
              first = 0;
            }
            else if (W.x2 == x0 && W.y2 == y0) {
              // this was the last wire of the partial polygon,
              // so the next wire (if any) will be the first wire
              // of the next partial polygon
              State = 2;
              first = 1;
            }
            else State = 1;
            if (!FrameWire) {
              DeviceDraw(W.x1, W.y1, W.x2, W.y2, State);
            }
          }
          if (InPassPour && millfreeyes) {
            if (SelectedDevice == devScript) {
              printf("# pouring\n");
            }
            DeviceReInit(BlowUp_RubOut);
            State = 4;
            int fx1[], fy1[], fx2[], fy2[];
            int fcnt = 0;
            P.fillings(F) {
              fx1[fcnt] = F.x1;
              fy1[fcnt] = F.y1;
              fx2[fcnt] = F.x2;
              fy2[fcnt] = F.y2;
              fcnt++;
            }
            int diry = fy1[0];
            for (int m = 0; m <= fcnt; m++) {
              if (diry == fy1[m]) {
                DeviceDraw(fx1[m], fy1[m], fx2[m], fy2[m], State);
              }
              else {
                // ****** milling reverse ******
                diry = fy1[m];  // next y_line
                for (int mb = m; mb <= fcnt; mb++) {
                  if (diry == fy1[mb]);
                  else break;
                }
                for(int mback = mb -1 ; mback >= m; mback--) {
                  DeviceDraw(fx2[mback], fy2[mback], fx1[mback], fy1[mback], State);
                  // reverse milling 08.05.2002 alf@cadsoft.de
                }
                m = mb;
                diry = fy1[m];  // next y_line
                m--;
              }
            }
          }
          DeviceEnd();
        }
        break;
      }
    }
    return Cmd;
  }
}
void Pad(UL_PAD P) {
  DeviceDraw(0, 0, P.x, P.y, 3);
}
void Package(UL_PACKAGE P) {
  P.contacts(C) {
    if (C.pad) Pad(C.pad);
  }
  return;
}
void Element(UL_ELEMENT E) {
  Package(E.package);
  return;
}
void PackageHole(UL_PACKAGE P) {
  P.holes(H) {
    if (u2mm(H.drill) > DrillHole) {
      CircleDraw(H.x, H.y, H.drill, DrillHole*10000);  // 2007.09.21 der Fraesweg fuer grosse Bohrungen wurde falsch berechnet
    }
    else DeviceDraw(0, 0, H.x, H.y, 3);
  }
  return;
}
void ElementHole(UL_ELEMENT E) {
  PackageHole(E.package);
  return;
}
void WriteDrills(void) {
  board(B) {
    DeviceReInit(PadDrill);
    B.elements(E) Element(E);
    DeviceReInit(ViaDrill);
    B.signals(S) {
      S.vias(V) {
        DeviceDraw(0, 0, V.x, V.y, 3);
      }
    }
  }
  return;
}
void WriteHoles(void) {
  board(B) {
    DeviceReInit(HoleDrill);
    B.elements(E) ElementHole(E);
    B.holes(H) {
      if (u2mm(H.drill) > DrillHole) {
        CircleDraw(H.x, H.y, H.drill, DrillHole*10000); // internal unit
      }
      else DeviceDraw(0, 0, H.x, H.y, 3);
    }
  }
  return;
}
// *** Pen assaign
void penAssign(void) {
  dlgDialog("Mill outlines: HPGL Pen Assignment") {
    string pen_assign = "\n";
    string t;
    PenList[PadDrill]      = "PadDrill";
    PenList[ViaDrill]      = "ViaDrill";
    PenList[Contour]       = "Contour";
    PenList[BlowUp_RubOut] = "Blow-Up/Rub-Out";
    PenList[HoleDrill]     = "HoleDrill";
    PenList[DimensionLine] = "DimensionLine";
    sprintf(t, "
";
    dlgLabel(pen_assign);
    dlgHBoxLayout {
      dlgStretch(0);
      dlgPushButton("OK") dlgAccept();
      dlgStretch(1);
    }
  };
  return;
}
void selectDevice(void) {
  File = filesetext(FileName, DeviceExt[SelectedDevice]);
  if (SelectedDevice != devHPGL) {
    info = showpic[12 + SelectedDevice];
    DrFile = filesetext(DrillFile, DrillExt[SelectedDevice]);
    DrillLabel = "D&rill file";
    DrBrowse = "Bro&wse";
  }
  else {
    info = showpic[12 + SelectedDevice];
    DrFile ="";
    DrillLabel = "D&rill file";
    DrBrowse = "Bro&wse";
  }
  return;
}
void setMillOffset(void) {
  board(B) {
    path = filedir(B.name);
    B.elements(E) {
      if (E.package.name == ref_pac) {
        ref_offsetX[ref_cnto] = E.x;
        ref_offsetY[ref_cnto] = E.y;
        ref_cnto++;
        E.package.circles(C) {
          if (C.layer == 45) {
            ref_offsetX[ref_cnto] = C.x;
            ref_offsetY[ref_cnto] = C.y;
            ref_cnto++;
          }
        }
        if(ref_offsetX[1] > ref_offsetX[2]) { // if first X > second X then swap
          ref_offsetX[3] = ref_offsetX[2];
          ref_offsetX[2] = ref_offsetX[1];
          ref_offsetX[1] = ref_offsetX[3];
        }
        if (ref_cnto == 3) {
          ref_null_offsetX = ref_offsetX[0] * -1;
          ref_null_offsetY = ref_offsetY[0] * -1;
          if (Mirror == -1) {
            mirr_offsetx = (ref_offsetX[2] + ref_offsetX[1] + ref_null_offsetX) * -1;
          }
          else {
            mirr_offsetx = ref_null_offsetX;
          }
        }
        else {
          ref_cnto = 0;  // more then 2 circles can't use as reference
          if (dlgMessageBox("The reference Hole-Package contains more then 2 Holes\n" +
                           "generate outlines without reference-offset", "accept", "break") != 0) exit(0);
        }
      }
    }
  }
  return;
}
void setblowinfo(void) {
  if (MillToolFree) {
    if (millfreeyes) {            // 14.05.2002 alf
      info = showpic[18];
      infotext = "PadDrill: PEN %d %.1f mm \n", PadDrill, DrillPad);
    pen_assign += t;
    sprintf(t, " ViaDrill: PEN %d %.1f mm \n", ViaDrill, DrillVia);
    pen_assign += t;
    sprintf(t, " Contour: PEN %d %.1f mm \n", Contour, MillToolOutl);
    pen_assign += t;
    sprintf(t, " BlowUp/RubOut: PEN %d %.1f mm \n", BlowUp_RubOut, MillToolFree);
    pen_assign += t;
    sprintf(t, " HoleDrill: PEN %d %.1f mm \n", HoleDrill, DrillHole);
    pen_assign += t;
    sprintf(t, " Mill outline (Dimension): PEN %d %.1f mm \n", DimensionLine, DimensionMillTool);
    pen_assign += t;
    pen_assign += " 
If value set to 0, no Blow-Up (second isolate)
and Rub-Out (free milling) is generated.
Tool diameter for sec. isolate
If value set, Blow-Up (second isolate) is generated. ";
  showpic[1]  = "
";
  showpic[1]  = " ";
  showpic[2]  = "
";
  showpic[2]  = " ";
  showpic[3]  = "
";
  showpic[3]  = " ";
  showpic[4]  = "
";
  showpic[4]  = " ";
  showpic[5]  = "
";
  showpic[5]  = " ";
  showpic[6]  = "
";
  showpic[6]  = " ";
  showpic[7]  = "
";
  showpic[7]  = " ";
  showpic[8]  = "
";
  showpic[8]  = " ";
  showpic[9]  = "
";
  showpic[9]  = " ";
  showpic[10] = "
";
  showpic[10] = " ";
  showpic[11] = "
";
  showpic[11] = " ";
  showpic[12] = "
";
  showpic[12] = " ";
  showpic[13] = "
";
  showpic[13] = " ";
  showpic[14] = "
";
  showpic[14] = " ";
  showpic[15] = "
";
  showpic[15] = " ";
  showpic[16] = "
";
  showpic[16] = " ";
  showpic[17] = "
";
  showpic[17] = " ";
  showpic[18] = "
";
  showpic[18] = " ";
  showpic[19] = "
";
  showpic[19] = " ";
  showpic[20] = "
";
  showpic[20] = " ";
  info = showpic[10];
  infotext = DRCinfo +  usage +  "
";
  info = showpic[10];
  infotext = DRCinfo +  usage +  " ";
  string Layers[];
  int    SelectedLayer = -1;
  int    ForceDialog = (!Device || !MillToolOutl);
  board(B) {
    B.signals(S) {
      if (S.name == OutlinesSignalName)
        Fatal("There is already a signal named " + OutlinesSignalName + " in this board!", "Please make sure that there is no such signal in this board.");
      // *** Check Rank - 26.07.2006 ***
      string s;
      int is_polygon = 0;
      S.polygons(P) {
        is_polygon++;
        if (P.rank == 6) {
          P.contours(W) {
            sprintf(s, "!%s Polygon in Layer %d at (%.4f %.4f)mm has Rank 6.\n\nDo not use Rank 6 while working with this ULP.\n" +
                       "Change the rank to 1..5 before using this ULP again.", S.name, P.layer, u2mm(W.x1), u2mm(W.y1) );
            break;
          }
          dlgMessageBox(s, "OK");
          exit(0);
        }
      }
      if (is_polygon) { // *** 01.08.2006 check if Polygon used in Layout
                        // wenn ein füllendes Polygon im Board plaziert ist, dann muß die Clearance um 0.005mm größer
                        // sein als der Fräser, damit bei Leiterbahnen die nicht im 0° 45° oder 90° Winkel also mit
                        // beliebigen Winkel verlegt sind, keine Füllprobleme wegen Rechenungenauigkeit auftauchen.
        B.signals(S) {
          if (u2mm(S.class.clearance) < MillToolOutl + 0.005) {
            sprintf(s, "!Clearance from CLASS %d %s = %.4f.\nMust be 0.005mm > Tool#1 %.4f", S.class.number, S.class.name, u2mm(S.class.clearance), MillToolOutl);
            dlgMessageBox(s, "OK");
            exit(0);
          }
        }
      }
    }
    int n;
    B.layers(L) {
      if (L.number <= 16 && L.visible) {
        if (Layer == L.number) SelectedLayer = n;
        sprintf(Layers[n++], "%d  %s", L.number, L.name);
      }
    }
    if (n == 0) Fatal("No signal layer active!", "Please activate the signal layer to generate outlines for.");
    if (!Layer) {
      if (n > 1) ForceDialog = 1;
      SelectedLayer = 0;
    }
    if (SelectedLayer < 0) {
      string s;
      sprintf(s, "%d", Layer);
      Fatal("Invalid layer: " + s, "The layer was not found or is not active.");
    }
    if (ForceDialog) {
      SelectedDevice = defaultdevice;
      File = FileName;
      DrFile = DrillFile;
      DrillLabel = "D&rill file";
      selectDevice();
      int mir = 0;
      dlgDialog(Version) {
        dlgHBoxLayout {
          dlgGridLayout {
            dlgCell(0, 0) dlgLabel("&Device");
            dlgCell(0, 1) dlgComboBox(DeviceNames, SelectedDevice) {
              if (SelectedDevice) selectDevice();
              else {
                File = "";
                DrFile = "";
              }
              // 26.07.2006
              if      (SelectedDevice == devHPGL) infotext = "";
              else if (SelectedDevice == devScript) {
              infotext = "
";
  string Layers[];
  int    SelectedLayer = -1;
  int    ForceDialog = (!Device || !MillToolOutl);
  board(B) {
    B.signals(S) {
      if (S.name == OutlinesSignalName)
        Fatal("There is already a signal named " + OutlinesSignalName + " in this board!", "Please make sure that there is no such signal in this board.");
      // *** Check Rank - 26.07.2006 ***
      string s;
      int is_polygon = 0;
      S.polygons(P) {
        is_polygon++;
        if (P.rank == 6) {
          P.contours(W) {
            sprintf(s, "!%s Polygon in Layer %d at (%.4f %.4f)mm has Rank 6.\n\nDo not use Rank 6 while working with this ULP.\n" +
                       "Change the rank to 1..5 before using this ULP again.", S.name, P.layer, u2mm(W.x1), u2mm(W.y1) );
            break;
          }
          dlgMessageBox(s, "OK");
          exit(0);
        }
      }
      if (is_polygon) { // *** 01.08.2006 check if Polygon used in Layout
                        // wenn ein füllendes Polygon im Board plaziert ist, dann muß die Clearance um 0.005mm größer
                        // sein als der Fräser, damit bei Leiterbahnen die nicht im 0° 45° oder 90° Winkel also mit
                        // beliebigen Winkel verlegt sind, keine Füllprobleme wegen Rechenungenauigkeit auftauchen.
        B.signals(S) {
          if (u2mm(S.class.clearance) < MillToolOutl + 0.005) {
            sprintf(s, "!Clearance from CLASS %d %s = %.4f.\nMust be 0.005mm > Tool#1 %.4f", S.class.number, S.class.name, u2mm(S.class.clearance), MillToolOutl);
            dlgMessageBox(s, "OK");
            exit(0);
          }
        }
      }
    }
    int n;
    B.layers(L) {
      if (L.number <= 16 && L.visible) {
        if (Layer == L.number) SelectedLayer = n;
        sprintf(Layers[n++], "%d  %s", L.number, L.name);
      }
    }
    if (n == 0) Fatal("No signal layer active!", "Please activate the signal layer to generate outlines for.");
    if (!Layer) {
      if (n > 1) ForceDialog = 1;
      SelectedLayer = 0;
    }
    if (SelectedLayer < 0) {
      string s;
      sprintf(s, "%d", Layer);
      Fatal("Invalid layer: " + s, "The layer was not found or is not active.");
    }
    if (ForceDialog) {
      SelectedDevice = defaultdevice;
      File = FileName;
      DrFile = DrillFile;
      DrillLabel = "D&rill file";
      selectDevice();
      int mir = 0;
      dlgDialog(Version) {
        dlgHBoxLayout {
          dlgGridLayout {
            dlgCell(0, 0) dlgLabel("&Device");
            dlgCell(0, 1) dlgComboBox(DeviceNames, SelectedDevice) {
              if (SelectedDevice) selectDevice();
              else {
                File = "";
                DrFile = "";
              }
              // 26.07.2006
              if      (SelectedDevice == devHPGL) infotext = "";
              else if (SelectedDevice == devScript) {
              infotext = "
" +
                         "Layer+101 to read copper pouring milling,
" +
                         "Layer+102 to read real outlines milling (Dimension)
" +
                         "into board as wire.";
            }
          }
          dlgCell(0, 3) dlgCheckBox("onl&y drills (HPGL)", onlydrill) {
            if (onlydrill) {
              generatedrills = 1;
              infotext = "
";
              }
            }
            dlgCell(6, 4) dlgPushButton("Pad info")     { info = showpic[5]; infotext = "Pad drill diameter"; };
            dlgCell(7, 0) dlgLabel("&Via drill");
            dlgCell(7, 1) dlgRealEdit(DrillVia, 0.005, 10);
            dlgCell(7, 2) dlgLabel("mm");
            dlgCell(7, 4) dlgPushButton("Via info"){
              info = showpic[6];
              infotext = "Via drill diameter.";
            }
            dlgCell(8, 0) dlgLabel("&Hole drill");
            dlgCell(8, 1) dlgRealEdit(DrillHole, 0.005, 10);
            dlgCell(8, 2) dlgLabel("mm");
            dlgCell(8, 4) dlgPushButton("Hole info"){
              info = showpic[7];
              infotext = "Hole drill diameter.";
            }
            dlgCell(9, 0) dlgLabel("Dist. &Copper/Dim");
            dlgCell(9, 1) dlgRealEdit(Distance_Copper_Dimension, 0.0, 20);
            dlgCell(9, 2) dlgLabel("mm");
            dlgCell(9, 4) dlgPushButton("Distance info") {
              info = showpic[10];
              infotext = infotext = DRCinfo +  usage +
                         " ";
            }
            dlgCell(10, 0) dlgLabel("Mill Board/D&im");
            dlgCell(10, 1) dlgRealEdit(DimensionMillTool, 0.0, 5);
            dlgCell(10, 2) dlgLabel("mm");
            dlgCell(10, 3) dlgLabel(" (0 = OFF)");
            dlgCell(10, 4) dlgPushButton("Dimension info") {
              info = showpic[16];
              if (DimensionMillTool) {
                infotext = "
";
            }
            dlgCell(10, 0) dlgLabel("Mill Board/D&im");
            dlgCell(10, 1) dlgRealEdit(DimensionMillTool, 0.0, 5);
            dlgCell(10, 2) dlgLabel("mm");
            dlgCell(10, 3) dlgLabel(" (0 = OFF)");
            dlgCell(10, 4) dlgPushButton("Dimension info") {
              info = showpic[16];
              if (DimensionMillTool) {
                infotext = "
Set value 0 for switch OFF this function.
Set value for switch ON this function.