#usage "de:Spiegelt das gesamte Board, spiegelt dann die Bauteile an ihrem Origin zurück und zeichnet die Leiterbahnen " "(Top/Bottom) gespiegelt ohne die Layer zu tauschen.
" "Um zum Beispiel aus dem Layout für einem linken Scheinwerfer das Layout für einen rechten Scheinwerfer zu erzeugen.

" "Bei symmetrischen zweipoligen Bauteilen in senkrechter Ausrichtung reicht es die Bauteile einfach zurück zu spiegeln, " "symmetrische zweipolige Bauteile in waagerechter Ausrichtung müssen zusätzlich um 180° gedreht werden " "um die Position der PADs mit den Leiterbahnen wieder in Einklang zu bringen.

" "Achtung: Asymmetrische und mehrreihige Bauteile wie SOT23 SO14 ... können nicht ohne weiteres gespiegelt und " "die Anbindung der Leiterbahnen an den PADs behalten werden. " "Hier muß der Layouter die gerouteten Wire (Leiterbahnen) teilweise aufripen und neu verlegen.
" "Um die Anschlüsse zu überprüfen wird der DRC gestartet und die die Fehlerliste angezeigt. Jeder Overlap bedeutet " "eine falsch angeschlossene Leiterbahn die entsprechend behandelt werden muß.
" "Je nach Symmetrie des Bauteiles muß entweder das Bauteil verschoben/gedreht und/oder die Wire neu verlegt werden " "(RIPUP und ROUTE).

" "Interessant ist schon die Definition beim Anlegen in der Bibliothek, so wie beim platzieren der Bauteile " "im Board ob man den entsprechenden Platz für die gespiegelte Version freihalten kann. " "Das erspart einiges an Nacharbeit.
" "Unter Umständen mach es Sinn das Bauteil symmetrisch in Bezug auf die PADs und asymmetrisch zu den Packagekonturen " "anzulegen, damit dann der Origin zwischen den Pads liegt und man das Bauteil nur noch drehen mus.

" "Die Meldung ! Alle Signale umwandeln? muß mit Ja beanbtwortet werden, damit die Leiterbahnen aufgeript " "werden können." "

" "Author: alf@cadsoft.de" , "en: Mirrors the whole board, flips the components back to their original positions and draws the " "mirrored traces in top and bottom without changing the original layer.

" "For example, for creating the layout for a headlamp on the left out of the layout of a headlamp on the right side. " "In case you are using symmetrical two-pole components in a vertical orientation it suffices to mirror the components, " "if they are in a horizontal orientation you additionally have to rotate them by 180 degrees in order to conciliate " "the position of the pads and the traces.
" "Note: Asymmetric and multi-row components like SOT23, SO14.... can't be mirrored without mixing up the connections " "between pads and traces. In this case you have to ripup the traces and layout them correctly afterwards.
" "For checking the connections, run the DRC and look into the errors list. Each overlap error shows a mis-connected " "trace you have to take care on.
" "Depending on the symmetry of the component you have to move/rotate it and/or re-route the traces (RIPUP and ROUTE).

" "Think of this already when creating the library element and placing the component in the layout, and try to " "reserve some space for the mirrored component. This can save a lot of effort.
" "I can be senseful to create the component symmetric in terms of the pads and asymmetric in terms of the package " "contours so that the origin is located between the pads and you finally only have to rotate the component.

" "The message Ripup all signals? has to be answered with Yes in order to ripup all signals." "

