#usage "en: <b>BGA Escape Routing </b>"
           "<p>"
           "Usage: run route-bga element"
           "<p>"
           "<author>Author: support@cadsoft.de</author>"

// THIS PROGRAM IS PROVIDED AS IS AND WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED

#require 6.0000

string Version = "1.0";

//======================================================================================
// Include
enum { false = 0, true = 1 };

// Debug stuff

int Dbg = 0;
string DbgMsg, DbgTxt, DbgFile;

void DbgInit() {
   if (Dbg) {
      output(DbgFile, "wt") printf("");
      DbgTxt = "";
   }
}

void DbgDump() {
   if (Dbg) {
      output(DbgFile, "wba") printf(DbgTxt);
      DbgTxt = "";
   }
}

// General utilities
int Sgn(int i) {
   return (i > 0) ? 1 : (i < 0) ? -1 : 0;
}

string I2Str(int i) {
   string str;
   sprintf(str, "%d", i);
   return str;
}

// nrDigits: number of digits after dot/comma
string R2Str(real r, int nrDigits) {
   string str;
   sprintf(str, "%." + I2Str(nrDigits) + "f", r);
   return str;
}

int Str2U(string s) {
   if (strstr(s, "mm") > 0) return int(strtod(s) * 10000 * 32);
   if (strstr(s, "mil") > 0) return int(strtod(s) * 10 * 32 * 25.4);
   if (strstr(s, "in") > 0) return int(strtod(s) * 10000 * 32 * 25.4);
   if (strstr(s, "mic") > 0) return int(strtod(s) * 10 * 32);
   dlgMessageBox("Unknown unit ! Cannot convert.");
   return 0;
}

// Language support for dialogs: German/English
// Please keep to alphabetic sorting for better maintainability !

string Dictionary[] = {
  "en\v"
  "de\v",
  "All contacts\v"
  "Alle Kontakte\v",
  "BGA analysis for \v"
  "BGA-Analyse von \v",
  "Cancel\v"
  "Beenden\v",
  "Contact diameter: \v"
  "Kontakt-Durchmesser: \v",
  "Contact distance: \v"
  "Kontakt-Abstand: \v",
  "Contacts not completely circular (Roundness < 99%) !\v"
  "Kontakte nicht vollständig kreisförmig (Rundheit < 99%) !\v",
  "Contacts with signals\v"
  "Kontakte mit Signalen\v",
  "Coverage\v"
  "Abdeckung\v",
  "Deviation of SMD dimensions\v"
  "Unterschiedliche SMD-Grössen !\v",
  "Element contains non SMD contacts !\v"
  "Element enthält Nicht-SMD-Kontakte !\v",
  "Escape distance: \v"
  "Escape-Distanz: \v",
  "Help\v"
  "Hilfe\v",
  "Make bones\v"
  "Knochentechnik\v",
  "Maximum via drill for bone technique: \v"
  "Maximaler Via-Durchmesser für die Knochentechnik: \v",
  "Maximum wire width for one wire between contacts: \v"
  "Maximale Wire-Breite für eine Bahn zwischen den Kontakten: \v",
  "Micro via drill size:\v"
  "Micro-Via-Durchmesser:\v",
  "Not enough space for vias between contacts. \v"
  "Nicht ausreichend Platz für Vias zwischen den Kontakten. \v",
  "Not enough space for wires between contacts. \v"
  "Nicht ausreichend Platz für Wires zwischen den Kontakten. \v",
  "not found\v"
  "nicht gefunden\v",
  "Number of contacts: \v"
  "Anzahl von Kontakten: \v",
  "Please start from Layout editor !\v"
  "Bitte starten Sie vom Layout-Editor aus !\v",
  "Processing %d of %d contacts\v"
  "Bearbeite %d von %d Kontakten\v",
  "Stacked micro vias:\v"
  "Stacked Micro-Vias:\v",
  "Stacked micro vias recommended !\v"
  "Empfehlung von Stacked Micro-Vias !\v",
  "Use net classes\v"
  "Netzklassen verwenden\v",
  "Use specific sizes:\v"
  "Spezifische Grössen verwenden:\v",
  "with signals\v"
  "mit Signalen\v",
  "Via drill size:\v"
  "Via-Durchmesser:\v",
  "Via strategy\v"
  "Via-Strategie\v",
  "Wire and via sizes:\v"
  "Wire- und Via-Grössen:\v",
  "Wire width:\v"
  "Wire-Breite:\v"
};

string DlgLang = language();
if (DlgLang != "de") DlgLang = "en";
int LangIdx = strstr(Dictionary[0], DlgLang) / 3;

// Translate, based on dictionary
string tr(string s) {
   string t = lookup(Dictionary, s, LangIdx, '\v');
   return (t != "") ? t : s;
}
// End include
//==============================================================================

//==============================================================================
// Globals
//==============================================================================

// Constants
//----------

// States of grid points
enum { NEAR_WIRE    = 1,
       NEAR_VIA     = 2,
       WIRE         = 3,
       VIA          = 4,
       CON          = 1 << 3,
       VISITED      = 1 << 4

    };
enum { ROUTE_MASK = 0x7 };

// Dimensions
enum { DIM_X, DIM_Y };

enum { INVALID_POS = -1 };

// Via strategies
enum { BONES = 0, STACKED_VIAS = 1 };
// Route modes
enum { ESCAPE, INTERNAL };

// Variables
//----------

// DRU stuff
int ExpDRUs = true;
string DruFile;
// Not adjusted
int MinWireWire, MinWirePad, MinWireVia, MinPadVia, MinViaVia;
// Used as minimums
int MinWireWidth;
int MinViaDrill, MinMicroViaDrill;
// Restring stuff
real RestFacVia, RestFacMicroVia;
int RestMinVia, RestMaxVia, RestMinMicroVia, RestMaxMicroVia;
int NrLayers, Layers[];

int EscapeDist = 5 * 10000 * 32; // 5 mm

int Dialog = true;
string ScrFile;
int ExtUnit;
string ExtUnitName;
string ElemNames[];
int NrElems;

real Lbr2BrdRot[] = { 1, 0,
                      0, 1 };
int ElemX, ElemY;

int WireWidth;
int ViaDrill, ViaDiam;
int MicroViaDrill, MicroViaDiam;

// Net classes
int UseNetClasses = false;
int MaxNetClassId;
int ClassAdjWidths[], ClassAdjDrills[], ClassClearances[];

// BGA specific
//-------------
// Element as whole
string ElemName;
real ElemAngle;
int ElemMirror;
int NrNewSig;
int ElemViaStrat = BONES;  //STACKED_VIAS;
int ElemRouteAll = false;

// Contacts
int ConXMin, ConYMin;
int NrCons, NrConsX, NrConsY, NrConsWithSig;
string ConSigNames[];
int ConXs[], ConYs[], ConClassIds[], ConIdxs[];
// Corresponding coords of lbr package
int LbrPacConXs[], LbrPacConYs[];
int ConDist, ConDiam;

int MaxWireWidth1, MaxViaDrill;

// Grid Matrix
int NrGrid, NrGridX, NrGridY, GridStates[];
int GridOriginX, GridOriginY;
int GridDist;
int GridMultiple = 1;
// Only calculated and relevant if more than one wire between contacts:
//  - Distance from contact middle to next possible wire middle.
int GridConMWireM;
//  - Distance from wire middle to neighboring wire middle
real GridWireMWireM;

// Matrix of allowed transitions
//  - Number of states relevant for routing through/beside them
int NrOccStates = 6;
int Allowed[] = { true, true,  true,  true,   true,  true,  // NEAR_VIA
                  true, true,  true,  true,   true,  true,  // NEAR_WIRE
                  true, false, false, false, false, false,  // WIRE
                  true, false, true,  false, false, false };// VIA
