#usage "en:export 3d-data from board to IDF-FileFormat
"
"Layer 50 contains the 3D board dimension, Layer 57 and 58 store the height info"
"for top and bottom mounted parts respectively. If these layers don't exist in your"
"design the ULP can create them for you."
"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 = "2.3";
// 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);
}
";
string Docuemantpath = "Weiter Infos finden sie im Dokumentenordner unter " + "/generate_3d_data_eng.pdf
";
//############################################################################
// Author: Jorge Garcia
// Version | Date | log
//---------+------------+-----------------------------------------------------
// v2.1 | 2015-11-03 | - Changed layers to 50, 57 and 58.
// - Added a function to check for the existence of these layers. If not found gives the option to create them.
//
// v2.3 | 2015-11-16 | - Fixed mirroring issue in FreeCAD
//
// Author: Teunis van Beelen
// Version | Date | log
//---------+------------+-----------------------------------------------------
// v2.2 | 2015-11-10 | - Make sure that parts that are placed on the bottom are mirrored correctly.
//
// v2.0 | 2015-05-20 | - Changed layers to 150, 157 and 158.
// - Canged filename extensions from .IDL to .emp and .IDB to .emn.
// - Write the unmodified packagenames to the IDF files.
// - Fixed the code to select a directory for the IDF files.
//
//
// 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 CheckForIDFLayers() {
int LayerSum = 0;
int ButtonClicked;
string command;
board(B) {
B.layers(L) { // this lets the ULP know which layers are missing
if(L.number == Layer3dBoardDimension){
LayerSum += 1;
}
if(L.number == tLayer3Ddata){
LayerSum += 2;
}
if(L.number == bLayer3Ddata){
LayerSum += 4;
}
}
}
switch(LayerSum) {
case 0:
//None of the layers where found all three need to be created
ButtonClicked = dlgMessageBox("Missing layers 50,57,58. These are used to define the 3D properties of your design. Would you like to create them? Once the layers have been created run the ULP again to produce your IDF output.","&Yes","&No");
if(ButtonClicked == 0){
sprintf(command, "LAYER %d 3dBoardDimension;LAYER %d t3Ddata;LAYER %d b3Ddata;",Layer3dBoardDimension,tLayer3Ddata,bLayer3Ddata);
exit(command);
}
else{
exit(0);
}
break;
case 1:
//Only Layer3dBoardDimension was found the other two need to be made
ButtonClicked = dlgMessageBox("Missing layers 57,58. These are used to define the 3D properties of your design. Would you like to create them? Once the layers have been created run the ULP again to produce your IDF output.","&Yes","&No");
if(ButtonClicked == 0){
sprintf(command, "LAYER %d t3Ddata;LAYER %d b3Ddata;",tLayer3Ddata,bLayer3Ddata);
exit(command);
}
else{
exit(0);
}
break;
case 2:
//Only tLayer3Ddata was found the other two need to be made
ButtonClicked = dlgMessageBox("Missing layers 50,58. These are used to define the 3D properties of your design. Would you like to create them? Once the layers have been created run the ULP again to produce your IDF output.","&Yes","&No");
if(ButtonClicked == 0){
sprintf(command, "LAYER %d 3dBoardDimension;LAYER %d b3Ddata;",Layer3dBoardDimension,bLayer3Ddata);
exit(command);
}
else{
exit(0);
}
break;
case 3:
//Layers Layer3dBoardDimension and tLayer3Ddata were found the other needs to be made
ButtonClicked = dlgMessageBox("Missing layers 58. These are used to define the 3D properties of your design. Would you like to create them? Once the layers have been created run the ULP again to produce your IDF output.","&Yes","&No");
if(ButtonClicked == 0){
sprintf(command, "LAYER %d b3Ddata;",bLayer3Ddata);
exit(command);
}
else{
exit(0);
}
break;
case 4:
//Only bLayer3Ddata was found the other two need to made
ButtonClicked = dlgMessageBox("Missing layers 50,57. These are used to define the 3D properties of your design. Would you like to create them? Once the layers have been created run the ULP again to produce your IDF output.","&Yes","&No");
if(ButtonClicked == 0){
sprintf(command, "LAYER %d 3dBoardDimension;LAYER %d t3Ddata;",Layer3dBoardDimension,tLayer3Ddata);
exit(command);
}
else{
exit(0);
}
break;
case 5:
//Layers Layer3dBoardDimension and bLayer3Ddata were found the other needs to be made
ButtonClicked = dlgMessageBox("Missing layers 57. These are used to define the 3D properties of your design. Would you like to create them? Once the layers have been created run the ULP again to produce your IDF output.","&Yes","&No");
if(ButtonClicked == 0){
sprintf(command, "LAYER %d t3Ddata;",tLayer3Ddata);
exit(command);
}
else{
exit(0);
}
break;
case 6:
//Layers tLayer3Ddata and bLayer3Ddata were found the other one needs to be made
ButtonClicked = dlgMessageBox("Missing layers 50. These are used to define the 3D properties of your design. Would you like to create them? Once the layers have been created run the ULP again to produce your IDF output.","&Yes","&No");
if(ButtonClicked == 0){
sprintf(command, "LAYER %d 3dBoardDimension;",Layer3dBoardDimension);
exit(command);
}
else{
exit(0);
}
break;
case 7:
//All layers were found nothing need to be done
break;
}
}
////////////////////////////////////////////////////
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, int mirrorval) { // write to file
int first = 1;
int i = 0;
int startx = PointX1[i];
int starty = PointY1[i];
if(mirrorval)
{
mirrorval = -1;
}
else
{
mirrorval = 1;
}
for(i = 0; i < NumSegments; i++) {
NumCutouts = LoopCounter;
switch(SegmentType[i]) {
case LINE : IDF_Line((PointX1[i]-originx)*mirrorval, PointY1[i]-originy, (PointX2[i]-originx)*mirrorval, 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)*mirrorval, PointY1[i]-originy, (PointX2[i]-originx)*mirrorval, 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!