#usage "en:export 3d-data from board to IDF-FileFormat

" "More info can be found here: .../eagle.../doc/generate_3d_data_eng.pdf
" "Original author: Uploaded by Neubacher Andreas from Commend International GmbHh
" "co-Author: alf@cadsoft.de

" , "de:export 3d-data from board to IDF-FileFormat

" "Im Layer 50 muß eine Kopie der Boardkontour des Layer 20 angelegt werden. Die Wirebreite in Micron " "ergibt die Boarddicke in mm.
" "Die Bordkontour kann entweder im Board lebst oder in einem Package angelegt werden, es muß nur die " "obenstehende Bedingung eingehalten werden.
" "Um in einem Board einen Durchbruch zu erzeugen, kann man entweder im Board selbst oder in einem Package " "eine Kontur mit der Wirebreite von 0 im Layer 50 anlegen.
" "Original author: Uploaded by Neubacher Andreas from Commend International GmbHh
" "co-Author: alf@cadsoft.de

" #require 6.0600 string UlpVersion = "1.9"; string Docuemantpath = "Weiter Infos finden sie im Dokumentenordner unter " + path_doc[0] + "/generate_3d_data_eng.pdf
"; //############################################################################ // Author: Andy Neubacher // Version | Date | log //---------+------------+----------------------------------------------------- // v1.9 | 2014-04-08 | - Now can change the filedirectory an filename // select Top and or Bottom side of parts to export // v1.8 | 2014-04-01 | - Cutout und Koordinaten berichtigt (nur beim ersten Wire, Anfang und Endkoordinate ausgeben) // dann immer nur die 2. Koordinate bis die erste Koordinate wieder erreicht ist. // - LoopCounter für geschlossene Konturen (Durchbruch/Cutout) wieder berichtigt. // // v1.7 | 2014-01-20 | - draw also pad-holes // v1.6 | 2013-12-19 | - LoopCounter für geschlossene Konturen (Durchbruch/Cutout). // Die Bedingung für die Erkennung der einzelnen Konturen ist: // 1. Die allererste Kontour im Layer 50 ist die Boardkontur, // was bedeutet, diese Kontour muss als allererste Kontur gezeichnet werden. // 2. Jede weitere Kontur wird als Cutout betrachtet, und es wird keine Überprüfung // vorgenommen, ob diese Kontur innerhalb oder ausserhalb der Board-Dimension ist. // 3. Jede Kontur muss durchgehend gezeichnet und geschlossen sein. // Das bedeutet, man darf innerhalb einer Kontur nicht an beliebigen Punkten // das (WIRE) zeichnen unterbrechen, und an einer anderen Stelle wieder beginnen, // also dem vorher unterbrochenen *Ende* entgegen zeichnen. // // v1.5 | 2013-12-18 | - file renamed and now without version, for use in Text-Menu // | | - draw board cutout // | | - ++CutoutNumSegments change to CutoutNumSegments++ // alf@cadsoft.de // v1.4 | 2013-12-11 | - correct Board outline Curve // | | - correct Pre- to Post-Increment in all Functions // | | - fixed by Angelo.Cuccato@bogen-electronic.com // v1.3 | 2013-03-14 | - correct using ARC coorinates // v1.2 | 2013-01-14 | - Link in description to download page - fixes by alf@cadsoft.de // v1.1 | 2012-12-04 | - korrect Loop Label for contours in layer Dimension - fixes by alf@cadsoft.de // v1.0 | 2012-08-20 | - more resolution %.6f of coordinates - fixes by alf@cadsoft.de // v0.9 | 2012-02-08 | - used function u2mm() for Z-axis value - fixes by alf@cadsoft.de // | | // v0.8 | 2011-05-31 | - changed characters that caused import problems // | | - changed output file extension to .emn and .emp // | | - fixes by morten@riftlabs.com // | | // v0.7 | 2006-10-24 | - outline of board can also be drawn direct in brd-file // | | - any hole is taken from devices and board direct // | | - close open polygons on request // | | - drill vias on request // | | - bugfix : error if device contains more than one poly on top and bottom // | | - bugfix : sorting of points possible failed if polygon was open // | | - bugfix : partheights on bottomlayer were ignored // v0.6 | 2006-08-31 | now multiple partheights are possible // v0.5 | 2006-08-25 | modification to 'RIR-standard' // v0.4 | 2005-11-09 | 1st export of a real board+parts successfull // v0.3 | 2005-11-03 | correct mirrors at top and bottom side // v0.2 | 2005-10-21 | 1st working IDF-data // v0.1 | 2005-09-30 | start of project //---------+------------+----------------------------------------------------- // // HOWTO: // Create your board outline on layer 50. // In your library, draw the component outlines on layer 57 (tCad) and use the line thickness to set component height. // 1000 mic = 1 mm (e.g. if your grid is in mm, then a line thickness of 0.001 equals a component height of 1 mm). // More info can be found here: ftp://ftp.cadsoft.de/eagle/userfiles/ulp/generate_3d_data_eng.pdf // //############################################################################ int Layer3dBoardDimension = 50; int tLayer3Ddata = 57; int bLayer3Ddata = 58; int SelectSide[] = { 1, 1 }; // select Part output on side Top / Bottom int maxCutoutLineWidth = 0; // -->> is linewidth 0.0 -> line is a cutout ?? string UlpName = filename(argv[0]); // gets ulp name // partoptions int NumParts; // number of parts string PartName[]; // name of part // vars for polygon calculation int NumSegments = 0; // number of points int PointX1[]; // start x int PointY1[]; // start y int PointX2[]; // end x int PointY2[]; // end y int SegmentType[]; // LINE, ARC, CIRCLE real SegmentCurve[]; // used by ARCs : - if drawn CCW, + if drawn CW int SegmentWidth[]; // linewitdh = partheight int CutoutNumSegments = 0; int CutoutPointX1[]; // start x int CutoutPointY1[]; // start y int CutoutPointX2[]; // end x int CutoutPointY2[]; // end y int CutoutSegmentType[]; // LINE, ARC, CIRCLE int CutoutSegmentCurve[]; // used by ARCs : -180 if drawn CCW, +180 if drawn CW int CutoutSegmentWidth[]; // linewitdh = partheight int LoopCounter = 0; // 2015-04-02 für Boardoutline und Cutoutline enum { LINE = 1, ARC = 2, CIRCLE = 3 }; enum { CUTOUT = 0, OUTLINE = -1 }; enum { false = 0, true = 1}; string Type[]; Type[0] = "0"; Type[LINE] = "LINE"; Type[ARC] = "ARC"; Type[CIRCLE] = "CIRCLE"; // vars for library int NumPacInLib = 0; // number of pacs in library int PacIn_LIB_heights[]; // if 0 = only one partheight, if != 0 more than one height real PacIn_LIB_angle[]; // rotation of part in board string PacIn_LIB_name[]; // names of pack's in library int PacIn_LIB_Device_mountside[]; // TOP, BOTTOM string PacIn_LIB_SubPart_mountside[]; // mountside of subparts int NumCutouts = 0; // number of holes in the pcb (drills, millings) int OutlineInDeviceFound = false; // is outline drawn in device symbol int CloseOpenPoly = false; // close polygon if gap is smaller than ...mm real MaxGapWidth = 0.1; // value of maximum gap to close int DrillViaHoles = true; // drill via-holes in pcb real MinViaHoleDia = 0.254; // diameter of drill //////////////////////////////////////////////////// void CollectPartData(UL_BOARD brd) { // read out all parts NumParts = 0; brd.elements(E) { PartName[NumParts] = E.name; PacIn_LIB_angle[NumParts] = -1; // fill with invalid rotation and ... PacIn_LIB_Device_mountside[NumParts] = -1; // fill with invalid mountside (mirror) for "void IDF_LibaryElectrical(UL_BOARD BRD)" function PacIn_LIB_heights[NumParts] = 0; // part is only one package NumParts++; } } //////////////////////////////////////////////////// void IDF_Circle(int x1, int y1, int x2, int y2, int type) { // output circle to file in IDF format int loopcount = 0; if(type == CUTOUT) loopcount = NumCutouts; // 2013-12-19 printf("%d %.6f %.6f 0\n", loopcount, u2mm(x1), u2mm(y1)); printf("%d %.6f %.6f 360\n", loopcount, u2mm(x2), u2mm(y2)); return; } //////////////////////////////////////////////////// void IDF_Arc(int x1, int y1, int x2, int y2, real angle, int type, int first) { // output arc to file in IDF format int loopcount = 0; if(type == CUTOUT) loopcount = NumCutouts; // 2013-12-19 if (first) printf("%d %.6f %.6f 0\n", loopcount, u2mm(x1), u2mm(y1)); printf("%d %.6f %.6f %.1f\n", loopcount, u2mm(x2), u2mm(y2), angle); return; } //////////////////////////////////////////////////// void IDF_Line(int x1, int y1, int x2, int y2, int type, int first) { // output line to file in IDF format int loopcount = 0; if(type == CUTOUT) loopcount = NumCutouts; // 2013-12-19 if (first) printf("%d %.6f %.6f 0\n", loopcount, u2mm(x1), u2mm(y1)); printf("%d %.6f %.6f 0\n", loopcount, u2mm(x2), u2mm(y2)); return; } //////////////////////////////////////////////////// void OutputLines(int originx, int originy, int type) { // write to file int first = 1; int i = 0; int startx = PointX1[i]; int starty = PointY1[i]; for(i = 0; i < NumSegments; i++) { NumCutouts = LoopCounter; switch(SegmentType[i]) { case LINE : IDF_Line(PointX1[i]-originx, PointY1[i]-originy, PointX2[i]-originx, PointY2[i]-originy, type, first); first = 0; // nur die erste Koordinate wird mit xy1 und xy2 ausgegeben, alles weiteren nur yx2, // bis zum Ende sprich schliessen der Schleife break; case ARC : IDF_Arc(PointX1[i]-originx, PointY1[i]-originy, PointX2[i]-originx, PointY2[i]-originy, SegmentCurve[i], type, first); first = 0; // nur die erste Koordinate wird mit xy1 und xy2 ausgegeben, alles weiteren nur yx2, // bis zum Ende sprich schliessen der Schleife break; case CIRCLE : IDF_Circle(PointX1[i]-originx, PointY1[i]-originy, PointX2[i]-originx, PointY2[i]-originy, type); LoopCounter++; NumCutouts = LoopCounter; startx = PointX1[i+1]; starty = PointY1[i+1]; first = 1; break; default : string s; sprintf(s, "Unknown type in Segment %d", i); if (dlgMessageBox(s, "OK", "esc") != 0) exit(-182); } if (startx == PointX2[i] && starty == PointY2[i]) { first = 1; // beim Anfang eine neuen Kontour wieder beie Koordinatenpaare ausgeben startx = PointX1[i+1]; starty = PointY1[i+1]; LoopCounter++; // die Schleife (Kontur) ist geschlossen. Bedingung siehe in oben. } } return; } //////////////////////////////////////////////////// void OutputBoardOutLines(int originx, int originy, int type) { // write to file :: type ist hier irrelevant int first = 1; int i = 0; int startx = PointX1[i]; int starty = PointY1[i]; for(i = 0; i < NumSegments; i++) { NumCutouts = LoopCounter; switch(SegmentType[i]) { // 2013-12-18 case LINE : IDF_Line(PointX1[i]-originx, PointY1[i]-originy, PointX2[i]-originx, PointY2[i]-originy, CUTOUT, first); first = 0; // nur die erste Koordinate wird mit xy1 und xy2 ausgegeben, alles weiteren nur yx2, // bis zum Ende sprich schliessen der Schleife break; case ARC : IDF_Arc(PointX1[i]-originx, PointY1[i]-originy, PointX2[i]-originx, PointY2[i]-originy, SegmentCurve[i], CUTOUT, first); first = 0; // nur die erste Koordinate wird mit xy1 und xy2 ausgegeben, alles weiteren nur yx2, // bis zum Ende sprich schliessen der Schleife break; case CIRCLE : IDF_Circle(PointX1[i]-originx, PointY1[i]-originy, PointX2[i]-originx, PointY2[i]-originy, CUTOUT); LoopCounter++; NumCutouts = LoopCounter; startx = PointX1[i+1]; starty = PointY1[i+1]; first = 1; break; default : string s; sprintf(s, "Unknown type in Segment %d", i); } if (startx == PointX2[i] && starty == PointY2[i]) { first = 1; // beim Anfang eine neuen Kontour wieder beie Koordinatenpaare ausgeben startx = PointX1[i+1]; starty = PointY1[i+1]; LoopCounter++; // die Schleife (Kontur) ist geschlossen. Bedingung siehe in oben. } } return; } void OutputBoardCutOutLines(int originx, int originy, int type) { // write to file :: type ist hier irrelevant int first = 1; int i = 0; int startx = CutoutPointX1[i]; int starty = CutoutPointY1[i]; for(i = 0; i < CutoutNumSegments; i++) { NumCutouts = LoopCounter; switch(CutoutSegmentType[i]) { // 2013-12-18 case LINE : IDF_Line(CutoutPointX1[i]-originx, CutoutPointY1[i]-originy, CutoutPointX2[i]-originx, CutoutPointY2[i]-originy, CUTOUT, first); first = 0; // nur die erste Koordinate wird mit xy1 und xy2 ausgegeben, alles weiteren nur yx2, // bis zum Ende sprich schliessen der Schleife break; case ARC : IDF_Arc(CutoutPointX1[i]-originx, CutoutPointY1[i]-originy, CutoutPointX2[i]-originx, CutoutPointY2[i]-originy, CutoutSegmentCurve[i], CUTOUT, first); first = 0; // nur die erste Koordinate wird mit xy1 und xy2 ausgegeben, alles weiteren nur yx2, // bis zum Ende sprich schliessen der Schleife break; case CIRCLE : IDF_Circle(CutoutPointX1[i]-originx, CutoutPointY1[i]-originy, CutoutPointX2[i]-originx, CutoutPointY2[i]-originy, CUTOUT); LoopCounter++; NumCutouts = LoopCounter; startx = CutoutPointX1[i+1]; starty = CutoutPointY1[i+1]; first = 1; break; default : string s; sprintf(s, "Unknown type in Segment %d", i); } if (startx == CutoutPointX2[i] && starty == CutoutPointY2[i]) { first = 1; // beim Anfang eine neuen Kontour wieder beie Koordinatenpaare ausgeben startx = CutoutPointX1[i+1]; starty = CutoutPointY1[i+1]; LoopCounter++; // die Schleife (Kontur) ist geschlossen. Bedingung siehe in oben. } } return; } //////////////////////////////////////////////////// int IsPointEqual (int x1, int y1, int x2, int y2) { // really need a func-description ?? if(x1 == x2 && y1 == y2) return true; return false; } //////////////////////////////////////////////////// real getPartHeight() { // returns maximum partlevel real ret = u2mm(SegmentWidth[0])*1000.0; // 2012-02-08 return ret; } //////////////////////////////////////////////////// int IsPointInCircle(int target_x, int target_y, int circle_x, int circle_y, real radius) { real dist,a,b; a = abs(target_y-circle_y); b = abs(target_x-circle_x); // calculate distance dist = sqrt(a*a + b*b); if(dist <= radius) return true; return false; } //////////////////////////////////////////////////// //int GetArcOptions(UL_WIRE w) { // is arc drawn CW or CCW // if(IsPointEqual(w.x1, w.y1, w.x2, w.y2)) return (w.arc.angle1 - w.arc.angle2); // else return (w.arc.angle2 - w.arc.angle1); //} //////////////////////////////////////////////////// int DataOnLayerPresent(UL_ELEMENT E, int layer) { E.package.circles(CIR) { // circle found if(CIR.layer == layer && CIR.width > maxCutoutLineWidth) return true; } E.package.wires(W) { if(W.arc) { // arc found if(W.layer == layer && W.width > maxCutoutLineWidth) return true; } else { // straight line found if(W.layer == layer && W.width > maxCutoutLineWidth) return true; } } return false; // no 3d-data on given layer found } //////////////////////////////////////////////////// void CollectRemainingPoints(int start, int nr) { int i; for(i = 0; i < nr; i++) { PointX1[i] = PointX1[i+start]; PointY1[i] = PointY1[i+start]; PointX2[i] = PointX2[i+start]; PointY2[i] = PointY2[i+start]; SegmentType[i] = SegmentType[i+start]; SegmentCurve[i] = SegmentCurve[i+start]; SegmentWidth[i] = SegmentWidth[i+start]; } NumSegments = nr; return; } //////////////////////////////////////////////////// int SortPointsEx (void) { // sort all points to a continous polygon int i; int ptfound; int ResultPointX1[]; // start x int ResultPointY1[]; // start y int ResultPointX2[]; // end x int ResultPointY2[]; // end y int ResultSegmentType[]; // SegmentType int ResultSegmentCurve[]; // options of segment (only used for ARCs) int ResultSegmentWidth[]; // width of line int PointUsed[]; enum { NOT_FOUND = 0, FOUND = 1, ERROR = 2 }; enum { NOT_USED = 0, USED = 1 }; for(i = 0; i < NumSegments; i++) PointUsed[i] = NOT_USED; ResultPointX1[0] = PointX1[0]; // startpoint ResultPointY1[0] = PointY1[0]; ResultPointX2[0] = PointX2[0]; ResultPointY2[0] = PointY2[0]; ResultSegmentType[0] = SegmentType[0]; ResultSegmentWidth[0] = SegmentWidth[0]; ResultSegmentCurve[0] = SegmentCurve[0]; PointUsed[0] = USED; for(int x=1; x= (NumSegments-1)) { if(ptfound != FOUND) ptfound = ERROR; } } while(ptfound == NOT_FOUND); // if point was not found -> end sorting of points if (ptfound == ERROR) break; } // for NumSegments // count how many points are not used int n; int NumPointsUsed = 0; for(i=0; i < NumSegments; i++) { if(PointUsed[i] == USED) NumPointsUsed++; } // copy not used points to end of ResultPoint[] for(i = NumSegments-1; i >= NumPointsUsed; i--) { for(n = 0; n < NumSegments; n++) { if(PointUsed[n] == NOT_USED) { // copy if point is not used PointUsed[n] = USED; ResultPointX1[i] = PointX1[n]; ResultPointY1[i] = PointY1[n]; ResultPointX2[i] = PointX2[n]; ResultPointY2[i] = PointY2[n]; ResultSegmentCurve[i] = SegmentCurve[n]; ResultSegmentType[i] = SegmentType[n]; ResultSegmentWidth[i] = SegmentWidth[n]; break; } } } // copy back sorted list for(i=0; i search for points within circle if(IsPointInCircle(PointX1[i], PointY1[i], PointX2[LastPoint], PointY2[LastPoint], mm2u(MaxGapWidth))) { // 2012-02-08 PointX1[NumSegments] = PointX2[LastPoint]; // add new line with start at last-point PointY1[NumSegments] = PointY2[LastPoint]; PointX2[NumSegments] = PointX1[i]; PointY2[NumSegments] = PointY1[i]; SegmentType[NumSegments] = LINE; SegmentWidth[NumSegments] = SegmentWidth[LastPoint]; SegmentCurve[NumSegments++] = 0; // 2013-12-11 LineAdded = true; break; // point within circle found and added -> escape from "for"-loop } if(IsPointInCircle(PointX2[i], PointY2[i], PointX2[LastPoint], PointY2[LastPoint], mm2u(MaxGapWidth))) { // 2012-02-08 PointX1[NumSegments] = PointX2[LastPoint]; // add new line with start at last-point PointY1[NumSegments] = PointY2[LastPoint]; PointX2[NumSegments] = PointX2[i]; PointY2[NumSegments] = PointY2[i]; SegmentType[NumSegments] = LINE; SegmentWidth[NumSegments] = SegmentWidth[LastPoint]; SegmentCurve[NumSegments++] = 0; // 2013-12-11 LineAdded = true; break; // point within circle found and added -> escape from "for"-loop } } } } while(LineAdded); // resort points and close gaps if new line added // finished correct -> but is polygon closed ?!? (firstpoint and lastpoint may no be the same coordinate !) if(IsPointEqual(PointX1[0], PointY1[0], PointX2[LastPoint], PointY2[LastPoint]) == false) { // poly closed ? if(IsPointInCircle(PointX1[0], PointY1[0], PointX2[LastPoint], PointY2[LastPoint], mm2u(MaxGapWidth))) { // 2012-02-08 PointX1[NumSegments] = PointX2[LastPoint]; // add new line with start at last-point PointY1[NumSegments] = PointY2[LastPoint]; PointX2[NumSegments] = PointX1[0]; PointY2[NumSegments] = PointY1[0]; SegmentType[NumSegments] = LINE; SegmentWidth[NumSegments] = SegmentWidth[LastPoint]; SegmentCurve[NumSegments++] = 0; // 2013-12-11 } } return; } //////////////////////////////////////////////////// int SortPoints(string ElementName) { // sort all points to a continous polygon int LastPoint, RemainingPoints; // close open polygon if (CloseOpenPoly) CloseOpenPolygon(); // do final sorting RemainingPoints = SortPointsEx(); // check if polygon is closed LastPoint = NumSegments - RemainingPoints - 1; if (LastPoint < 0) { dlgMessageBox("!Negativ Index in function SortPoints().", "OK"); exit(-472); } if(IsPointEqual(PointX1[0], PointY1[0], PointX2[LastPoint], PointY2[LastPoint]) == false) { if(SegmentType[0] != CIRCLE) { string x; int pt_x, pt_y; pt_x = PointX2[LastPoint]; pt_y = PointY2[LastPoint]; sprintf(x,"!%s contains an open polygon !\n\ncoordinate : X= %.6f[mm], Y= %.6f[mm]", ElementName, u2mm(pt_x), u2mm(pt_y)); if (dlgMessageBox(x, "OK", "Show") != 0) { sprintf(x, "WIN (%.6fmm %.6fmm);\n", u2mm(pt_x), u2mm(pt_y)); exit(x); } } } return RemainingPoints; } //////////////////////////////////////////////////// void IDF_LibaryHeader(UL_BOARD BRD) { //-> create header of libary int t = time(); string date; string sourceID; sprintf(date, "%d/%02d/%02d.%02d:%02d:%02d", t2year(t), t2month(t), t2day(t), t2hour(t), t2minute(t), t2second(t)); sprintf(sourceID, "Commend International >%s %s %s<", UlpName, UlpVersion, EAGLE_SIGNATURE); // 2013-01-14 printf(".HEADER\n"); printf("LIBRARY_FILE 3.0 \"%s\" %s 1\n", sourceID, date); printf(".END_HEADER\n"); return; } //////////////////////////////////////////////////// int CollectLibOutlineSegments(UL_ELEMENT E, int layer) { // get all segments of elements on 3D-layer NumSegments=0; E.package.circles(CIR) { // create circles if(CIR.layer == layer && CIR.width > maxCutoutLineWidth) { PointX1[NumSegments] = CIR.x; PointY1[NumSegments] = CIR.y; PointX2[NumSegments] = CIR.x + CIR.radius; PointY2[NumSegments] = CIR.y; SegmentType[NumSegments] = CIRCLE; SegmentWidth[NumSegments] = CIR.width; SegmentCurve[NumSegments++] = 0; // 2013-12-11 } } E.package.wires(W) { if(W.curve) { // create arcs if(W.layer == layer && W.width > maxCutoutLineWidth) { PointX1[NumSegments] = W.x1; // 2013-03-14 use wire coordinate, not arc! PointY1[NumSegments] = W.y1; PointX2[NumSegments] = W.x2; PointY2[NumSegments] = W.y2; SegmentType[NumSegments] = ARC; SegmentWidth[NumSegments] = W.width; SegmentCurve[NumSegments++] = W.curve; //GetArcOptions(W); } } else { // create straight lines if(W.layer == layer && W.width > maxCutoutLineWidth) { PointX1[NumSegments] = W.x1; PointY1[NumSegments] = W.y1; PointX2[NumSegments] = W.x2; PointY2[NumSegments] = W.y2; SegmentType[NumSegments] = LINE; SegmentWidth[NumSegments] = W.width; SegmentCurve[NumSegments++] = 0; // 2013-12-11 } } } if(NumSegments != 0) { // show warningbox if element contains no 3d-data if(layer == bLayer3Ddata) { // is given layer the bottom layer -> mirror for(int i=0; i create electrical parts (R, C, IC, ...) int i,x,inlib,NrOfPolygon; int RemainingPoints; string MountSide[] = {"TOP", "BOTTOM"}; int layer[] = { tLayer3Ddata, bLayer3Ddata }; for(i=0;i subract given offsets -> E.x, E.y printf(".END_ELECTRICAL\n"); CollectRemainingPoints(NumSegments, RemainingPoints); PacIn_LIB_heights[NumPacInLib] = NrOfPolygon; // 0=package has only 1 poly; >0 package has more than one poly-outline PacIn_LIB_name[NumPacInLib] = E.package.name; // mark that package is now in LIB-file PacIn_LIB_Device_mountside[NumPacInLib] = E.mirror; // safe top- or bottom-side PacIn_LIB_SubPart_mountside[NumPacInLib] = MountSide[tb]; // safe mountside of subparts PacIn_LIB_angle[NumPacInLib++] = E.angle; // safe angle of partplacement if(RemainingPoints != 0 || NrOfPolygon != 0) NrOfPolygon++; } while(RemainingPoints != 0); // get multiple outline with multiple partheights } } } } } return; } //////////////////////////////////////////////////// void IDF_LibaryMechanical(UL_BOARD BRD) { //-> create mechanical parts (drills, holes, ...) // not implemented yet return; } //////////////////////////////////////////////////// int CollectBoardOutlineSegments(UL_BOARD BRD) { //++ get all segments of boardoutline NumSegments = 0; // first search board outline in board direct BRD.circles(CIR) { if(CIR.layer == Layer3dBoardDimension) { if (CIR.width > maxCutoutLineWidth) { PointX1[NumSegments] = CIR.x; PointY1[NumSegments] = CIR.y; PointX2[NumSegments] = CIR.x + CIR.radius; PointY2[NumSegments] = CIR.y; SegmentType[NumSegments] = CIRCLE; SegmentWidth[NumSegments] = CIR.width; SegmentCurve[NumSegments++] = 0; } else { CutoutPointX1[CutoutNumSegments] = CIR.x; CutoutPointY1[CutoutNumSegments] = CIR.y; CutoutPointX2[CutoutNumSegments] = CIR.x + CIR.radius; CutoutPointY2[CutoutNumSegments] = CIR.y; CutoutSegmentType[CutoutNumSegments] = CIRCLE; CutoutSegmentWidth[CutoutNumSegments] = CIR.width; CutoutSegmentCurve[CutoutNumSegments++] = 0; } } } BRD.wires(W) { if(W.layer == Layer3dBoardDimension) { if (W.width > maxCutoutLineWidth) { if(W.curve) { // create arcs board-outline board PointX1[NumSegments] = W.x1; // 2013-03-14 use direct wire coordinate, not arc-coordinate PointY1[NumSegments] = W.y1; PointX2[NumSegments] = W.x2; PointY2[NumSegments] = W.y2; SegmentType[NumSegments] = ARC; SegmentWidth[NumSegments] = W.width; SegmentCurve[NumSegments] = W.curve; // GetArcOptions(W); } else { // create straight lines board-outline board PointX1[NumSegments] = W.x1; PointY1[NumSegments] = W.y1; PointX2[NumSegments] = W.x2; PointY2[NumSegments] = W.y2; SegmentType[NumSegments] = LINE; SegmentWidth[NumSegments] = W.width; SegmentCurve[NumSegments] = 0; } NumSegments++; } else { if(W.curve) { // create arcs board-outline board CutoutPointX1[CutoutNumSegments] = W.x1; CutoutPointY1[CutoutNumSegments] = W.y1; CutoutPointX2[CutoutNumSegments] = W.x2; CutoutPointY2[CutoutNumSegments] = W.y2; CutoutSegmentType[CutoutNumSegments] = ARC; CutoutSegmentWidth[CutoutNumSegments] = W.width; CutoutSegmentCurve[CutoutNumSegments] = W.curve; //GetArcOptions(W); } else { CutoutPointX1[CutoutNumSegments] = W.x1; CutoutPointY1[CutoutNumSegments] = W.y1; CutoutPointX2[CutoutNumSegments] = W.x2; CutoutPointY2[CutoutNumSegments] = W.y2; CutoutSegmentType[CutoutNumSegments] = LINE; CutoutSegmentWidth[CutoutNumSegments] = W.width; CutoutSegmentCurve[CutoutNumSegments] = 0; } CutoutNumSegments++; } } } // second search board outline in packages BRD.elements(E) { E.package.circles(CIR) { // create board-outline of package-libary if(CIR.layer == Layer3dBoardDimension) { PointX1[NumSegments] = CIR.x; PointY1[NumSegments] = CIR.y; PointX2[NumSegments] = CIR.x + CIR.radius; PointY2[NumSegments] = CIR.y; SegmentType[NumSegments] = CIRCLE; SegmentWidth[NumSegments] = CIR.width; SegmentCurve[NumSegments++] = 0; } } } BRD.elements(E) { E.package.wires(W) { if(W.layer == Layer3dBoardDimension) { if (W.width > maxCutoutLineWidth) { if(W.curve) { // create arcs board-outline of package-libary PointX1[NumSegments] = W.x1; PointY1[NumSegments] = W.y1; PointX2[NumSegments] = W.x2; PointY2[NumSegments] = W.y2; SegmentType[NumSegments] = ARC; SegmentWidth[NumSegments] = W.width; SegmentCurve[NumSegments] = W.curve; // GetArcOptions(W); } else { // create straight lines board-outline of package-libary PointX1[NumSegments] = W.x1; PointY1[NumSegments] = W.y1; PointX2[NumSegments] = W.x2; PointY2[NumSegments] = W.y2; SegmentType[NumSegments] = LINE; SegmentWidth[NumSegments] = W.width; SegmentCurve[NumSegments] = 0; } NumSegments++; } else { if(W.curve) { // create arcs board-cutout of package-libary CutoutPointX1[CutoutNumSegments] = W.x1; CutoutPointY1[CutoutNumSegments] = W.y1; CutoutPointX2[CutoutNumSegments] = W.x2; CutoutPointY2[CutoutNumSegments] = W.y2; CutoutSegmentType[CutoutNumSegments] = ARC; CutoutSegmentWidth[CutoutNumSegments] = W.width; CutoutSegmentCurve[CutoutNumSegments] = W.curve; //GetArcOptions(W); } else { // create line board-cutout of package-libary CutoutPointX1[CutoutNumSegments] = W.x1; CutoutPointY1[CutoutNumSegments] = W.y1; CutoutPointX2[CutoutNumSegments] = W.x2; CutoutPointY2[CutoutNumSegments] = W.y2; CutoutSegmentType[CutoutNumSegments] = LINE; CutoutSegmentWidth[CutoutNumSegments] = W.width; CutoutSegmentCurve[CutoutNumSegments] = 0; } CutoutNumSegments++; } } } } if(NumSegments == 0) { string error; sprintf(error,"!No board-outline on layer %d found !", Layer3dBoardDimension); dlgMessageBox(error); exit(-771); } return NumSegments; } //////////////////////////////////////////////////// void IDF_BoardHeader(UL_BOARD BRD) { //-## create header of boardfile int t = time(); string date; string sourceID; sprintf(date, "%d/%02d/%02d.%02d:%02d:%02d", t2year(t), t2month(t), t2day(t), t2hour(t), t2minute(t), t2second(t)); sprintf(sourceID, "Commend International >%s %s %s<", UlpName, UlpVersion, EAGLE_SIGNATURE); printf(".HEADER\n"); printf("BOARD_FILE 3.0 \"%s\" %s 1\n", sourceID, date); printf("\"%s\" MM\n", filename(BRD.name)); printf(".END_HEADER\n"); return; } //////////////////////////////////////////////////// void IDF_BoardOutline(UL_BOARD BRD) { //-## create outline of board // get outline CollectBoardOutlineSegments(BRD); SortPoints("Board-Outline"); // boardname // draw outline printf(".BOARD_OUTLINE UNOWNED\n"); printf("%.6f\n", getPartHeight()); OutputBoardOutLines(0, 0, OUTLINE); // subract given offsets -> E.x, E.y OutputBoardCutOutLines(0, 0, OUTLINE); printf(".END_BOARD_OUTLINE\n"); LoopCounter = 0; return; } //////////////////////////////////////////////////// void IDF_BoardPlaceParts(UL_BOARD BRD) { //-## mount parts on board int i, inlib; string MountSide[] = {"TOP","BOTTOM"}; printf(".PLACEMENT\n"); BRD.elements(E) { inlib = 0; for(i=0; i more than one part on same x/y position int lib_start = i; do { printf("%s_ANGLE%.1f_%s.%d CI_LIB %s.%d\n", E.package.name, E.angle, PacIn_LIB_SubPart_mountside[lib_start], PacIn_LIB_heights[lib_start], E.name, PacIn_LIB_heights[lib_start]); printf("%.6f %.6f 0 0 %s PLACED\n", u2mm(E.x), u2mm(E.y), PacIn_LIB_SubPart_mountside[lib_start]); lib_start++; }while(PacIn_LIB_heights[lib_start] > 1); } } } printf(".END_PLACEMENT\n"); return; } //////////////////////////////////////////////////// void IDF_BoardHoles(UL_BOARD BRD) { //-## create holes in board (cutout, partholes, ...) printf(".DRILLED_HOLES\n"); // drill holes from board direct BRD.holes(H) printf("%.6f %.6f %.6f NPTH BOARD Other UNOWNED\n", u2mm(H.drill), u2mm(H.x), u2mm(H.y)); // drill holes from elements BRD.elements(E) { E.package.holes(H) printf("%.6f %.6f %.6f NPTH BOARD Other UNOWNED\n", u2mm(H.drill), u2mm(H.x), u2mm(H.y)); } // drill pad(holes) from elements 2014-01-20 BRD.elements(E) { E.package.contacts(C) if (C.pad) if (u2mm(C.pad.drill) > MinViaHoleDia) printf("%.6f %.6f %.2f PTH BOARD Other UNOWNED\n", u2mm(C.pad.drill), u2mm(C.pad.x), u2mm(C.pad.y)); } // drill big via's BRD.signals(S) { S.vias(V) { if((DrillViaHoles == true) && (u2mm(V.drill) > MinViaHoleDia)) // drill via-hole printf("%.6f %.6f %.6f PTH BOARD VIA UNOWNED\n", u2mm(V.drill), u2mm(V.x), u2mm(V.y)); } } printf(".END_DRILLED_HOLES\n"); return; } //////////////////////////////////////////////////// void IDF_CreateLibaryFile(UL_BOARD BRD, string fname) { // create libary for the IDF-board output(fname, "wt") { IDF_LibaryHeader(BRD); IDF_LibaryElectrical(BRD); IDF_LibaryMechanical(BRD); } return; } //////////////////////////////////////////////////// void IDF_CreateBoardFile(UL_BOARD BRD, string fname) { // create boardfile with parts of libary output(fname, "wt") { IDF_BoardHeader(BRD); IDF_BoardOutline(BRD); IDF_BoardHoles(BRD); IDF_BoardPlaceParts(BRD); } return; } //////////////////////////////////////////////////// void DisplayHelp(void) { // show helptext dlgDialog("Generate 3D IDF data") { dlgLabel("Version " + UlpVersion); dlgHBoxLayout dlgSpacing(500); dlgHBoxLayout { dlgVBoxLayout dlgSpacing(200); dlgTextView(usage); } dlgLabel(Docuemantpath); dlgHBoxLayout { dlgStretch(1); dlgPushButton("-Back") dlgReject(); } }; return; } //////////////////////////////////////////////////// // S T A R T of U L P // if (board) { int proceed = 0; string FileDirectoryName; string FilenamePanel; string FileNameIDB; // Board string FileNameIDL; // Library board(BRD) { FileDirectoryName = filedir(BRD.name); FileNameIDB = filesetext(filename(BRD.name), ".IDB"); FileNameIDL = filesetext(filename(BRD.name), ".IDL"); } dlgDialog("Generate 3D data format (IDF-file)") { dlgHBoxLayout { dlgGroup(" settings ") { dlgGridLayout { dlgCell(0,0) dlgCheckBox("close polygon if gap is smaller than : ", CloseOpenPoly); dlgCell(0,1) dlgRealEdit(MaxGapWidth); dlgCell(0,2) dlgLabel(" mm"); dlgCell(1,0) dlgCheckBox("drill via-holes if via is bigger than : ", DrillViaHoles); dlgCell(1,1) dlgRealEdit(MinViaHoleDia); dlgCell(1,2) dlgLabel(" mm"); dlgCell(2,0) dlgCheckBox("Export top parts", SelectSide[0]); dlgCell(3,0) dlgCheckBox("Export bottom parts", SelectSide[1]); } } } dlgGridLayout { dlgCell(1,0) dlgLabel("Output Directory:"); dlgCell(1,1) dlgLabel(FileDirectoryName, 1); dlgCell(1,2) { dlgPushButton("Browse") { string fdn = dlgDirectory("Save IDF file", FileDirectoryName); if (fdn) { int len = strlen(fdn); if (fdn[len] != '/') fdn+="/"; // dlgDirectory gibt den Pfad ohne abschliessenden Slash zurück! FileDirectoryName = fdn; } } } dlgCell(2,0) dlgLabel("Filename IDB (Board)"); dlgCell(2,1) dlgStringEdit(FileNameIDB); dlgCell(2,2) { dlgPushButton("Browse") { string fdb = dlgFileSave("Save IDF file", FileDirectoryName, "IDF files (*.IDB)"); if (fdb) { FileNameIDB = filename(fdb); if(strchr(FileNameIDB, '.') < 0) // add fileextension if missing FileNameIDB += ".IDB"; FileNameIDL = filesetext(FileNameIDB, ".IDL"); } } } dlgCell(3,0) dlgLabel("Filename IDL (Library)"); dlgCell(3,1) dlgStringEdit(FileNameIDL); dlgCell(3,2) { dlgPushButton("Browse") { string fdl = dlgFileSave("Save IDF file", FileDirectoryName, "IDF files (*.IDL)"); if (fdl) { FileNameIDL = filename(fdl); if(strchr(FileNameIDL, '.') < 0) // add fileextension if missing FileNameIDL += ".IDL"; FileNameIDB = filesetext(FileNameIDL, ".IDB"); } } } } dlgHBoxLayout { dlgPushButton("OK") { proceed = 1; dlgAccept(); } dlgPushButton("Cancel") dlgReject(); dlgStretch(1); dlgLabel("Version: " + UlpVersion); dlgPushButton("Help") DisplayHelp(); } }; if(proceed) { project.board(BRD) { CollectPartData(BRD); // read out data from BRD IDF_CreateLibaryFile(BRD, FileNameIDL); IDF_CreateBoardFile(BRD, FileNameIDB); } } } else { dlgMessageBox(usage + "


ERROR: No board!

\nThis program can only work in the layout editor."); exit(0); }