// The index position for contact in the Allowed-Matrix.
int AllowedIdxCon = 5;

// Current states while traversing grid
int CurTraverse;
int CurGridX, CurGridY;
//int CurRouteDir;
int CurIncX, CurIncY;
int CurEscaped;

int PrevTryIncI, PrevTryIncJ; // PrevStepMode ?
int CurRouteMode;
int CurDeltaI, CurDeltaJ;
int CurTryIncI[], CurTryIncJ[], CurNrTries;  // Optimize with CurStepModes[3] ?
int CurTakeBone;
int CurBoneX, CurBoneY;

// Escape trials
int TryI, TryJ;

//==============================================================================
// Functions
//==============================================================================

int ExtU2U(real x) {
   return (ExtUnit == GRID_UNIT_MM)   ? mm2u(x)   :
          (ExtUnit == GRID_UNIT_INCH) ? inch2u(x) :
          (ExtUnit == GRID_UNIT_MIC)  ? mic2u(x)  : mil2u(x);
}

real U2ExtU(int x) {
   return (ExtUnit == GRID_UNIT_MM)   ? u2mm(x)   :
          (ExtUnit == GRID_UNIT_INCH) ? u2inch(x) :
          (ExtUnit == GRID_UNIT_MIC)  ? u2mic(x)  : u2mil(x);
}

string U2ExtUStr(int u) {
   return R2Str(U2ExtU(u), 3) + ExtUnitName;
}

int GetViaDiam(int drill) {
   return drill + min(max(RestFacVia * drill, RestMinVia), RestMaxVia) * 2;
}

void GetDesignParams() {
   // Read relevant design rule parameters:
   string lines[], laySetupStr;
   int nrLines = fileread(lines, DruFile);
   if (nrLines == 0) {
      dlgMessageBox("Error reading design rules !");
      exit(EXIT_FAILURE);
   }
   for (int i = 0; i < nrLines; ++i) {
      string words[];
      int nrWords = strsplit(words, lines[i], ' ');
      if (nrWords < 3) continue;    // Empty line or some other stuff
      string keyword = words[0];
      if      (keyword == "mdWireWire") MinWireWire = Str2U(words[2]);
      else if (keyword == "mdWirePad") MinWirePad = Str2U(words[2]);
      else if (keyword == "mdWireVia") MinWireVia = Str2U(words[2]);
      else if (keyword == "mdPadVia") MinPadVia = Str2U(words[2]);
      else if (keyword == "mdViaVia") MinViaVia = Str2U(words[2]);
      else if (keyword == "msWidth") MinWireWidth = Str2U(words[2]);
      else if (keyword == "layerSetup") laySetupStr = words[2];
      else if (keyword == "msDrill") MinViaDrill = Str2U(words[2]);
      else if (keyword == "rvViaOuter") RestFacVia = strtod(words[2]);
      else if (keyword == "rlMinViaOuter") RestMinVia = Str2U(words[2]);
      else if (keyword == "rlMaxViaOuter") RestMaxVia = Str2U(words[2]);
      else if (keyword == "msMicroVia") MinMicroViaDrill = Str2U(words[2]);
      else if (keyword == "rvMicroViaOuter") RestFacMicroVia = strtod(words[2]);
      else if (keyword == "rlMinMicroViaOuter") RestMinMicroVia = Str2U(words[2]);
      else if (keyword == "rlMaxMicroViaOuter") RestMaxMicroVia = Str2U(words[2]);
   }
   // Evaluate LayerSetup-String:
   for (int j = 1; j <= 16; ++j) {
      string layStr;
      sprintf(layStr, "[\\D]%d[\\D]", j);
      if (strxstr(laySetupStr, layStr) != -1) {
         Layers[NrLayers++] = j;
         if (Dbg) { sprintf(DbgMsg, "Layer %d\n", Layers[NrLayers -1]); DbgTxt += DbgMsg; }
      }
   }
   WireWidth = MinWireWidth;
   ViaDrill = MinViaDrill;
   MicroViaDrill = min(MinMicroViaDrill, MinViaDrill);
   if (Dbg) {
      sprintf(DbgMsg, "DRU Params: MinWireWire %f MinWirePad %f MinWireVia %f MinPadVia %f MinViaVia %f\n",
              u2mm(MinWireWire), u2mm(MinWirePad), u2mm(MinWireVia), u2mm(MinPadVia), u2mm(MinViaVia));
      DbgTxt += DbgMsg;
   }
}

int GridState(int lay, int i, int j) {
    return (i >= 0 && i < NrGridX && j >= 0 && j < NrGridY) ? GridStates[lay * NrGrid + i * NrGridY + j]: 0;
}

int GridHasState(int lay, int i, int j, int state) {
   return GridStates[lay * NrGrid + i * NrGridY + j] & state;
}

// Not valid for route states !
void SetGridState(int lay, int i, int j, int state) {
   GridStates[lay * NrGrid + i * NrGridY + j] |= state;
}

void SetGridRouteState(int lay, int i, int j, int state) {
   if (i >= 0 && i < NrGridX && j >= 0 && j < NrGridY)
      GridStates[lay * NrGrid + i * NrGridY + j] = (GridStates[lay * NrGrid + i * NrGridY + j] & ~ROUTE_MASK) | state;
}

int GridCoord(int k, int dim) {
   if (GridMultiple <= 2)
      return (dim == DIM_X ? GridOriginX : GridOriginY) + k * GridDist;
   // If more than 1 trace fits between it's not so obvious...
   int r = k % GridMultiple - 1;
   int between = (r > -1) ? GridConMWireM + r * GridWireMWireM : 0;
   return (dim == DIM_X ? GridOriginX : GridOriginY) + k / GridMultiple * ConDist + between;
}

int LbrGrid2BrdCoord(int i, int j, int dim) {
   int elemCoord = (dim == DIM_X) ? ElemX : ElemY;
   int rowOffs =   (dim == DIM_X) ? 0 : 2;                                          // LbrPacX = LbrPac = 0
   return round(Lbr2BrdRot[rowOffs] * GridCoord(i, DIM_X) + Lbr2BrdRot[rowOffs + 1] * GridCoord(j, DIM_Y)) + elemCoord;
}

