#usage "en:export 3d-data from board to IDF-FileFormat
"
"More info can be found here: .../eagle.../doc/generate_3d_data_eng.pdf
"
"
"
"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.
"
"
"
"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 \nThis program can only work in the layout editor.");
exit(0);
}
";
//############################################################################
// 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
ERROR: No board!