Files
SyncHome/trunk/ulp/generate_3d_data_v10.ulp

1206 lines
33 KiB
Plaintext
Raw Permalink Normal View History

2023-03-09 10:24:21 +00:00
#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);
}
}
}