void SetupElement(string elemName) {
   int err = false, errRound = false, errType = false, errSize = false;
   int found = false;
   int roundTol = 1; // Perhaps check this out more detailed !
   int conXMax = INT_MIN, conYMax = INT_MIN;
   int conDiamMin = INT_MAX, conDiamMax = INT_MIN;   // Checking radius deviations
   int conDistMin = INT_MAX, conDistMax = INT_MIN;   // Checking distance deviations

   ElemName = elemName;
   NrNewSig = 0;
   NrCons = 0;
   ConDist = INT_MAX;
   ConXMin = INT_MAX, ConYMin = INT_MAX;
   if (Dbg) DbgTxt += "SetupElement:\n";

   // Find element and package of according library
   board(B) {
      string lbrName, pacName;
      B.elements(E)
         if (E.name == elemName) {
            found = true;
            lbrName = E.package.library;
            pacName = E.package.name;
            // Params for xform
            ElemX = E.x;
            ElemY = E.y;
            ElemMirror = E.mirror;
            ElemAngle = E.angle / 180 * PI;  // Bogenmass !
            Lbr2BrdRot[0] = cos(ElemAngle);
            Lbr2BrdRot[1] =-sin(ElemAngle);
            Lbr2BrdRot[2] = sin(ElemAngle);
            Lbr2BrdRot[3] = cos(ElemAngle);
            if (Dbg) {
               sprintf(DbgMsg, "angle, Rot: %f,  (%f %f %f %f)\n", E.angle, Lbr2BrdRot[0], Lbr2BrdRot[1] ,Lbr2BrdRot[2] ,Lbr2BrdRot[3]);
               DbgTxt += DbgMsg;
            }
            E.package.contacts(C) {
               ConXs[NrCons] = C.x;
               ConYs[NrCons] = C.y;
               ConSigNames[NrCons] = C.signal;
               if (C.signal != "") ++NrConsWithSig;
               if (Dbg) { sprintf(DbgMsg, "Elem: (%f %f)\n", u2mm(ConXs[NrCons]), u2mm(ConYs[NrCons]));  DbgTxt += DbgMsg; }
               ++NrCons;
            }
            break;
         }
      if (!found) {
         dlgMessageBox("Element " + elemName + " " + tr("not found") + "!");
         exit(EXIT_FAILURE);
      }
      found = false;
      B.libraries(L) if (!found && L.name == lbrName)
         L.packages(PAC) if (PAC.name == pacName) {
            found = true;
            int i;
            PAC.contacts(C) {
               LbrPacConXs[i] = C.x;
               LbrPacConYs[i++] = C.y;
               // Get grid dimensions
               ConXMin = min(ConXMin, C.x);
               conXMax = max(conXMax, C.x);
               ConYMin = min(ConYMin, C.y);
               conYMax = max(conYMax, C.y);
               if (Dbg) { sprintf(DbgMsg, "LbrPac: (%f %f)\n", u2mm(LbrPacConXs[i-1]), u2mm(LbrPacConYs[i-1]));  DbgTxt += DbgMsg; }
               if (C.smd) {
                  ConDiam = C.smd.dx;
                  if (C.smd.dx != C.smd.dy || 100 - C.smd.roundness > roundTol)
                     errRound = true;
                  conDiamMin = min(conDiamMin, C.smd.dx);
                  conDiamMax = max(conDiamMax, C.smd.dx);
                  if (conDiamMin != conDiamMax)
                     errSize = true;
               }
               else {
                  errType = true;
                  break;
               }
            }
            break;
         }
   }

   // Error conditions:
   if (errSize) dlgMessageBox(tr("Deviation of SMD sizes ") + " ! " + "Minimum = " +
                              U2ExtUStr(conDiamMin) + ", Maximum = " + U2ExtUStr(conDiamMax));
   if (errType) dlgMessageBox(tr("Element contains non SMD contacts !"));
   if (errRound) dlgMessageBox(tr("Contacts not completely circular !"));
   if (errSize || errType || errRound)
      exit(EXIT_FAILURE);

   ConDiam = (conDiamMin + conDiamMax) / 2;  // Tolerant approach. Tolerances tbd.
   sort(NrCons, ConIdxs, LbrPacConXs, LbrPacConYs);
   // Calc contact distance.
   for (int i = 1, curX = LbrPacConXs[ConIdxs[0]]; i < NrCons; ++i) {
      if (LbrPacConXs[ConIdxs[i]] == curX)
         ConDist = min(ConDist, LbrPacConYs[ConIdxs[i]] - LbrPacConYs[ConIdxs[i - 1]]);
      else {
         ConDist = min(ConDist, LbrPacConXs[ConIdxs[i]] - curX);
         curX = LbrPacConXs[ConIdxs[i]];
      }
   }
   if (Dbg) DbgDump();

   real rNrConsX = real(conXMax - ConXMin) / ConDist + 1;
   real rNrConsY = real(conYMax - ConYMin) / ConDist + 1;
   NrConsX = round(rNrConsX);
   NrConsY = round(rNrConsY);
   if (Dbg) {
      real dbgEpsX = abs(rNrConsX - NrConsX);
      real dbgEpsY = abs(rNrConsY - NrConsY);
      sprintf(DbgMsg, "Deviation NrConsX/Y: %.7f %.7f\n", dbgEpsX, dbgEpsY);  DbgTxt += DbgMsg;
      DbgDump();
   }
   MaxWireWidth1 = max(ConDist - ConDiam - 2 * MinWirePad, 0);
   int viaDiam = sqrt(2) * ConDist - ConDiam - 2 * MinPadVia;
   MaxViaDrill = max(max(min(viaDiam / (1 + 2 * RestFacVia), viaDiam - 2 * RestMinVia), viaDiam - 2 * RestMaxVia), 0);
}

void IncludeClasses() {
   string sigNames[];
   int sigClassIds[], nrSigs;
   board(B) {
      int maxNetClassId;
      B.classes(C) if (C.name != "") {
         // Adjustment:
         ClassAdjWidths[C.number] = max(C.width, MinWireWidth);
         ClassAdjDrills[C.number] = max(C.drill, MinViaDrill); // CalcViaDiam
         ClassClearances[C.number] = C.clearance;
         if (Dbg) {
            sprintf(DbgMsg, "C.number %d C.width %f C.drill %f C.clearance %f\n",
                    C.number, u2mm(C.width), u2mm(C.drill), u2mm(C.clearance));
            DbgTxt += DbgMsg;
         }
         maxNetClassId = max(maxNetClassId, C.number);
      }
      // Incorporate class widths, drills etc. in design parameters.
      ViaDiam = GetViaDiam(MinViaDrill);
      for (int i = 0; i < maxNetClassId; ++i) {
         WireWidth = max(WireWidth, ClassAdjWidths[i]);
         ViaDiam = max(ViaDiam, GetViaDiam(ClassAdjDrills[i]));
         MinWireWire = max(MinWireWire, ClassClearances[i]);
         MinWirePad = max(MinWirePad, ClassClearances[i]);
         MinWireVia = max(MinWireVia, ClassClearances[i]);
         MinViaVia = max(MinViaVia, ClassClearances[i]);
         MinPadVia = max(MinPadVia, ClassClearances[i]);
      }
      // Collect BGA's signals and classes
      B.signals(S)
         S.contactrefs(CR)
            if (CR.element.name == ElemName) {
               sigNames[nrSigs] = S.name;
               sigClassIds[nrSigs++] = S.class.number;
               if (Dbg) {
                  sprintf(DbgMsg, "BGA-Signal: %s %d\n", sigNames[nrSigs - 1], sigClassIds[nrSigs - 1]);
                  DbgTxt += DbgMsg;
               }
               break;
            }
   }
   for (int i = 0; i < NrCons; ++i)
      if (ConSigNames[i] != "")
         for (int j = 0; j < nrSigs; ++j)
            if (sigNames[j] == ConSigNames[i]) {
               ConClassIds[i] =  sigClassIds[j];
               if (Dbg) {
                  sprintf(DbgMsg, "Con->ClassId: %d %d\n", i, ConClassIds[i]);
                  DbgTxt += DbgMsg;
               }
               break;
            }
   DbgDump();
}