" "Author: alf@cadsoft.de" #require 5.1100 // THIS PROGRAM IS PROVIDED AS IS AND WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED string Version = "1.0.0"; // 2011-05-25 int test = 0; string Sname = ""; string CheckPos = ""; int CntCheckPos = 0; int CntSymPos = 0; int CntSymPos180 = 0; string cmd, s; int lVisible[]; int useLayer[]; int lcolor[]; int lfill[]; string lNames[] = { " " }; string lockName[]; int cntlock = 0; int maxX = INT_MIN; int minX = INT_MAX; int maxY = INT_MIN; int minY = INT_MAX; int bminx, bmaxx, bminy, bmaxy; string vShape[]; vShape[VIA_SHAPE_SQUARE] = "square"; vShape[VIA_SHAPE_ROUND] = "round"; vShape[VIA_SHAPE_OCTAGON] = "octagon"; vShape[VIA_SHAPE_ANNULUS] = "annulus"; vShape[VIA_SHAPE_THERMAL] = "thermal"; /**** functions ****/ void checkmaxmin(int x1, int x2, int y1, int y2) { if (x1 > maxX) maxX = x1; if (x2 > maxX) maxX = x2; if (y1 > maxY) maxY = y1; if (y2 > maxY) maxY = y2; if (x1 < minX) minX = x1; if (x2 < minX) minX = x2; if (y1 < minY) minY = y1; if (y2 < minY) minY = y2; return; } void checkarc( int x1, int x2, int y1, int y2, int xc, int yc, real angle1, real angle2, real radius) { checkmaxmin( x1, x2, y1, y2 ); if ( angle2 > angle1 + 270.0) { if ( angle1 < 90 ) checkmaxmin( x1 , xc - radius, yc + radius, yc - radius ); else if( angle1 < 180 ) checkmaxmin( xc - radius, xc + radius, y1 , yc - radius ); else if( angle1 < 270 ) checkmaxmin( x1 , xc + radius, yc - radius, yc + radius ); else if( angle1 < 360 ) checkmaxmin( xc + radius, xc - radius, y1 , yc + radius ); } else if( angle2 > angle1 + 180.0) { if ( angle1 < 90 ) checkmaxmin( x1 , xc - radius, yc + radius, y2 ); else if( angle1 < 180 ) checkmaxmin( x1 , xc - radius, yc - radius, y2 ); else if( angle1 < 270 ) checkmaxmin( x1 , xc + radius, yc - radius, y2 ); else if( angle1 < 360 ) checkmaxmin( x1 , xc + radius, yc + radius, y2 ); } else if( angle2 > angle1 + 90.0 ) { if ( angle1 < 90 ) checkmaxmin( x1 , x2 , yc + radius, y2 ); else if( angle1 < 180 ) checkmaxmin( x1 , xc - radius, y1 , y2 ); else if( angle1 < 270 ) checkmaxmin( x1 , x2 , yc - radius, y2 ); else if( angle1 < 360 ) checkmaxmin( x1 , xc + radius, y1 , y2 ); } return; } void lock_elements(void) { for (int lc = 0; lc < cntlock; lc++) { cmd += "LOCK " + lockName[lc] + ";\n"; } return; } void setcolor(void) { string s; sprintf(s, "SET COLOR_LAYER 1 %d;\n", lcolor[16]); // shwap layer color and fill cmd += s; sprintf(s, "SET FILL_LAYER 1 %d;\n", lfill[16]); cmd += s; sprintf(s, "SET COLOR_LAYER 16 %d;\n", lcolor[1]); cmd += s; sprintf(s, "SET FILL_LAYER 16 %d;\n", lfill[1]); cmd += s; sprintf(s, "SET COLOR_LAYER 21 %d;\n", lcolor[22]); cmd += s; sprintf(s, "SET FILL_LAYER 21 %d;\n", lfill[22]); cmd += s; sprintf(s, "SET COLOR_LAYER 22 %d;\n", lcolor[21]); cmd += s; sprintf(s, "SET FILL_LAYER 22 %d;\n", lfill[21]); cmd += s; sprintf(s, "SET COLOR_LAYER 23 %d;\n", lcolor[24]); cmd += s; sprintf(s, "SET FILL_LAYER 23 %d;\n", lfill[24]); cmd += s; sprintf(s, "SET COLOR_LAYER 24 %d;\n", lcolor[23]); cmd += s; sprintf(s, "SET FILL_LAYER 24 %d;\n", lfill[23]); cmd += s; sprintf(s, "SET COLOR_LAYER 25 %d;\n", lcolor[26]); cmd += s; sprintf(s, "SET FILL_LAYER 25 %d;\n", lfill[26]); cmd += s; sprintf(s, "SET COLOR_LAYER 26 %d;\n", lcolor[25]); cmd += s; sprintf(s, "SET FILL_LAYER 26 %d;\n", lfill[25]); cmd += s; sprintf(s, "SET COLOR_LAYER 27 %d;\n", lcolor[28]); cmd += s; sprintf(s, "SET FILL_LAYER 27 %d;\n", lfill[28]); cmd += s; sprintf(s, "SET COLOR_LAYER 28 %d;\n", lcolor[27]); cmd += s; sprintf(s, "SET FILL_LAYER 28 %d;\n", lfill[27]); cmd += s; sprintf(s, "SET COLOR_LAYER 29 %d;\n", lcolor[30]); cmd += s; sprintf(s, "SET FILL_LAYER 29 %d;\n", lfill[30]); cmd += s; sprintf(s, "SET COLOR_LAYER 30 %d;\n", lcolor[29]); cmd += s; sprintf(s, "SET FILL_LAYER 30 %d;\n", lfill[29]); cmd += s; sprintf(s, "SET COLOR_LAYER 31 %d;\n", lcolor[32]); cmd += s; sprintf(s, "SET FILL_LAYER 31 %d;\n", lfill[32]); cmd += s; sprintf(s, "SET COLOR_LAYER 32 %d;\n", lcolor[31]); cmd += s; sprintf(s, "SET FILL_LAYER 32 %d;\n", lfill[31]); cmd += s; sprintf(s, "SET COLOR_LAYER 51 %d;\n", lcolor[52]); cmd += s; sprintf(s, "SET FILL_LAYER 51 %d;\n", lfill[52]); cmd += s; sprintf(s, "SET COLOR_LAYER 52 %d;\n", lcolor[51]); cmd += s; sprintf(s, "SET FILL_LAYER 52 %d;\n", lfill[51]); cmd += s; return; } int checkVia(UL_VIA V) { if (V.start != 1 || V.end != 16) return 1; return 0; } // main if (board) { board(B) { if (argc < 2) { int verror = 0; B.signals(S) S.vias(V) verror += checkVia(V); if (verror) { dlgMessageBox("!Do not use this ULP if used blind or micro via(s)!", "OK"); exit(-1); } bminx = B.area.x1; bmaxx = B.area.x2; bminy = B.area.y1; bmaxy = B.area.y2; B.layers(L) { lNames[L.number] = L.name; lVisible[L.number] = L.visible; useLayer[L.number] = L.used; lcolor[L.number] = L.color; lfill[L.number] = L.fill; } B.wires(W) { if (W.layer == 20) { if (W.arc) { checkarc(W.arc.x1, W.arc.x2, W.arc.y1, W.arc.y2, W.arc.xc, W.arc.yc, W.arc.angle1, W.arc.angle2, W.arc.radius); } else { checkmaxmin( W.x1, W.x2, W.y1, W.y2 ); } } } B.circles(C) { if (C.layer == 20) { checkmaxmin( C.x - C.radius, C.x + C.radius, C.y - C.radius, C.y + C.radius ); } } B.elements(E) { E.package.wires(W) { if (W.layer == 20) { // *** Dimension in Packages *** if (W.arc) { checkarc(W.arc.x1, W.arc.x2, W.arc.y1, W.arc.y2, W.arc.xc, W.arc.yc, W.arc.angle1, W.arc.angle2, W.arc.radius); } else { checkmaxmin( W.x1, W.x2, W.y1, W.y2 ); } } } E.package.circles(C) { if (C.layer == 20) { checkmaxmin( C.x - C.radius, C.x + C.radius, C.y - C.radius, C.y + C.radius ); } } } real mirrorline = (u2mm(maxX) + u2mm(minX)) / 2; sprintf(s, "DISPLAY ALL;\n"); cmd += s; sprintf(s, "GROUP ALL;\n"); cmd += s; cmd += "LOCK (>S 0 0);\n"; // unlock all sprintf(s, "GRID MM FINEST;\nMIRROR (>%.4fmm %.4fmm);\nGRID LAST;\n", mirrorline, u2mm(minY) ); cmd += s; cmd += "SET CONFIRM YES;\n"; // die folgende Abfrage mit Ja beantworten. cmd += "RIPUP;\n"; // alles aufripen bevor die Wire neu gezeichnet werden. cmd += "SET CONFIRM OFF;\n"; B.elements(E) { //checkMirrorRotate(E); int ccnt = 0; int east = 0, south = 0, west = 0, north = 0, un_defined = 0; // Ost- Süd- West- Nord-Ausrichtung der Pads E.package.contacts(C) { ccnt++; if (C.x == E.x && C.y < E.y) south++; // ist im Westen else if (C.x == E.x && C.y > E.y) north++; // ist im Osten else if (C.y == E.y && C.x < E.x) west++; // ist im Süden else if (C.y == E.y && C.x > E.x) east++; // ist im Norden else un_defined++; // undefiniert } if (un_defined) { // nur spiegeln, der Anwender muß sich das ansehen sprintf(s, "MIRROR %s; # un_defined \n", E.name); cmd += s; CntCheckPos++; } else if (east || west) { // spiegen und 180° drehen X-Achse sprintf(s, "MIrror %s; # east || west\nROTATE R180 %s; # east || west\n", E.name, E.name); cmd += s; sprintf(s, "%s\n", E.name); CheckPos += s; CntSymPos180++; } else if (north || south) { // nur spiegeln Y-Achse sprintf(s, "MIRROR %s; # north || south \n", E.name); cmd += s; CntSymPos ++; } else { if (ccnt) { sprintf(s, "MIRROR %s; # any \n", E.name); cmd += s; CntCheckPos++; } // ccnt = 0 - Packages ohne Pads nicht zurückspiegeln, kann Dimesion-Package oder sonstiges sein. } } B.signals(S) { S.wires(W) { if (W.layer != 19) { if (W.layer == 1) cmd += "CHANGE LAYER 1;\n"; else if (W.layer == 16) cmd += "CHANGE LAYER 16;\n"; else { if (dlgMessageBox("!Es werden nur Layer 1 und 16 unterstützt.", "OK", "CANCEL") != 0) exit(-99); } sprintf(s, "WIRE '%s' %.4fmm (%.4fmm %.4fmm ) %+.3f (%.4fmm %.4fmm );\n", S.name, u2mm(W.width), u2mm(W.x1) * -1.0 + mirrorline*2, u2mm(W.y1), W.curve, u2mm(W.x2) * -1.0 + mirrorline*2 , u2mm(W.y2) ); cmd += s; } } S.vias(V) { string vFlag = ""; if (V.flags) vFlag = "STOP"; sprintf(s, "CHANGE DRILL %.4fmm;\n", u2mm(V.drill)); sprintf(s, "VIA '%s' %.4fmm %s %d-%d %s (%.4fmm %.4fmm);\n", S.name, u2mm(V.diameter[16]), vShape[V.shape[16]], V.start, V.end, vFlag, u2mm(V.x) * -1.0 + mirrorline*2, u2mm(V.y) ); cmd += s; } S.polygons(P) { // swap polygon to origin layer P.wires(W) { sprintf(s, "CHANGE LAYER %d (%.4fmm %.4fmm);\n", W.layer, u2mm(W.x1) * -1.0 + mirrorline*2, u2mm(W.y1)); cmd += s; break; } } } cmd += ";\n"; B.elements(E) if (E.locked) cmd += "LOCK " + E.name + ";\n"; // 2009-12-01 cmd += "DISPLAY NONE "; for(int l = 1; l < 256; l++) { if (lNames[l]) { // Layer defined if (lVisible[l]) { if (l == 1 || l == 16 || l == 21 || l == 22 || l == 23 || l == 24 || l == 25 || l == 26 || l == 27 || l == 28 || l == 29 || l == 30 || l == 31 || l == 32 || l == 51 || l == 52 ) { if (l == 1) sprintf(s, " %d", 16); // 2009-12-01 shwap visible layers else if (l == 16) sprintf(s, " %d", 1); else if (l == 21) sprintf(s, " %d", 22); else if (l == 22) sprintf(s, " %d", 21); else if (l == 23) sprintf(s, " %d", 24); else if (l == 24) sprintf(s, " %d", 23); else if (l == 25) sprintf(s, " %d", 26); else if (l == 26) sprintf(s, " %d", 25); else if (l == 27) sprintf(s, " %d", 28); else if (l == 28) sprintf(s, " %d", 27); else if (l == 29) sprintf(s, " %d", 30); else if (l == 30) sprintf(s, " %d", 29); else if (l == 31) sprintf(s, " %d", 32); else if (l == 32) sprintf(s, " %d", 31); else if (l == 51) sprintf(s, " %d", 52); else if (l == 52) sprintf(s, " %d", 51); } else sprintf(s, " %d", l); cmd += s; } } } cmd += ";\n"; } Sname = filesetext(B.name, "~mirror~script~.scr"); string saveas = dlgFileSave("Save as", B.name, "*.brd"); if (saveas) { sprintf(s, "WRITE '@%s';\n", saveas); // 2011-05-25 save as cmd += s; } else { cmd += "RUN ulpmessage 'Do not forgot to save the board by a new name.

