1206 lines
33 KiB
Plaintext
1206 lines
33 KiB
Plaintext
#usage "<b>export 3d-data from board to IDF-FileFormat V3.0</b>\n"
|
|
"<p>"
|
|
"<author>Author: Neubacher Andy</author><br>"
|
|
"<author>co-Author: alf@cadsoft.de</author>"
|
|
|
|
#require 6.0300
|
|
|
|
//############################################################################
|
|
// Author: Andy Neubacher
|
|
// Version | Date | log
|
|
//---------+------------+-----------------------------------------------------
|
|
// v1.0 | 2012-08-20 | - more resolution %.6f of coordinates
|
|
// | | 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 maxCutoutLineWidth = 0; // -->> is linewidth 0.0 -> line is a cutout
|
|
|
|
|
|
string HelpText = "<b>1)</b> <p>3D use u2mm()</p>\n";
|
|
|
|
string UlpName;
|
|
string UlpVersion = "V0.9";
|
|
|
|
|
|
|
|
|
|
|
|
// 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[]; // ARC, LINE, CIRCLE
|
|
int SegmentOptions[]; // used by ARCs : -180 if drawn CCW, +180 if drawn CW
|
|
int SegmentWidth[]; // linewitdh = partheight
|
|
enum { LINE = 0, ARC = 1, CIRCLE = 2 };
|
|
enum { CUTOUT = 0, OUTLINE = -1 };
|
|
enum { false = 0, true = 1};
|
|
|
|
|
|
// 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 = 1; // diameter of drill
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////
|
|
void GetUlpName(void) // reads out the own ULP-name
|
|
{
|
|
string s = strsub(argv[0], 0, strlen(argv[0])-4);
|
|
char c = '/';
|
|
int pos = strrchr(s, c);
|
|
|
|
if (pos >= 0)
|
|
UlpName = strsub(s, pos + 1);
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////
|
|
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 x=0;
|
|
|
|
if(type == CUTOUT)
|
|
x = NumCutouts;
|
|
|
|
printf("%d %.6f %.6f 0\n", x, u2mm(x1), u2mm(y1));
|
|
printf("%d %.6f %.6f 360\n", x, u2mm(x2), u2mm(y2));
|
|
}
|
|
|
|
////////////////////////////////////////////////////
|
|
void IDF_Arc(int x1, int y1, int x2, int y2, int angle, int type) // output arc to file in IDF format
|
|
{
|
|
int x=0;
|
|
|
|
if(type == CUTOUT)
|
|
x = NumCutouts;
|
|
|
|
printf("%d %.6f %.6f 0\n", x, u2mm(x1), u2mm(y1));
|
|
printf("%d %.6f %.6f %d\n", x, u2mm(x2), u2mm(y2), angle);
|
|
}
|
|
|
|
////////////////////////////////////////////////////
|
|
void IDF_Line(int x1, int y1, int x2, int y2, int type) // output line to file in IDF format
|
|
{
|
|
int x=0;
|
|
|
|
if(type == CUTOUT)
|
|
x = NumCutouts;
|
|
|
|
printf("%d %.6f %.6f 0\n", x, u2mm(x1), u2mm(y1));
|
|
printf("%d %.6f %.6f 0\n", x, u2mm(x2), u2mm(y2));
|
|
}
|
|
|
|
////////////////////////////////////////////////////
|
|
void OutputLines(int originx, int originy, int type) // write to file
|
|
{
|
|
for(int i=0; i<NumSegments; i++)
|
|
{
|
|
switch(SegmentType[i])
|
|
{
|
|
case LINE : IDF_Line(PointX1[i]-originx, PointY1[i]-originy, PointX2[i]-originx, PointY2[i]-originy, type);
|
|
break;
|
|
|
|
case ARC : IDF_Arc(PointX1[i]-originx, PointY1[i]-originy, PointX2[i]-originx, PointY2[i]-originy, SegmentOptions[i], type);
|
|
break;
|
|
|
|
case CIRCLE : IDF_Circle(PointX1[i]-originx, PointY1[i]-originy, PointX2[i]-originx, PointY2[i]-originy, type);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////
|
|
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.arc.layer == layer && W.arc.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];
|
|
SegmentOptions[i] = SegmentOptions[i+start];
|
|
SegmentWidth[i] = SegmentWidth[i+start];
|
|
}
|
|
|
|
NumSegments = nr;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////
|
|
int SortPointsEx () // 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 ResultSegmentOptions[]; // 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];
|
|
ResultSegmentOptions[0] = SegmentOptions[0];
|
|
PointUsed[0] = USED;
|
|
|
|
|
|
for(int x=1; x<NumSegments; x++)
|
|
{
|
|
i = 1;
|
|
ptfound = NOT_FOUND;
|
|
|
|
do
|
|
{
|
|
if(!PointUsed[i])
|
|
{
|
|
// search left point of segment in list
|
|
if( IsPointEqual(PointX1[i], PointY1[i], ResultPointX2[x-1], ResultPointY2[x-1]) ) // x1,y1 = startpoint | x2,y2 = endpoint
|
|
{
|
|
ptfound = FOUND;
|
|
PointUsed[i] = USED;
|
|
ResultPointX1[x] = PointX1[i];
|
|
ResultPointY1[x] = PointY1[i];
|
|
ResultPointX2[x] = PointX2[i];
|
|
ResultPointY2[x] = PointY2[i];
|
|
ResultSegmentType[x] = SegmentType[i];
|
|
ResultSegmentWidth[x] = SegmentWidth[i];
|
|
ResultSegmentOptions[x] = SegmentOptions[i];
|
|
}
|
|
|
|
// search right point of segment in list
|
|
if( IsPointEqual(PointX2[i], PointY2[i], ResultPointX2[x-1], ResultPointY2[x-1]) ) // x2,y2 = startpoint | x1,y1 = endpoint
|
|
{
|
|
ptfound = FOUND;
|
|
PointUsed[i] = USED;
|
|
ResultPointX1[x] = PointX2[i];
|
|
ResultPointY1[x] = PointY2[i];
|
|
ResultPointX2[x] = PointX1[i];
|
|
ResultPointY2[x] = PointY1[i];
|
|
ResultSegmentType[x] = SegmentType[i];
|
|
ResultSegmentWidth[x] = SegmentWidth[i];
|
|
ResultSegmentOptions[x] = SegmentOptions[i] * (-1); // swap also drawing direction of ARC (clockwise, counter-clockwise)
|
|
}
|
|
} // point used
|
|
|
|
|
|
// checked full array ?
|
|
if(i++ >= (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];
|
|
ResultSegmentOptions[i] = SegmentOptions[n];
|
|
ResultSegmentType[i] = SegmentType[n];
|
|
ResultSegmentWidth[i] = SegmentWidth[n];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// copy back sorted list
|
|
for(i=0; i<NumSegments; i++)
|
|
{
|
|
PointX1[i] = ResultPointX1[i];
|
|
PointY1[i] = ResultPointY1[i];
|
|
PointX2[i] = ResultPointX2[i];
|
|
PointY2[i] = ResultPointY2[i];
|
|
SegmentType[i] = ResultSegmentType[i];
|
|
SegmentOptions[i] = ResultSegmentOptions[i];
|
|
SegmentWidth[i] = ResultSegmentWidth[i];
|
|
}
|
|
|
|
|
|
return NumSegments-NumPointsUsed; // return number of pending points
|
|
} // SortPointsEx
|
|
|
|
|
|
////////////////////////////////////////////////////
|
|
void CloseOpenPolygon ()
|
|
{
|
|
int i, LineAdded;
|
|
int RemainingPoints, LastPoint;
|
|
|
|
|
|
do
|
|
{
|
|
LineAdded = false;
|
|
RemainingPoints = SortPointsEx();
|
|
LastPoint = NumSegments - RemainingPoints - 1;
|
|
|
|
if(RemainingPoints) // only search for gaps if remainingpoints != 0
|
|
{
|
|
for(i=LastPoint+1; i<NumSegments; i++) // start from last+1 poly-segment -> search for points within circle
|
|
{
|
|
if(IsPointInCircle(PointX1[i], PointY1[i], PointX2[LastPoint], PointY2[LastPoint], mm2u(MaxGapWidth)))
|
|
{
|
|
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];
|
|
SegmentOptions[++NumSegments] = 0;
|
|
|
|
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)))
|
|
{
|
|
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];
|
|
SegmentOptions[++NumSegments] = 0;
|
|
|
|
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)))
|
|
{
|
|
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];
|
|
SegmentOptions[++NumSegments] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////
|
|
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(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.ulp %s<", UlpName, UlpVersion);
|
|
|
|
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;
|
|
SegmentOptions[++NumSegments] = 0;
|
|
}
|
|
}
|
|
|
|
E.package.wires(W)
|
|
{
|
|
if(W.arc) // create arcs
|
|
{
|
|
if(W.arc.layer == layer && W.arc.width > maxCutoutLineWidth)
|
|
{
|
|
PointX1[NumSegments] = W.arc.x1;
|
|
PointY1[NumSegments] = W.arc.y1;
|
|
PointX2[NumSegments] = W.arc.x2;
|
|
PointY2[NumSegments] = W.arc.y2;
|
|
SegmentType[NumSegments] = ARC;
|
|
SegmentWidth[NumSegments] = W.arc.width;
|
|
SegmentOptions[++NumSegments] = 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;
|
|
SegmentOptions[++NumSegments] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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<NumSegments;i++) // swap x-coordinate for bottom parts
|
|
{
|
|
PointX1[i] = E.x - ((E.x - PointX1[i])*(-1));
|
|
PointX2[i] = E.x - ((E.x - PointX2[i])*(-1));
|
|
}
|
|
}
|
|
}
|
|
|
|
return NumSegments;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////
|
|
void IDF_LibaryElectrical(UL_BOARD BRD) //-> 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<NumParts;i++)
|
|
{
|
|
BRD.elements(E)
|
|
{
|
|
inlib = 0;
|
|
for(x=0; x<NumPacInLib; x++) // check if new package
|
|
{
|
|
if(PacIn_LIB_name[x] == E.package.name &&
|
|
PacIn_LIB_angle[x] == E.angle &&
|
|
PacIn_LIB_Device_mountside[x] == E.mirror)
|
|
inlib = 1;
|
|
}
|
|
|
|
if(E.name == PartName[i] && inlib == 0) // is package still in LIB-file
|
|
{
|
|
NrOfPolygon = 0;
|
|
if(DataOnLayerPresent(E, bLayer3Ddata)) // if poly on bot-side found start from "1"
|
|
NrOfPolygon=1;
|
|
|
|
for(int b=0;b<2;b++)
|
|
{
|
|
if(CollectLibOutlineSegments(E, layer[b])) // only add package to lib if line-segments in layer found
|
|
{
|
|
do
|
|
{
|
|
RemainingPoints = SortPoints(E.name); // returns number of open/not_used points
|
|
NumSegments -= RemainingPoints; // calculate nr of correct/used points in poly
|
|
|
|
// more than one outline in device present ?
|
|
if((NrOfPolygon == 0) && (RemainingPoints != 0))
|
|
NrOfPolygon=1;
|
|
|
|
printf(".ELECTRICAL\n");
|
|
printf("%s_ANGLE%.1f_%s.%d CI_LIB MM %.6f\n", E.package.name, E.angle, MountSide[b], NrOfPolygon, getPartHeight());
|
|
OutputLines(E.x, E.y, OUTLINE); // add package to libfile -> 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[b]; // 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;
|
|
|
|
BRD.elements(E)
|
|
{
|
|
E.package.circles(CIR) // create board-outline of package-libary
|
|
{
|
|
if(CIR.layer == Layer3dBoardDimension && 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;
|
|
SegmentOptions[++NumSegments] = 0;
|
|
}
|
|
}
|
|
}
|
|
BRD.elements(E)
|
|
{
|
|
E.package.wires(W)
|
|
{
|
|
if(W.arc) // create arcs direct from board
|
|
{
|
|
if(W.arc.layer == Layer3dBoardDimension && W.arc.width > maxCutoutLineWidth)
|
|
{
|
|
PointX1[NumSegments] = W.arc.x1;
|
|
PointY1[NumSegments] = W.arc.y1;
|
|
PointX2[NumSegments] = W.arc.x2;
|
|
PointY2[NumSegments] = W.arc.y2;
|
|
SegmentType[NumSegments] = ARC;
|
|
SegmentWidth[NumSegments] = W.arc.width;
|
|
SegmentOptions[++NumSegments] = GetArcOptions(W);
|
|
}
|
|
}
|
|
else // create straight lines direct from board
|
|
{
|
|
if(W.layer == Layer3dBoardDimension && 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;
|
|
SegmentOptions[++NumSegments] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(NumSegments != 0)
|
|
{
|
|
OutlineInDeviceFound = true;
|
|
return NumSegments; // outline was found in a device -> return
|
|
}
|
|
|
|
|
|
|
|
// outline of board was not found in any device -> check if drawn in boardfile direct
|
|
BRD.circles(CIR) // create board-outline of package-libary
|
|
{
|
|
if(CIR.layer == Layer3dBoardDimension && 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;
|
|
SegmentOptions[++NumSegments] = 0;
|
|
}
|
|
}
|
|
BRD.wires(W)
|
|
{
|
|
if(W.arc) // create arcs direct from board
|
|
{
|
|
if(W.arc.layer == Layer3dBoardDimension && W.arc.width > maxCutoutLineWidth)
|
|
{
|
|
PointX1[NumSegments] = W.arc.x1;
|
|
PointY1[NumSegments] = W.arc.y1;
|
|
PointX2[NumSegments] = W.arc.x2;
|
|
PointY2[NumSegments] = W.arc.y2;
|
|
SegmentType[NumSegments] = ARC;
|
|
SegmentWidth[NumSegments] = W.arc.width;
|
|
SegmentOptions[++NumSegments] = GetArcOptions(W);
|
|
}
|
|
}
|
|
else // create straight lines direct from board
|
|
{
|
|
if(W.layer == Layer3dBoardDimension && 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;
|
|
SegmentOptions[++NumSegments] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if(NumSegments == 0)
|
|
{
|
|
string x;
|
|
sprintf(x,"!no board-outline on layer %d found !", Layer3dBoardDimension);
|
|
dlgMessageBox(x);
|
|
}
|
|
|
|
return NumSegments;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////
|
|
int CollectBoardCutoutSegments(UL_ELEMENT E, int layer, int type)
|
|
{
|
|
NumSegments=0;
|
|
|
|
|
|
if(type == CIRCLE)
|
|
{
|
|
E.package.circles(CIR) // create circles direct from board
|
|
{
|
|
if(CIR.layer == layer && CIR.width < (maxCutoutLineWidth+1))
|
|
{
|
|
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;
|
|
SegmentOptions[++NumSegments] = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
E.package.wires(W)
|
|
{
|
|
if(W.arc) // create arcs direct from board
|
|
{
|
|
if(W.arc.layer == layer && W.arc.width < (maxCutoutLineWidth+1))
|
|
{
|
|
PointX1[NumSegments] = W.arc.x1;
|
|
PointY1[NumSegments] = W.arc.y1;
|
|
PointX2[NumSegments] = W.arc.x2;
|
|
PointY2[NumSegments] = W.arc.y2;
|
|
SegmentType[NumSegments] = ARC;
|
|
SegmentWidth[NumSegments] = W.arc.width;
|
|
SegmentOptions[++NumSegments] = GetArcOptions(W);
|
|
}
|
|
}
|
|
else // create straight lines direct from board
|
|
{
|
|
if(W.layer == layer && W.width < (maxCutoutLineWidth+1))
|
|
{
|
|
PointX1[NumSegments] = W.x1;
|
|
PointY1[NumSegments] = W.y1;
|
|
PointX2[NumSegments] = W.x2;
|
|
PointY2[NumSegments] = W.y2;
|
|
SegmentType[NumSegments] = LINE;
|
|
SegmentWidth[NumSegments] = W.width;
|
|
SegmentOptions[++NumSegments] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NumSegments;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////
|
|
void DoCutoutsFromElements(UL_BOARD BRD)
|
|
{
|
|
int ret, RemainingPoints;
|
|
|
|
// get cutouts from elements (holes, ...)
|
|
BRD.elements(E)
|
|
{
|
|
// get cutout segments (LINEs and ARCs)
|
|
ret = CollectBoardCutoutSegments(E, Layer3dBoardDimension, LINE);
|
|
if(ret)
|
|
{
|
|
do
|
|
{
|
|
NumCutouts++; // increment board cutouts
|
|
RemainingPoints = SortPoints(E.name); // elementname
|
|
NumSegments -= RemainingPoints; // calculate nr of correct points of poly
|
|
OutputLines(0, 0, CUTOUT); // subract given offsets -> E.x, E.y
|
|
|
|
if(RemainingPoints)
|
|
CollectRemainingPoints(NumSegments, RemainingPoints);
|
|
|
|
}while(RemainingPoints != 0);
|
|
}
|
|
|
|
// get cutout holes (CIRCLEs)
|
|
ret = CollectBoardCutoutSegments(E, Layer3dBoardDimension, CIRCLE);
|
|
if(ret)
|
|
{
|
|
for(int i=0;i<ret;i++)
|
|
{
|
|
NumCutouts++;
|
|
IDF_Circle(PointX1[i], PointY1[i], PointX2[i], PointY2[i], CUTOUT);
|
|
}
|
|
}
|
|
|
|
// get cutouts from 3d-toplayer (for each device)
|
|
if(CollectBoardCutoutSegments(E, tLayer3Ddata, LINE))
|
|
{
|
|
NumCutouts++; // increment board cutouts
|
|
SortPoints(E.name); // elementname
|
|
OutputLines(0, 0, CUTOUT); // subract given offsets -> E.x, E.y
|
|
}
|
|
|
|
// get cutouts from 3d-bottomlayer (for each device)
|
|
if(CollectBoardCutoutSegments(E, bLayer3Ddata, LINE))
|
|
{
|
|
NumCutouts++; // increment board cutouts
|
|
SortPoints(E.name); // elementname
|
|
OutputLines(0, 0, CUTOUT); // subract given offsets -> E.x, E.y
|
|
}
|
|
|
|
} //BRD.elements(E)
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////
|
|
void DoCutoutsFromBoardDirect(UL_BOARD BRD)
|
|
{
|
|
// drill circles if linewidth = 0
|
|
BRD.circles(CIR) // create circles direct from board
|
|
{
|
|
if(CIR.layer == Layer3dBoardDimension && CIR.width < (maxCutoutLineWidth+1))
|
|
{
|
|
NumCutouts++;
|
|
IDF_Circle(CIR.x, CIR.y, CIR.x+CIR.radius, CIR.y, CUTOUT);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////
|
|
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.ulp %s<", UlpName, UlpVersion);
|
|
|
|
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());
|
|
OutputLines(0, 0, OUTLINE); // subract given offsets -> E.x, E.y
|
|
|
|
// draw cutouts
|
|
DoCutoutsFromElements(BRD); // get cutouts from devices -> board is a device
|
|
|
|
if(OutlineInDeviceFound) // get cutouts from board direct -> outline direct drawn in board
|
|
DoCutoutsFromBoardDirect(BRD);
|
|
|
|
printf(".END_BOARD_OUTLINE\n");
|
|
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<NumPacInLib; i++) // check if elements was found in libary
|
|
{
|
|
if(PacIn_LIB_name[i] == E.package.name && PacIn_LIB_Device_mountside[i] == E.mirror && PacIn_LIB_angle[i] == E.angle)
|
|
{
|
|
inlib = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if(inlib) // only add part if part is in lib-file
|
|
{
|
|
if(PacIn_LIB_heights[i] == 0) // part has only one height
|
|
{
|
|
printf("%s_ANGLE%.1f_%s.0 CI_LIB %s.0\n", E.package.name, E.angle, PacIn_LIB_SubPart_mountside[i], E.name);
|
|
printf("%.6f %.6f 0 0 %s PLACED\n", u2mm(E.x), u2mm(E.y), MountSide[E.mirror]);
|
|
}
|
|
else // part has multiple heights -> 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 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 IDF_CreatePanelFile(UL_BOARD BRD, string fname) // create panel, containing board and parts of libary
|
|
{
|
|
// not yet implemented
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////
|
|
void DisplayHelp(void) // show helptext
|
|
{
|
|
dlgDialog("generation of different variants - Help")
|
|
{
|
|
dlgHBoxLayout dlgSpacing(500);
|
|
dlgHBoxLayout
|
|
{
|
|
dlgVBoxLayout dlgSpacing(200);
|
|
dlgTextView(HelpText);
|
|
}
|
|
dlgHBoxLayout
|
|
{
|
|
dlgStretch(1);
|
|
dlgPushButton("-Close") dlgReject();
|
|
}
|
|
};
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////
|
|
// S T A R T of U L P
|
|
//
|
|
if (!board)
|
|
{
|
|
dlgMessageBox(usage + "<hr><b>ERROR: No board!</b><p>\nThis program can only work in the layout editor.");
|
|
exit(1);
|
|
}
|
|
else
|
|
{
|
|
int proceed = 0;
|
|
string FilenameLibary;
|
|
string FilenameBoard;
|
|
string FilenamePanel;
|
|
|
|
|
|
|
|
|
|
project.board(BRD)
|
|
{
|
|
FilenameBoard = filesetext(BRD.name, ".emn");
|
|
FilenameLibary = filesetext(BRD.name, ".emp");
|
|
}
|
|
|
|
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 ");
|
|
}
|
|
}
|
|
}
|
|
dlgHBoxLayout
|
|
{
|
|
dlgLabel("enter output-filename : ");
|
|
dlgStringEdit(FilenameBoard);
|
|
dlgPushButton("Browse")
|
|
{
|
|
string FileName = dlgFileSave("Save IDF file", FilenameBoard, "IDF files (*.emn)");
|
|
if (FileName)
|
|
{
|
|
if(strchr(FileName, '.') < 0) // add fileextension if missing
|
|
FileName += ".emn";
|
|
|
|
FilenameLibary = filesetext(FileName, ".emp");
|
|
FilenameBoard = FileName;
|
|
}
|
|
}
|
|
|
|
}
|
|
dlgHBoxLayout
|
|
{
|
|
dlgPushButton("OK")
|
|
{
|
|
proceed = 1;
|
|
dlgAccept();
|
|
}
|
|
|
|
dlgPushButton("Help")
|
|
DisplayHelp();
|
|
|
|
dlgPushButton("Cancel")
|
|
dlgReject();
|
|
}
|
|
};
|
|
|
|
|
|
if(proceed)
|
|
{
|
|
project.board(BRD)
|
|
{
|
|
GetUlpName(); // gets ulp name
|
|
CollectPartData(BRD); // read out data from BRD
|
|
|
|
IDF_CreateLibaryFile(BRD, FilenameLibary);
|
|
IDF_CreateBoardFile(BRD, FilenameBoard);
|
|
IDF_CreatePanelFile(BRD, FilenamePanel);
|
|
}
|
|
}
|
|
} |