void InitGrid() {
   if (Dbg) {
      sprintf(DbgMsg, "InitGrid: ViaDiam %f MicroViaDiam %f WireWidth %f \n"
                      "MinWireWire %f MinWirePad %f MinWireVia %f MinViaVia %f MinPadVia %f\n",
                      u2mm(ViaDiam), u2mm(MicroViaDiam), u2mm(WireWidth), u2mm(MinWireWire),
                      u2mm(MinWirePad), u2mm(MinWireVia), u2mm(MinViaVia), u2mm(MinPadVia));
      DbgTxt += DbgMsg;
   }
   // Nr traces between contacts:
   int freeWidth = ConDist - ConDiam - 2 * MinWirePad - WireWidth;
   if (freeWidth >= 0) {
      GridMultiple = 2;
      // In this case we support more than 1 trace between !
      if (ElemViaStrat == STACKED_VIAS)
         GridMultiple += freeWidth / (WireWidth + MinWireWire);
   }
   if (GridMultiple <= 2) GridDist = ConDist / GridMultiple;
   else {
      GridConMWireM = ConDiam / 2 + MinWirePad + WireWidth / 2;
      GridWireMWireM = real(freeWidth) / (GridMultiple - 2);
   }
   NrGridX = (NrConsX - 1) * GridMultiple + 1;
   NrGridY = (NrConsY - 1) * GridMultiple + 1;
   NrGrid = NrGridX * NrGridY;
   GridOriginX = ConXMin;
   GridOriginY = ConYMin;

   if (Dbg) {
      sprintf(DbgMsg, "GridMultiple %d GridDist %f NrGridX %d NrGridY %d \nGridOriginX %f GridOriginY %f\n",
             GridMultiple, u2mm(GridDist), NrGridX, NrGridY, u2mm(GridOriginX), u2mm(GridOriginY));
      DbgTxt += DbgMsg; DbgDump();
   }

   int nrTotal = NrGrid * NrLayers;
   for (int i = nrTotal - 1; i >= 0; --i) GridStates[i] = 0; // Faster initialisation this way...
   for (int k = 0; k < NrCons; ++k) {
      int i = round(real(LbrPacConXs[k] - GridOriginX) / ConDist * GridMultiple);
      int j = round(real(LbrPacConYs[k] - GridOriginY) / ConDist * GridMultiple);
      SetGridState(0, i, j, CON);
   }
   Allowed[NrOccStates * (NEAR_WIRE - 1) + AllowedIdxCon] = (GridDist * sqrt(2) >= ConDiam + MinWirePad * 2 + WireWidth);
   Allowed[NrOccStates * (VIA - 1) + NEAR_VIA] = (GridDist >= ViaDiam + MinViaVia);
   Allowed[NrOccStates * (NEAR_VIA - 1) + VIA] = Allowed[NrOccStates * (VIA - 1) + NEAR_VIA];
   Allowed[NrOccStates * (NEAR_VIA - 1) + AllowedIdxCon] = (GridDist >= ViaDiam / 2 + ConDiam / 2 + MinPadVia);
   Allowed[NrOccStates * (NEAR_VIA - 1) + WIRE] = (GridDist >= ViaDiam / 2 + WireWidth / 2 + MinWireVia);
   // Leads to diagonal problem !
   //Allowed[NrOccStates * (WIRE - 1) + NEAR_VIA] = Allowed[NrOccStates * (NEAR_VIA - 1) + WIRE];
   if (Dbg) {
      sprintf(DbgMsg,"NEAR_WIRE -> CON: GridDist * sqrt(2) %f <-> %f\n",
              u2mm(int(GridDist * sqrt(2))), u2mm(ConDiam + MinWirePad * 2 + WireWidth)); DbgTxt += DbgMsg;
      sprintf(DbgMsg,"NEAR_VIA -> CON: ViaDiam / 2 + ConDiam / 2 + MinPadVia: %f\n",
              u2mm(ViaDiam / 2 + ConDiam / 2 + MinPadVia)); DbgTxt += DbgMsg;
      sprintf(DbgMsg,"WIRE -> NEAR_VIA: ViaDiam / 2 + WireWidth / 2 + MinWireVia: %f\n",
              u2mm(ViaDiam / 2 + WireWidth / 2 + MinWireVia)); DbgTxt += DbgMsg;

      sprintf(DbgMsg,"Allowed-Matrix:\n"); DbgTxt += DbgMsg;
      for (int i = 0; i<4; ++i) {
         sprintf(DbgMsg,"%d %d %d %d %d %d\n", Allowed[NrOccStates * i], Allowed[NrOccStates * i + 1],
                                               Allowed[NrOccStates * i + 2], Allowed[NrOccStates * i + 3],
                                               Allowed[NrOccStates * i + 4], Allowed[NrOccStates * i + 5]);
         DbgTxt += DbgMsg;
      }
      DbgDump();
   }
}

int AllowedTransition(int curState, int newRouteState) {
   int allowed = (curState & CON) ? Allowed[NrOccStates * (newRouteState - 1) + AllowedIdxCon] : true;
   if (allowed) {
      int routeState = curState & ROUTE_MASK; // 3 bit !
      if (routeState != 0)
         allowed = Allowed[NrOccStates * (newRouteState - 1) + routeState];
   }
   if (Dbg) { sprintf(DbgMsg, "AllowedTransition: %d -> %d: %d\n", curState, newRouteState, allowed); DbgTxt += DbgMsg; }
   return allowed;
}

int Layer(int i) {
   return ElemMirror ? Layers[NrLayers - 1 - i] : Layers[i];
}

int ConIdx(int i, int j) {
   if (Dbg) { sprintf(DbgMsg, "ConIdx: i %d j %d\n", i, j); DbgTxt += DbgMsg; }
   if ((i % GridMultiple != 0) || (j % GridMultiple != 0)) return -1;
   int x = GridOriginX + round(ConDist * i / GridMultiple);  // ! oder ConXs/Y - Arrays ersetzen !
   int y = GridOriginY + round(ConDist * j / GridMultiple);
   for (int k = 0; k < NrCons; ++k)
      if ((LbrPacConXs[ConIdxs[k]] == x) && (LbrPacConYs[ConIdxs[k]] == y)) {
         if (Dbg) { sprintf(DbgMsg, "ConIdx return: %d\n", ConIdxs[k]); DbgTxt += DbgMsg; }
         return ConIdxs[k];
      }
      else if (LbrPacConXs[ConIdxs[k]] > x) {
         if (Dbg) DbgTxt += "LbrPacConXs[ConIdxs[k]] > x !";
         return -1;
      }
   return -1;
}

string NewSigName() {
  string newSigName;
  sprintf(newSigName, "%s-%d", ElemName, ++NrNewSig); //IC2-34. Alternative: IC2-i-j, i,j Kontaktpos.
  return newSigName;
}

int BorderDist(int i, int j) {
   return min(min(i, NrGridX - 1 - i), min(j, NrGridY - 1 - j));
}

void TurnLeft() {
   if (Dbg) { sprintf(DbgMsg, "TurnLeft: before: (%d %d) (%d %d)\n", CurDeltaI, CurDeltaJ, CurIncX, CurIncY); DbgTxt += DbgMsg; }
   int oldDeltaI = CurDeltaI, oldIncX = CurIncX;
   CurDeltaI = -CurDeltaJ;
   CurDeltaJ =  oldDeltaI;
   CurIncX = -CurIncY;
   CurIncY =  oldIncX;
   if (Dbg) { sprintf(DbgMsg, "TurnLeft: after: (%d %d) (%d %d)\n", CurDeltaI, CurDeltaJ, CurIncX, CurIncY); DbgTxt += DbgMsg; }
}