Vergessen Sie nicht die Datei unter einem anderen Namen zu speichern.';\n"; } cmd += "DRC ; \n ERRORS "; output(Sname, "wtD") printf("%s", cmd); } } if (CheckPos) { sprintf(s, "%d Elements mirrord / Bauteile bespiegelt.\n%d Elements mirrord an 180 degree rotated / Bauteile gespiegelt und 180° gedreht.\n\n%d Elements to check, see list / Bauteile zum überpüfen nach Liste.\n", CntSymPos, CntSymPos180, CntCheckPos ); dlgDialog("Check routig of elements") { dlgHBoxLayout dlgSpacing(800); dlgLabel(usage); dlgSpacing(12); dlgLabel(s); dlgHBoxLayout { dlgVBoxLayout dlgSpacing(300); dlgTextView(CheckPos); } dlgHBoxLayout { dlgStretch(1); dlgPushButton("+OK") dlgAccept(); dlgPushButton("-CANCEL") { dlgReject(); exit(-1); } dlgStretch(1); } }; } if (test) { dlgDialog("Test") { dlgTextEdit(cmd); dlgHBoxLayout { dlgPushButton("ok") dlgAccept(); dlgPushButton("esc") { dlgReject(); exit(-2); } } }; } exit("SCRIPT '" + Sname + "'");