int SetNextPos() {
   if (Dbg) { sprintf(DbgMsg, "SetNextPos: before: %d %d\n", CurGridX, CurGridY); DbgTxt += DbgMsg; DbgDump(); }
   if (!CurTraverse) {
      CurGridX = 0;
      CurGridY = NrGridY - 1;
      // Route to left, traverse down
      CurDeltaI = -1;
      CurDeltaJ = 0;
      CurIncX = 0;
      CurIncY = -1;
      SetGridState(0, CurGridX, CurGridY, VISITED);
      CurTraverse = true;
      return true;
   }
   int turn1 = false;
   if (GridHasState(0, CurGridX + CurIncX, CurGridY + CurIncY, VISITED)) {
      TurnLeft();
      turn1 = true;
   }
   CurGridX += CurIncX;
   CurGridY += CurIncY;
   if (!turn1 && min(CurGridX, NrGridX - 1 - CurGridX) == min(CurGridY, NrGridY - 1 - CurGridY))
      TurnLeft();
   if (GridHasState(0, CurGridX, CurGridY, VISITED))
      CurGridX = CurGridY = INVALID_POS;
   else
      SetGridState(0, CurGridX, CurGridY, VISITED);
   if (Dbg) { sprintf(DbgMsg, "SetNextPos: after: %d %d\n", CurGridX, CurGridY); DbgTxt += DbgMsg; DbgDump(); }
   return (CurGridX != INVALID_POS);
}

int SetNextConPos() {
   while (SetNextPos() && !(GridHasState(0, CurGridX, CurGridY, CON) &&
          (ElemRouteAll || ConSigNames[ConIdx(CurGridX, CurGridY)] != "")));
   CurEscaped = false;
   CurTakeBone = false;
   CurRouteMode = ESCAPE;
   return (CurGridX != INVALID_POS);
}

int IsBorder(int i, int j) {
   return (i == 0) || (i == NrGridX - 1) || (j == 0) || (j == NrGridY - 1);
}

int TryRoute(int lay, int i, int j, int incX, int incY, int routeType, int singleStep) {
   if (Dbg) {
      sprintf(DbgMsg, "Try start: i %d j %d incX %d inc Y %d type %d\n", i,j,incX,incY,routeType);
      DbgTxt += DbgMsg; DbgDump();
   }
   TryI = i;
   TryJ = j;
   while (true) {
      if (IsBorder(TryI, TryJ) || !AllowedTransition(GridState(lay, TryI + incX, TryJ + incY), routeType)) break;
      if (routeType == VIA) {
          if (incX == 0) {
             if (!(AllowedTransition(GridState(lay, TryI - 1, TryJ + incY), NEAR_VIA) &&
                   AllowedTransition(GridState(lay, TryI + 1, TryJ + incY), NEAR_VIA) &&
                   AllowedTransition(GridState(lay, TryI, TryJ + 2 * incY), NEAR_VIA))) break;
          }
          else if (incY == 0) {
             if (!(AllowedTransition(GridState(lay, TryI + incX, TryJ - 1), NEAR_VIA) &&
                   AllowedTransition(GridState(lay, TryI + incX, TryJ + 1), NEAR_VIA) &&
                   AllowedTransition(GridState(lay, TryI + 2 * incX, TryJ), NEAR_VIA))) break;
          }
          else if (!(AllowedTransition(GridState(lay, TryI + incX - 1, TryJ + incY    ), NEAR_VIA) &&
                     AllowedTransition(GridState(lay, TryI + incX    , TryJ + incY - 1), NEAR_VIA) &&
                     AllowedTransition(GridState(lay, TryI + incX + 1, TryJ + incY    ), NEAR_VIA) &&
                     AllowedTransition(GridState(lay, TryI + incX    , TryJ + incY + 1), NEAR_VIA))) break;
      }
      else if ((incX * incY != 0) &&
               !(AllowedTransition(GridState(lay, TryI       , TryJ + incY), NEAR_WIRE) &&
                 AllowedTransition(GridState(lay, TryI + incX, TryJ       ), NEAR_WIRE))) break;
      TryI += incX;
      TryJ += incY;
      if (singleStep) break;
   }
   CurEscaped = IsBorder(TryI, TryJ);
   if (Dbg) { sprintf(DbgMsg, "Try end: TriI %d TryJ %d\n", TryI, TryJ); DbgTxt += DbgMsg; DbgDump(); }
   return CurEscaped || (TryI != i) || (TryJ != j);
}

void SetNextSteps() {
   if (CurRouteMode == ESCAPE) {
      int prevI = (PrevTryIncI == 0 && PrevTryIncJ == 0) ? Sgn(CurDeltaI) : PrevTryIncI;
      int prevJ = (PrevTryIncI == 0 && PrevTryIncJ == 0) ? Sgn(CurDeltaJ) : PrevTryIncJ;
      int incI[] = { prevI, Sgn( prevI + prevJ), Sgn(prevI - prevJ) };
      int incJ[] = { prevJ, Sgn(-prevI + prevJ), Sgn(prevI + prevJ) };
      int idx[] = { 0, 1, 2 };
      CurNrTries = 3;
      if (Dbg) {
         sprintf(DbgMsg, "SetNextSteps before: CurNrTries %d\n", CurNrTries); DbgTxt += DbgMsg;
         for (int k = 0; k < CurNrTries; ++k) {
            sprintf(DbgMsg, "%d %d,", incI[k], incJ[k]); DbgTxt += DbgMsg;
         }
         DbgTxt += "\n";
      }
      // Assumption: Only one direction can be opposite of escape direction
      if (incI[1] * CurDeltaI < 0 || incJ[1] * CurDeltaJ < 0) {
         incI[1] = incI[2];
         incJ[1] = incJ[2];
         --CurNrTries;
      }
      else if (incI[2] * CurDeltaI < 0 || incJ[2] * CurDeltaJ < 0)
         --CurNrTries;
      // If direction exactly matches to escape direction take this one first !
      for (int k = 1; k < CurNrTries; ++k)
         if (Sgn(incI[k]) == Sgn(CurDeltaI) && Sgn(incJ[k]) == Sgn(CurDeltaJ)) { // Sgn would be obsolete if all normed
            idx[0] = k;
            idx[k] = 0;
            break;
         }
      // If direction is orthogonal (only one) make it the last one !
      for (k = 0; k < CurNrTries; ++k)
         if (incI[idx[k]] * CurDeltaI + incJ[idx[k]] * CurDeltaJ == 0) {
            int t = idx[k];
            idx[k] = idx[CurNrTries - 1];
            idx[CurNrTries - 1] = t;
         }
      for (k = 0; k < CurNrTries; ++k) {
         CurTryIncI[k] = incI[idx[k]];
         CurTryIncJ[k] = incJ[idx[k]];
      }
   }
   if (Dbg) {
      sprintf(DbgMsg, "SetNextSteps: CurNrTries %d\n", CurNrTries); DbgTxt += DbgMsg;
      for (int k = 0; k < CurNrTries; ++k) {
         sprintf(DbgMsg, "%d %d,", CurTryIncI[k], CurTryIncJ[k]); DbgTxt += DbgMsg;
      }
      DbgTxt += "\n";
   }
}

// Important: (i,j) must be a BORDER POINT !
int EscapeVec(int i, int j, int dim) {
   int k, l;
   if (i == 0 && j > 0) k = -EscapeDist;
   else if (i <  NrGridX - 1 && j == 0) l = -EscapeDist;
   else if (i == NrGridX - 1 && j < NrGridY - 1) k = EscapeDist;
   else l = EscapeDist;
   int rowOffs = (dim == DIM_X) ? 0 : 2;
   if (Dbg) {
      int tr = round(Lbr2BrdRot[rowOffs] * k + Lbr2BrdRot[rowOffs + 1] * l);
      sprintf(DbgMsg, "EscapeVec: i,j,k,l,dim: tr: (%d %d) (%f %f) %d: %f)\n",i, j, u2mm(k), u2mm(l), dim, u2mm(tr)); DbgTxt += DbgMsg;
   }
   return round(Lbr2BrdRot[rowOffs] * k + Lbr2BrdRot[rowOffs + 1] * l);
}

void Execute() {
   output(ScrFile, "wt")
      for (int i = 0; i < NrElems; ++i) {
         board(B)  B.elements(E)
            if (E.name == ElemNames[i]) {
               ViaDiam = GetViaDiam(ViaDrill);
               MicroViaDiam = MicroViaDrill + min(max(RestFacMicroVia * MicroViaDrill, RestMinMicroVia), RestMaxMicroVia) * 2; // same like GetViaDiam
               if (UseNetClasses) IncludeClasses();
               InitGrid();
               printf("CHANGE SHAPE round;\n");
               printf("SET WIRE_BEND 2;\n");  // 2 necessary for rotations != k * 45�
               int conCnt = 0;
               for (CurTraverse = false, SetNextConPos(); CurGridX != INVALID_POS; SetNextConPos()) { // Achte auf i !
                  string statMsg;
                  sprintf(statMsg, "Processing %d of %d contacts...", ++conCnt, ElemRouteAll ? NrCons : NrConsWithSig);
                  status(statMsg);
                  if (Dbg) { DbgTxt += statMsg; DbgDump(); }
                  for (int lay = 0, noRoute = false; lay < NrLayers && !CurEscaped && !noRoute; ++lay) {
                     int curTryI = (CurTakeBone) ? CurBoneX : CurGridX, curTryJ = (CurTakeBone) ? CurBoneY : CurGridY;
                     int tryI[] = { curTryI }, tryJ[] = { curTryJ };
                     int nrTryPts = 1, trapped = false;
                     int viaTrial = false, routeType = WIRE;
                     if (Dbg) {
                        sprintf(DbgMsg, "%s: Contact (%d %d), Layer %d", ElemNames[i], CurGridX, CurGridY, lay);
                        DbgTxt += DbgMsg + "\n"; DbgDump();
                     }
                     PrevTryIncI = PrevTryIncJ = 0;
                     if (lay == 0)
                        CurEscaped = (BorderDist(CurGridX, CurGridY) == 0);
                     while (!(trapped || CurEscaped)) {
                        if (!viaTrial) SetNextSteps();
                        int tryIncI, tryIncJ;
                        if ((TryRoute(lay, curTryI, curTryJ, tryIncI = CurTryIncI[0], tryIncJ = CurTryIncJ[0], routeType, true) > 0) ||
                            (CurNrTries > 1 &&
                             (TryRoute(lay, curTryI, curTryJ, tryIncI = CurTryIncI[1], tryIncJ = CurTryIncJ[1], routeType, true) > 0)) ||
                            (CurNrTries > 2 &&
                             (TryRoute(lay, curTryI, curTryJ, tryIncI = CurTryIncI[2], tryIncJ = CurTryIncJ[2], routeType, true) > 0))) {
                           tryI[nrTryPts] = TryI;
                           tryJ[nrTryPts++] = TryJ;
                           curTryI = TryI;
                           curTryJ = TryJ;
                           PrevTryIncI = tryIncI;
                           PrevTryIncJ = tryIncJ;
                           routeType = WIRE; // In any case continue with WIRE !
                           if (Dbg) DbgDump();
                        }
                        else if (ElemViaStrat == BONES && lay == 0 && nrTryPts >= 2 && !viaTrial) {
                           routeType = VIA;
                           nrTryPts = 1;
                           curTryI = CurGridX;
                           curTryJ = CurGridY;
                           tryI[0] = curTryI;
                           tryJ[0] = curTryJ;
                           PrevTryIncI = PrevTryIncJ = 0;
                           CurTryIncI[0] = CurDeltaI;
                           CurTryIncJ[0] = CurDeltaJ;
                           CurTryIncI[1] = Sgn( CurDeltaI + CurDeltaJ);
                           CurTryIncJ[1] = Sgn(-CurDeltaI + CurDeltaJ);
                           CurNrTries = 2;
                           viaTrial = true;
                        }
                        else
                           trapped = true;
                     }
                     if (CurEscaped || (lay == 0 && ElemViaStrat == BONES)) {
                        int cIdx = ConIdx(CurGridX, CurGridY);
                        real firstX = U2ExtU(CurTakeBone ? LbrGrid2BrdCoord(CurBoneX, CurBoneY, DIM_X) : ConXs[cIdx]);
                        real firstY = U2ExtU(CurTakeBone ? LbrGrid2BrdCoord(CurBoneX, CurBoneY, DIM_Y) : ConYs[cIdx]);
                        real outWireWidth = U2ExtU(UseNetClasses ? ClassAdjWidths[ConClassIds[cIdx]] : WireWidth) ;
                        real outViaDrill = U2ExtU(ElemViaStrat == STACKED_VIAS ? MicroViaDrill :
                                                  UseNetClasses ? ClassAdjDrills[ConClassIds[cIdx]] : ViaDrill);
                        if (Dbg) {
                           sprintf(DbgMsg, "Output: cIdx %d \n", cIdx); DbgTxt += DbgMsg;
                           if (UseNetClasses) {
                              sprintf(DbgMsg, "ConClassIds[cIdx] %d ClassWidths[ConClassIds[cIdx]] %f "
                                              "ClassDrills[ConClassIds[cIdx]] %f\n", ConClassIds[cIdx],
                                              u2mm(ClassAdjWidths[ConClassIds[cIdx]]), u2mm(ClassAdjDrills[ConClassIds[cIdx]]));
                              DbgTxt += DbgMsg;
                           }
                           DbgDump();
                        }
                        string sig = ConSigNames[cIdx];
                        if (sig == "") sig = NewSigName();
                        if (Dbg) { sprintf(DbgMsg, "Escaped, after NewSigName\n"); DbgTxt += DbgMsg; DbgDump(); }

                        if (CurEscaped) {
                           printf("LAYER %d;\n", Layer(lay));  // Evtl. nach vias
                           if (lay > 0) {
                              printf("CHANGE DRILL %f;\n", outViaDrill);
                              printf("VIA '%s'", sig);
                              if (ElemViaStrat == STACKED_VIAS) {
                                 for (int l = 1; l <= lay; ++l)
                                    printf(" %d-%d (%f %f)", Layer(l - 1), Layer(l), firstX, firstY);
                                 printf(";\n");
                              }
                              else {
                                 for (int l = 1; l <= lay; ++l) {
                                    SetGridRouteState(l, CurBoneX, CurBoneY, VIA);
                                    SetGridRouteState(l, CurBoneX - 1, CurBoneY    , NEAR_VIA);
                                    SetGridRouteState(l, CurBoneX + 1, CurBoneY    , NEAR_VIA);
                                    SetGridRouteState(l, CurBoneX    , CurBoneY - 1, NEAR_VIA);
                                    SetGridRouteState(l, CurBoneX    , CurBoneY + 1, NEAR_VIA);
                                 }
                                 printf(" %d-%d (%f %f);\n", Layer(0), Layer(lay), // Durchkontaktierung ?
                                        U2ExtU(LbrGrid2BrdCoord(CurBoneX, CurBoneY, DIM_X)),
                                        U2ExtU(LbrGrid2BrdCoord(CurBoneX, CurBoneY, DIM_Y)));
                              }
                           }
                           if (Dbg) { sprintf(DbgMsg, "Escaped, wire\n"); DbgTxt += DbgMsg; DbgDump(); }
                           printf("WIRE '%s' %f (%f %f)", sig, outWireWidth, firstX, firstY);
                           SetGridRouteState(lay, tryI[0], tryJ[0], WIRE); // Is VIA getting overridden ?
                           for (int i = 1; i < nrTryPts; ++i) {
                              // Mark wire
                              int incI = Sgn(tryI[i] - tryI[i - 1]), incJ = Sgn(tryJ[i] - tryJ[i - 1]);
                              // Put this together if no performance reasons against it  !
                              if (abs(tryI[i] - tryI[i - 1]) > 1 || abs(tryJ[i] - tryJ[i - 1]) > 1) {
                                 for (int k = tryI[i - 1] + incI, l = tryJ[i - 1] + incJ;
                                      (tryI[i] - k) * incI >= 0 && (tryJ[i] - l) * incJ >= 0; k += incI, l += incJ) {
                                    if (Dbg) { sprintf(DbgMsg, "k %d l %d ", k, l); DbgTxt += DbgMsg; DbgDump(); }
                                    SetGridRouteState(lay, k, l, WIRE);
                                    if (incI * incJ != 0) {
                                       SetGridRouteState(lay, k - incI, l,        NEAR_WIRE);
                                       SetGridRouteState(lay, k       , l - incJ, NEAR_WIRE);
                                    }
                                 }
                              }
                              else {
                                 SetGridRouteState(lay, tryI[i], tryJ[i], WIRE);
                                 if (incI * incJ != 0) {
                                    SetGridRouteState(lay, tryI[i - 1] + incI, tryJ[i - 1],        NEAR_WIRE);
                                    SetGridRouteState(lay, tryI[i - 1],        tryJ[i - 1] + incJ, NEAR_WIRE);
                                 }
                              }
                              printf(" (%f %f)", U2ExtU(LbrGrid2BrdCoord(tryI[i], tryJ[i], DIM_X)),
                                                 U2ExtU(LbrGrid2BrdCoord(tryI[i], tryJ[i], DIM_Y)));
                           }
                           // The escape wire !
                           if (Dbg) { sprintf(DbgMsg, "Escaped, wire outside\n"); DbgTxt += DbgMsg; DbgDump(); }
                           int lastI = tryI[nrTryPts - 1], lastJ = tryJ[nrTryPts - 1];
                           printf(" (%f %f);\n", U2ExtU(LbrGrid2BrdCoord(lastI, lastJ, DIM_X) + EscapeVec(lastI, lastJ, DIM_X)),
                                                 U2ExtU(LbrGrid2BrdCoord(lastI, lastJ, DIM_Y) + EscapeVec(lastI, lastJ, DIM_Y)));
                        }
                        else if (nrTryPts >= 2) {
                           if (Dbg) { sprintf(DbgMsg, "Bone-Stuff start\n"); DbgTxt += DbgMsg; DbgDump(); }
                           int incI = tryI[1] - tryI[0], incJ = tryJ[1] - tryJ[0];
                           printf("LAYER %d;\n", Layer(0));
                           printf("WIRE '%s' %f (%f %f) (%f %f);\n", sig, outWireWidth, firstX, firstY,
                                  U2ExtU(LbrGrid2BrdCoord(tryI[1], tryJ[1], DIM_X)),
                                  U2ExtU(LbrGrid2BrdCoord(tryI[1], tryJ[1], DIM_Y)));
                           // Assume this is a single diagonal step !
                           SetGridRouteState(lay, tryI[1], tryJ[1], WIRE);
                           if (incI * incJ != 0) { // Incs not necessary here !
                              SetGridRouteState(lay, tryI[1], tryJ[0], NEAR_WIRE);
                              SetGridRouteState(lay, tryI[0], tryJ[1], NEAR_WIRE);
                           }
                           CurBoneX = tryI[1];
                           CurBoneY = tryJ[1];
                           CurTakeBone = true;
                           ConSigNames[cIdx] = sig;
                           if (Dbg) { sprintf(DbgMsg, "Bone-Stuff end\n"); DbgTxt += DbgMsg; DbgDump(); }
                        }
                        else
                           noRoute = true;
                     }
                  }
               }
               printf("SET WIRE_BEND 3;\n");  // Limit to  k * 45� again.
            }
      }
   DbgDump();
   if (Dbg) dlgMessageBox("Calculation done !");
   exit("SCRIPT '" + ScrFile + "';");
}

//==============================================================================
// Main
//==============================================================================
/*
*/

void main() {
   string help[] = {
      "<h3>BGA Escape Routing</h3>"
      "Based on design rules an attempt is made to route all signals out of a BGA.<br>"
      "The package must follow a BGA design, meaning:<ul>"
      "<li> It is an SMD.</li>"
      "<li> All contacts have circular shape.</li>"
      "<li> All contacts lie on a grid (same distance dx and dy).</li>"
      "</ul>"
      "This is checked before start. "
      "All signals starting from the BGA should not be routed yet."
      "<p>"
      "<b>BGA analysis</b><br>"
      "Before start a feasibility analysis is made that helps you determine appropriate sizes "
      "and strategy. It is based on design rule settings, in particular: "
      "Minimum wire width, minimum via sizes, via restring settings, clearances between wires, "
      "pads and vias. Please make sure your design rules are correct to get accurate results."
      "<p>"
      "<b>Wire and via sizes</b><br>"
      "Wire and via sizes can either be taken over from the net classes of the connected signals or "
      "specific values may be set. In any case they are limited by the minimum values of the design rules."
      "<p>"
      "<b>Via strategy</b><br>"
      "There is the classical bone technique or stacked micro vias. "
      "The bone technique requires space for one trace between 2 neighbour contacts."
      "The BGA analysis checks this. For stacked micro vias the design rules's layer setup must fit."
      "The micro via drill size is independent from net class values."
      "<p>"
      "<b>Coverage</b><br>"
      "By default only contacts with signals are routed. If you want all signals, check the "
      "radio button in the coverage section."
      "<p>"
      "Finally, the <i>escape distance</i> specifies how far the signals are routed out of the BGA (default 5mm)."
      ,
      "<h3>BGA Escape Routing</h3>"
      "Unter Berücksichtigung der Design-Regeln wird versucht, alle Signale aus einem BGA herauszuführen.<br>"
      "Das Package muss einem BGA-Design entsprechen. Das heisst:<ul>"
      "<li> Es ist ein SMD.</li>"
      "<li> Alle Anschlüsse sind kreisförmig.</li>"
      "<li> Alle Anschlüsse liegen in einem gemeinsamen Raster (gleicher Wert für dx und dy).</li>"
      "</ul>"
      "Diese Kriterien werden vor dem Start geprüft. "
      "Alle Signale, die am BGA beginnen, sollten noch nicht geroutet sein."
      "<p>"
      "<b>BGA-Analyse</b><br>"
      "Zuerst wird eine Machbarkeitsanalyse durchgeführt, die helfen soll, die passenden Größen "
      "und die Strategie zu wählen. Diese basiert auf den Werten in den Design-Regeln, insbesondere: "
      "Minimum Wire Width, Minimum Via Sizes, Via-Restring-Einstellungen, Mindestabstände zwischen Wires, "
      "Pads und Vias. Bitte stellen Sie sicher, dass die Design-Regeln geeignet gewählt sind, um gute Ergebnisse zu erzielen."
      "<p>"
      "<b>Wire- und Via-Größen</b><br>"
      "Die Wire- und Via-Größen können entweder von den Netzklassen der angeschlossenen Signale übernommen "
      "oder spezifische Werte gesetzt werden. In jedem Fall werden sie von den Minimum-Werten in den Design-Regeln begrenzt."
      "<p>"
      "<b>Via-Strategie</b><br>"
      "Es gibt die klassische Knochentechnik oder Stacked Micro-Vias. "
      "Die Knochentechnik benötigt genügend Platz für eine Leitung zwischen zwei benachbarten Kontakten. "
      "Die BGA-Aanalyse prüft dies. Für Stacked Micro-Vias muss das Layer-Setup in den Design-Regeln stimmen. "
      "Der Bohrdurchmesser der Micro-Vias ist ein eigener Wert und hat nichts mit den Netzklassen-Werten zu tun. "
      "<p>"
      "<b>Abdeckung</b><br>"
      "Standardmäßig werden nur Kontakte mit Signalen bearbeitet. Wenn alle Kontakte geroutet werden sollen, "
      "wählen Sie diese Option unter Abdeckung."
      "<p>"
      "Die <i>Escape-Distanz</i> schliesslich definiert, wie weit die Signale aus dem BGA herausgeführt werden (Default 5mm)."
   };
   if (Dbg) DbgFile = filesetext(argv[0], "-dbg.txt");
   DbgInit();
   if (board) {
      int i, dummySel, dummySort;
      // Prepare step: Export DRUs if necessary
      for (i = 1; i <= argc; ++i)
          if (argv[i] == "-drus_done") ExpDRUs = false;
      board(B) DruFile = filesetext(B.name, ".dru");
      if (ExpDRUs) {
         string cmds = "DRC SAVE '" + DruFile + "';\nRUN '" + argv[0] + "' -drus_done";
         for (int i = 1; i <= argc; ++i)
             cmds += " " +  argv[i];
         exit(cmds + ";");
      }
      GetDesignParams();
      // Parse arguments. Design params may get overwritten.
      // Note: Only element name is officially supported !
      // Currently other params just for internal use in batch mode.
      for (i = 1; i < argc; ++i) {
         if (argv[i] == "-drus_done") ExpDRUs = false;
         else if (argv[i] == "-nodlg") { Dialog = false; Dbg = false; }
         else if (argv[i] == "-stacked") ElemViaStrat = STACKED_VIAS;
         else if (argv[i] == "-classes") UseNetClasses = true;
         else if (argv[i] == "-all") ElemRouteAll = true;
         else if (strstr(argv[i], "-width=") == 0)
            WireWidth = Str2U(strsub(argv[i], strlen("-width=")));
         else if (strstr(argv[i], "-esc=") == 0)
            EscapeDist = Str2U(strsub(argv[i], strlen("-esc=")));
         else if (strstr(argv[i], "-drill=") == 0)
            ViaDrill = MicroViaDrill = Str2U(strsub(argv[i], strlen("-drill=")));
         else ElemNames[NrElems++] = strupr(argv[i]);  // Ignore case !
      }
      if (ElemNames[0] == "" || NrElems > 1) { // We may allow multiple BGAs in the future.
         dlgMessageBox(usage);
         exit(-1);
      }
      board(B) {
         ScrFile = filesetext(B.name, "-bga.scr");
         ExtUnit = B.grid.unit;
      }
      ExtUnitName = (ExtUnit == GRID_UNIT_MM)   ? "mm"  :
                    (ExtUnit == GRID_UNIT_INCH) ? "in"  :
                    (ExtUnit == GRID_UNIT_MIC)  ? "mic" : "mil";
      SetupElement(ElemNames[0]);

      if (Dialog) int ret = dlgDialog("BGA Escape Routing") {
         real dlgWireWidth = U2ExtU(WireWidth);
         real dlgEscapeDist = U2ExtU(EscapeDist);
         real dlgViaDrill = U2ExtU(ViaDrill);
         real dlgMicroViaDrill = U2ExtU(MicroViaDrill);
         real dlgMinViaDrill = 0.999 * U2ExtU(MinViaDrill);
         real dlgMinMicroViaDrill = 0.999 * U2ExtU(min(MinMicroViaDrill, MinViaDrill));
         if (Dbg) dlgCheckBox("&Debug", Dbg);
         dlgGroup(tr("BGA analysis for ") + ElemName +":") {
            string analysis = "<ul>"
               "<li>" + tr("Number of contacts: ") + I2Str(NrCons) + " (" + I2Str(NrConsWithSig) + " " + tr("with signals") + ")." + "</li>"
               "<li>" + tr("Contact diameter: ") + U2ExtUStr(ConDiam) + "." + "</li>"
               "<li>" + tr("Contact distance: ") + U2ExtUStr(ConDist) + "." + "</li>"
               "<li>" + tr("Maximum wire width for one wire between contacts: ") + U2ExtUStr(MaxWireWidth1) + ".";
            if (MaxWireWidth1 < MinWireWidth)
               analysis += "<br> " + tr("Not enough space for wires between contacts. ") + tr("Stacked micro vias recommended !");
            analysis += "</li>";
            analysis += "<li>" + tr("Maximum via drill for bone technique: ") + U2ExtUStr(MaxViaDrill) + ".";
            if (MaxViaDrill < MinViaDrill)
               analysis += "<br> " + tr("Not enough space for vias between contacts. ") +  tr("Stacked micro vias recommended !");
            analysis += "</li>";
            analysis += "</ul>";
            dlgLabel(analysis);
         }
         dlgHBoxLayout {
            // Gruppe Wire width: Radiobutton mit RealEdit daneben...
            dlgGroup(tr("Wire and via sizes:")) {
               dlgHBoxLayout {
                  dlgRadioButton(tr("Use specific sizes:"), UseNetClasses);
                  dlgLabel(tr("Wire width:"));
                  // Upper limit ConDist: Just to have a rough limit. Exact maximum would depend
                  // on strategy and DRU settings.
                  dlgRealEdit(dlgWireWidth, U2ExtU(MinWireWidth), U2ExtU(ConDist));
                  dlgLabel(ExtUnitName);
                  dlgLabel(tr("Via drill size:"));
                  dlgRealEdit(dlgViaDrill, dlgMinViaDrill, U2ExtU(ConDist + ConDiam)); // Upper limit: Just to have a rough limit. See above.
                  dlgLabel(ExtUnitName);
               }
               dlgRadioButton(tr("Use net classes"), UseNetClasses);
            }
         }
         dlgGroup(tr("Via strategy")) {
            dlgRadioButton(tr("Make bones"), ElemViaStrat);
            dlgHBoxLayout {
               dlgRadioButton(tr("Stacked micro vias:"), ElemViaStrat);
               dlgLabel(tr("Micro via drill size:"));
               dlgRealEdit(dlgMicroViaDrill, dlgMinMicroViaDrill, U2ExtU(ConDiam)); // Upper limit: Rough value
               dlgLabel(ExtUnitName);
               dlgStretch(2);
            }
         }
         dlgGroup(tr("Coverage")) {
            dlgRadioButton(tr("Contacts with signals"), ElemRouteAll);
            dlgRadioButton(tr("All contacts"), ElemRouteAll);
         }
         dlgHBoxLayout {
            dlgLabel(tr("Escape distance: "));
            dlgRealEdit(dlgEscapeDist, 0, 1000);
            dlgLabel(ExtUnitName);
            dlgStretch(3);
         }
         dlgHBoxLayout {
            dlgPushButton(tr("Help") + "...")
               dlgMessageBox(DlgLang == "de" ? help[1] : help[0]);  // Adjust after translation !
            dlgPushButton(tr("Start")) {
               EscapeDist = ExtU2U(dlgEscapeDist);
               MicroViaDrill = ExtU2U(dlgMicroViaDrill);
               if (!UseNetClasses) {
                  WireWidth = ExtU2U(dlgWireWidth);
                  ViaDrill = ExtU2U(dlgViaDrill);
               }
               Execute();
            }
            dlgPushButton(tr("Cancel")) dlgReject();
         }
      };  // <-- This must be here ! (Ulp-Bug ?)
      else
         Execute();
   }
   else
      dlgMessageBox(tr("Please start from Layout editor !"));
}
