#usage "Generate IDF files from CADSoft Eagle

\n" "Run ULP on board file to generate IDF board and library files for import to CAD tools.
" "Version: 0.1 - Development Started - 12-8-13
" "Version: 0.2 - Bug fix and cleanup - 12-21-13
" "Version: 0.3 - Added in logic to lookup height if set - 1-29-14
" "Version: 0.4 - Added validations on height values to ensure only number values are passed - 2-7-14
" "Version: 0.5 - Baseline commit before code style changes - 3-20-14
" "Version: 0.6 - Code style Issues addressed - 3-20-14
" "Version: 0.7 - Updated component outline bounding box logic - 4-13-14
" "Version: 0.8 - Cleanup of unused functions - 5-3-14
" "Version: 0.9 - Fixed minor GUI issues related to reset of html table values - 5-6-14
" "Version: 0.10 - Added logic for varied heights in packages on board - 5-6-14
" "Version: 0.11 - Fixed bug in logic to handle HEIGHT attributes set with numbertext value.- 5-13-14
" "Version: 1.0 - Initial production release.- 5-21-14
" "Version: 1.1 - Change to consider circles on layer 20 regardless of other lines. Improved arc/circle processing logic. - 9-6-14
" "Version: 2.0 - Updated to integrate with SSI IDF site - 3-25-15
" "Version: 2.1 - Updates to include banner and links - 3-29-15
" "Version: 2.2 - Updates to logic to open links in browser - 4-7-15
" "

" "Author: marc.battistello@gmail.com" #require 5.1000 string version = "2.2"; //ulp version //vars used for the text dialog string TextMessageDialog; //used to store text shown to user string HeightHtmlTable = ""; string BoardThickness = "1.6"; //mm string WireLayer = "20"; //string DummyHistory[]; string LibraryLookup = "RCL:RESISTOR:TRANSISTOR:PINHEAD:RESISTOR-DIL:SMARTPRJ:C5B2,5A:CAP-WI:POLCAP:ELKO:CON-PTR500:SPECIAL:LED:CAPACITOR-WIMA:CON-WAGO-500:CON-HARTING-ML:ST-ML:CON-ML:CON-LSTB:CON-BERG:CON-SUBD:POLCAP"; string Libraries[]; //used to hold Libraries string PackageOutlines[]; //used to hold outlines of packages int PackageCounter = 0; //keeps track of how many entries in packages outline array string NULL_HEIGHT = "0.000000"; //used for null heights //web request urls string Host = "http://www.simplifiedsolutionsinc.com"; string Service_port = "8080"; //string service_port = "80"; string Static_port = "80"; string Service_name = "EMN-EMPAdvancedApp/EagleTo3DSSIServlet"; string Login_name = "EMN-EMPAdvancedApp/LinkLoginServlet"; string Static_name = "EagleTo3D/"; string Service_url = Host + ":" + Service_port + "/" + Service_name; string Login_url = Host + ":" + Service_port + "/" + Login_name; string Static_url = Host + "/" + Static_name; int useSecondaryMappings = 1; int useSuggestionMappings = 1; //debug flags int Debug = 1; //general Debug flag //----------------------------------------------------------------------------- // subroutines //----------------------------------------------------------------------------- string itos(int num) { string temp; sprintf(temp, "%d", num); return temp; } string rtos(real num) { string temp; sprintf(temp, "%f", num); return temp; } int IsInt(string str) { //returns 1 if int. 0 if not string //first check to make sure its not "0" which Is the fail mode for the cast if (str == "0") { return 1; } else if (strtol(str) != 0) { return 1; } else { return 0; } } //end IsInt int IsReal(string str) { //returns 1 if int. 0 if not string //first check to make sure its not "0.0" which Is the fail mode for the cast if (str == "0.0") { return 1; } else if (strtod(str) != 0.0) { return 1; } else { return 0; } } //end IsInt int CompareReals(real n1, real n2) { string n1s; string n2s; sprintf(n1s, "%1.2f", n1); sprintf(n2s, "%1.2f", n2); if (n1s == n2s) { return 1; } else { return 0; } } string GetBoardThickness() { //make sure there Is value for board thickness if (BoardThickness == "") { BoardThickness = "1.6"; } //check if first char Is decimal and if it Is then add 0 char dec = '.'; int pos = strchr(BoardThickness, dec); if (pos == 0) { BoardThickness = "0" + BoardThickness; } //return form value return BoardThickness; } //end func string GetBoardUnits() { /* GRID_UNIT_MIC microns GRID_UNIT_MM millimeter GRID_UNIT_MIL mil GRID_UNIT_INCH inch */ /* force to mm string unitsMap[] = { "MIC", "MM", "MIL", "IN" }; board(B) { return unitsMap[ B.grid.unit ]; } */ return "MM"; } real BackConvertDimensions(real mmDim) { real boardDim; int unitsInt; //get board dimensions board(B) { unitsInt = B.grid.unit; } /* GRID_UNIT_MIC microns GRID_UNIT_MM millimeter GRID_UNIT_MIL mil GRID_UNIT_INCH inch */ switch (unitsInt) { case 0: //micron boardDim = mmDim * 1000; break; case 1: //mm boardDim = mmDim; break; case 2: //mil boardDim = mmDim * 39.3700787; break; case 3: //inch boardDim = mmDim * 0.0393701; break; } //end switch //string unitsMap[] = { "MIC", "MM", "MIL", "IN" }; //dlgMessageBox( "MM:" + rtos( mmDim ) + ", " + unitsMap[ unitsInt ] + ": " + rtos( boardDim ) ); return boardDim; } //end func real GetDimInBoardUnits(int dim) { int unitsInt; real convDim; /* removed for force to mm board(B) { unitsInt = B.grid.unit; } switch( unitsInt ){ case 0: convDim = u2mic( dim ); break; case 1: convDim = u2mm( dim ); break; case 2: convDim = u2mil( dim ); break; case 3: convDim = u2inch( dim ); break; } //end switch */ convDim = u2mm(dim); return convDim; } //end func real GetTolerance() { int unitsInt; real Tolerance; /* removed to force to mm board(B) { unitsInt = B.grid.unit; } switch( unitsInt ){ case 0: Tolerance = 100; break; case 1: Tolerance = 0.05; break; case 2: Tolerance = 3.9; break; case 3: Tolerance = 0.004; break; } //end switch */ Tolerance = 0.05; return Tolerance; } //end func string GetBoardFilename() { board(BRD) { return filename(BRD.name); } //end board } string GetBoardDir() { board(BRD) { return filedir(BRD.name); } //end board } string GetBoardPath() { return GetBoardDir() + GetBoardFilename(); } string PadBRs( int count ){ string padding = ""; for( int i; i < count; i++ ){ padding += "
"; } return padding; } int IsWindows() { //Returns 1, if EAGLE Is running under Windows (0 for Linux/Mac) if ((strsub(argv[0], 0, 1) == "/") && (strsub(argv[0], 0, 2) != "//")) return 0; return 1; } //end sub int IsMac(){ if( (strsub( OS_SIGNATURE, 0, 3 ) == "Mac") || (strsub( OS_SIGNATURE, 0, 3 ) == "MAC") ) return 1; return 0; } void LaunchBrowser( string url ){ string launchCmd = ""; if( IsWindows() ){ launchCmd = "explorer \"" + url + "\""; } else if( IsMac() ){ launchCmd = "open " + url; } else{ //assume all others linux //if you are on linux then change this line if the buttons dont work launchCmd = "xdg-open " + url; } //run system command system( launchCmd ); } //end launch browser string GetFileSeperator() { if (IsWindows()) { return "\\"; } else { return "/"; } } // Removes carriage returns from a string and replaces with
string RemoveCarriageReturns(string dirtyString) { string cleanedString; for (int i = 0; dirtyString[i]; ++i) { if (dirtyString[i] == '\n') { cleanedString += "
"; } else { cleanedString += dirtyString[i]; } //end if-else } //end for return cleanedString; } //end sub // Removes spaces in string and replaces with string RemoveSpaces(string dirtyString) { string cleanedString; for (int i = 0; dirtyString[i]; ++i) { if (dirtyString[i] == ' ') { cleanedString += ""; } else { cleanedString += dirtyString[i]; } //end if-else } //end for return cleanedString; } //end sub string CleanString(string dirtyString) { dirtyString = strupr(dirtyString); string cleanedString; for (int i = 0; dirtyString[i]; ++i) { if (dirtyString[i] == '0' || dirtyString[i] == '1' || dirtyString[i] == '2' || dirtyString[i] == '3' || dirtyString[i] == '4' || dirtyString[i] == '5' || dirtyString[i] == '6' || dirtyString[i] == '7' || dirtyString[i] == '8' || dirtyString[i] == '9' || dirtyString[i] == 'A' || dirtyString[i] == 'B' || dirtyString[i] == 'C' || dirtyString[i] == 'D' || dirtyString[i] == 'E' || dirtyString[i] == 'F' || dirtyString[i] == 'G' || dirtyString[i] == 'H' || dirtyString[i] == 'I' || dirtyString[i] == 'J' || dirtyString[i] == 'K' || dirtyString[i] == 'L' || dirtyString[i] == 'M' || dirtyString[i] == 'N' || dirtyString[i] == 'O' || dirtyString[i] == 'P' || dirtyString[i] == 'Q' || dirtyString[i] == 'R' || dirtyString[i] == 'S' || dirtyString[i] == 'T' || dirtyString[i] == 'U' || dirtyString[i] == 'V' || dirtyString[i] == 'W' || dirtyString[i] == 'X' || dirtyString[i] == 'Y' || dirtyString[i] == 'Z' || dirtyString[i] == '_' || dirtyString[i] == '-' || dirtyString[i] == ' ') { cleanedString += dirtyString[i]; } else { cleanedString += "_"; } //end if-else } //end for return cleanedString; } //end sub // Removes spaces in string and replaces with string ReplaceSpaceWithUnderscore(string dirtyString) { string cleanedString; for (int i = 0; dirtyString[i]; ++i) { if (dirtyString[i] == ' ') { cleanedString += "_"; } else { cleanedString += dirtyString[i]; } //end if-else } //end for return cleanedString; } //end sub // 0-9, letters, dashes, and underscores are always safe. // Reads in file and converts it to be used in a html form submit string ParseFileToHtmlForm(string filePath) { string fileContents; int in = fileread(fileContents, filePath); fileContents = RemoveCarriageReturns(fileContents); fileContents = RemoveSpaces(fileContents); //dlgMessageBox( "File input value:" + in ); return fileContents; } //end sub string ParseStringToHtmlForm(string input) { return RemoveSpaces(RemoveCarriageReturns(input)); } //end sub //this function Is used to Get the html to show in the UI. //it uses a fixed header and footer and the call sets the body. string GetHtmlText(string body) { string os; //build header html string htmlHeader = " \ \ \ \ \ \ \ \ \ "; string htmlFooter = ""; //footer will be built later including OS padding //detect if windows or other if (IsWindows()) { os = "Windows"; } else { os = "Mac/Linux"; } //build footer and include OS specific padding for formatting. if (IsWindows()) { htmlFooter += "






"; } else { htmlFooter += ""; } //end if-else //build version string string versionString = "ULP Version: " + version + ", OS: " + os; //build footer text htmlFooter += " \ \ \ \ \ \ \ \ \ \ \

EAGLE IDF Exporter


CADSoft | Help( ULP Version: " + version + "\t, OS: " + os + " )

"; return htmlHeader + "" + body + "" + htmlFooter; } //end sub real GetDistBetweenPts(real x1, real y1, real x2, real y2) { //dist = sqrt( ( x2 - x1 )^2 + ( y2 - y1 )^2 ) return sqrt(pow((x2 - x1), 2) + pow((y2 - y1), 2)); } //end sub void WriteToFile(string filePath, string fileContents) { output(filePath, "wt") { printf(fileContents); } } //end sub string GetElementHeightAttr( UL_ELEMENT E ){ /* checks for attribute named HEIGHT. * casts height string to real to handle any invalid values. sets to 0 if not value. * returns height */ string height = NULL_HEIGHT; //check for element height E.attributes(A) { if (strupr(A.name) == "HEIGHT") { if ( strtod(A.value) <= 0.0) { height = NULL_HEIGHT; } else { height = rtos( strtod( A.value ) ); } //end if-else } //end if } //end loop thru attributes return height; } //end sub //----------------------------------------------------------------------------- // emp subroutines //----------------------------------------------------------------------------- string GetDateTime() { //time for date.time in header int now = time(); string dateStr; sprintf(dateStr, "%d/%02d/%02d.%02d:%02d:%02d", t2year(now), t2month(now), t2day(now), t2hour(now), t2minute(now), t2second(now)); return dateStr; } //end sub //looks up package outline and generates text for .electical section entry string GetComponentOutline(string footprint, string altname, string packageName, string units, string height) { //package array = [ { packname1, maxX, maxY, minX, minY }, // { packname2, maxX, maxY, minX, minY } // ] //board->Library->Package->Wires int foundPackage = 0; //lookup package name in array string packageNameLookup = lookup(PackageOutlines, packageName, 0); //check if found in lookup. if not then generate and put in array if (packageNameLookup == "") { //if package not in lookup then generate it //init max/min values real maxX = 0, maxY = 0, minX = 0, minY = 0; //loop thru all Libraries to find packagename board(B) { //go into library B.libraries(LBR) { //loop thru packages in library LBR.packages(P) { //if package name matches the one we are looking for then process it if (P.name == packageName) { //check min/ax for each of //circles() UL_CIRCLE - C.x, C.y, C.radius, C.width) //holes() UL_HOLE //polygons() UL_POLYGON -> wires() //rectangles() UL_RECTANGLE - R.x1, R.y1, R.x2, R.y2); //wires() UL_WIRE //set found package flag foundPackage = 1; //used to capture first loop of data int loopCounter = 0; real w2; //holds 1/2 of width //loop thru wires and Get max/min x/y //do wires first to take care of loopCounter == 0 case P.wires(W) { //get first data point if (loopCounter == 0) { w2 = W.width / 2; minX = GetDimInBoardUnits( min(W.x1 - w2, W.x2 - w2)); minY = GetDimInBoardUnits( min(W.y1 - w2, W.y2 - w2)); maxX = GetDimInBoardUnits( max(W.x1 + w2, W.x2 + w2)); maxY = GetDimInBoardUnits( max(W.y1 + w2, W.y2 + w2)); } if (W.arc) { w2 = W.width / 2; if (W.arc.angle2 > 360) maxX = max(maxX, GetDimInBoardUnits( W.arc.xc + W.arc.radius)); else if (((W.arc.angle1 < 90) && (W.arc.angle2 > 90)) || (W.arc.angle2 > 450)) maxY = max(maxY, GetDimInBoardUnits( W.arc.yc + W.arc.radius + w2)); else if (((W.arc.angle1 < 180) && (W.arc.angle2 > 180)) || (W.arc.angle2 > 540)) minX = min(minX, GetDimInBoardUnits( W.arc.xc - W.arc.radius - w2)); else if (((W.arc.angle1 < 270) && (W.arc.angle2 > 270)) || (W.arc.angle2 > 630)) minY = min(minY, GetDimInBoardUnits( W.arc.yc - W.arc.radius - w2)); } else { //handle non arc wires minX = min(minX, GetDimInBoardUnits(W.x1)); minY = min(minY, GetDimInBoardUnits(W.y1)); maxX = max(maxX, GetDimInBoardUnits(W.x1)); maxY = max(maxY, GetDimInBoardUnits(W.y1)); } //end if-else loopCounter++; } //end wires //UpdateBBoxBox(C.x - C.radius - w2, C.y - C.radius - w2, C.x + C.radius + w2, C.y + C.radius + w2); // xmin, ymin, xmax, ymax P.circles(C) { w2 = C.width / 2; minX = min(minX, GetDimInBoardUnits(C.x - C.radius - w2)); minY = min(minY, GetDimInBoardUnits(C.y - C.radius - w2)); maxX = max(maxX, GetDimInBoardUnits(C.x + C.radius + w2)); maxY = max(maxY, GetDimInBoardUnits(C.y + C.radius + w2)); loopCounter++; } //end circles //P.holes(){} - Not required. These are solder masks P.polygons(POLY) { POLY.wires(PW) { minX = min(minX, GetDimInBoardUnits(PW.x1)); minY = min(minY, GetDimInBoardUnits(PW.y1)); maxX = max(maxX, GetDimInBoardUnits(PW.x1)); maxY = max(maxY, GetDimInBoardUnits(PW.y1)); loopCounter++; } //end wires in polygon } //end polgons P.rectangles(R) { minX = min(minX, min(GetDimInBoardUnits(R.x1), GetDimInBoardUnits(R.x2))); minY = min(minY, min(GetDimInBoardUnits(R.y1), GetDimInBoardUnits(R.y2))); maxX = max(maxX, max(GetDimInBoardUnits(R.x1), GetDimInBoardUnits(R.x2))); maxY = max(maxY, max(GetDimInBoardUnits(R.y1), GetDimInBoardUnits(R.y2))); loopCounter++; } //end rectangles //break out of loop since match found break; } //end if for package name match } //end packages loop } //end Libraries loop } //end board loop //make sure found match. if not then put in default values string temp; if (foundPackage) { //load to array sprintf(temp, "%s\t%f\t%f\t%f\t%f", packageName, maxX, maxY, minX, minY); } else { sprintf(temp, "%s\t%f\t%f\t%f\t%f", packageName, maxX, maxY, minX, minY); } //end if-else for found package check //add data to package outline so it can be reused in future PackageOutlines[PackageCounter] = temp; PackageCounter++; } //end if for lookup check //generate outline data. first line is footpring, name, and units string outline = footprint + " " + altname + " " + units; //set default height based on units if (units == "MM") { outline += " " + height + "\n"; //check if height set. if not set to 1.0 /*if (height == "1.0") { outline += " 1.00\n"; } else { outline += " " + height + "\n"; }*/ } /*else if (units == "THOU") { //check if if (height == "0.0") { outline += " 39.37\n"; } else { outline += " " + height + "\n"; } } */ else { //treat as MM //check if if (height == "1.0") { outline += " 1.00\n"; } else { outline += " " + height + "\n"; } } //end if-else //map in max/min data string maxX = lookup(PackageOutlines, packageName, 1); string maxY = lookup(PackageOutlines, packageName, 2); string minX = lookup(PackageOutlines, packageName, 3); string minY = lookup(PackageOutlines, packageName, 4); /*rectangle based on max/min values. start in upper left and move clockwise 0 minX maxY 0 maxX maxY 0 maxX minY 0 minX minY 0 minX maxY */ //if max/min are all zeros then put in 1x1 block if (maxX == "0.000000" && maxY == "0.000000" && minX == "0.000000" && minX == "0.000000") { outline += "0 1.00 -1.00 0.0\n" + "0 -1.00 -1.00 0.0\n" + "0 -1.00 1.00 0.0\n" + "0 1.00 1.00 0.0\n" + "0 1.00 -1.00 0.0\n"; } //if they are all the same they also set in 1x1 block so that something is shown else if (maxX == minX || maxY == minY) { outline += "0 1.00 -1.00 0.0\n" + "0 -1.00 -1.00 0.0\n" + "0 -1.00 1.00 0.0\n" + "0 1.00 1.00 0.0\n" + "0 1.00 -1.00 0.0\n"; } //otherwise actual coords provided. use those in output else { outline += "0 " + minX + " " + maxY + " 0.0\n" + "0 " + maxX + " " + maxY + " 0.0\n" + "0 " + maxX + " " + minY + " 0.0\n" + "0 " + minX + " " + minY + " 0.0\n" + "0 " + minX + " " + maxY + " 0.0\n"; } //end if-else return outline; } //end sub string GetEmpHeader() { /* .HEADER LIBRARY_FILE 3.0 "Commend International >generate_3d_data_v10-5_MJB.ulp V0.9<" 2012/03/21.12:23:09 1 .END_HEADER */ return ".HEADER\n" + "LIBRARY_FILE 3.0 \"Eagle IDF Exporter " + version + "\" " + GetDateTime() + "\n" + ".END_HEADER\n"; } //end sub string GetEmpElectrical() { /* .ELECTRICAL TSSOP16 NOREFDES MM 4876.80 0 -2.51 -2.28 0 0 2.51 -2.28 0 0 2.51 2.28 0 0 -2.51 2.28 0 0 -2.51 -2.28 0 .END_ELECTRICAL */ string electricalSection; string footprint; string altname; string height = NULL_HEIGHT; string LibraryLookup; string components[]; string component; //loop thru elements on board board(BRD) { int i = 0; BRD.elements(E) { height = NULL_HEIGHT; //check if library in lookup LibraryLookup = lookup(Libraries, strupr(E.package.library), 0); //set footprint and altname based on logic if (LibraryLookup != "") { footprint = E.package.name; altname = E.package.name; //add comment to emn for debuggin //placementSection += "#Resistor or rcl library found\n"; } else if (E.value != "") { altname = E.value; footprint = E.package.name; //add comment to emn for debuggin //placementSection += "#E.value present\n"; } else { //if library Is not in list and the E.value Is blank then concat refid and package name altname = E.name + "_" + E.package.name; footprint = E.package.name; //add comment to emn for debuggin //placementSection += "#Fallback logic applied. Not res/rcl and E.value not found.\n"; } altname = CleanString(altname); footprint = CleanString(footprint); //check for element height /* E.attributes(A) { if (strupr(A.name) == "HEIGHT") { if (strtod(A.value) < 0) { height = "0.000000"; } else { height = rtos(strtod(A.value)); } //end if-else } //end if } //end loop thru attributes */ //check if height set for element. if not set then set to NULL_HEIGHT height = GetElementHeightAttr( E ); //add component to height lookup table HeightHtmlTable = HeightHtmlTable + "" + E.name + "" + altname + "" + E.package.name + ""; //if height is not set then set to default if (height == NULL_HEIGHT) { height = "1.0"; //set to value of 1.0 HeightHtmlTable = HeightHtmlTable + "" + height + ""; } else { HeightHtmlTable = HeightHtmlTable + "" + height + ""; } //if-else for height value //if height is default value then do not append it to altname. this will prevent the height from poluting the naming convention //if height is not default then append it to altname so that its unique if( height == "1.0" ){ //component = lookup(components, footprint + "~" + altname, 0); - Fix for multi comp and heights component = lookup(components, footprint + "~" + altname, 0); //if component Is empty then it has not been written out yet. if (component == "") { //build section by call to GetComponentOutlineDummy( string footprint, string altname, string units ) electricalSection += ".ELECTRICAL\n" + GetComponentOutline(footprint, altname, E.package.name, GetBoardUnits(), height) + ".END_ELECTRICAL\n"; //add component to components list. //components[i] = footprint + "~" + altname; - Fix for multi comp and heights components[i] = footprint + "~" + altname; i++; } //end if } else{ component = lookup(components, footprint + "_" + height + "~" + altname + "_" + height, 0); //if component Is empty then it has not been written out yet. if (component == "") { //build section by call to GetComponentOutlineDummy( string footprint, string altname, string units ) electricalSection += ".ELECTRICAL\n" + GetComponentOutline(footprint + "_" + height, altname + "_" + height, E.package.name, GetBoardUnits(), height) + ".END_ELECTRICAL\n"; //add component to components list. //components[i] = footprint + "~" + altname; - Fix for multi comp and heights components[i] = footprint + "_" + height + "~" + altname + "_" + height; i++; } //end if } //end if-else } //end loop thru elements } //end loop thru board return electricalSection; } //end sub string GetEmpMechanical() { return ""; } //end sub //----------------------------------------------------------------------------- // emn subroutines //----------------------------------------------------------------------------- string GetEmnHeader() { /* .HEADER BOARD_FILE 3.0 "Commend International >generate_3d_data_v10-5_MJB.ulp V0.9<" 2012/03/21.12:23:10 1 "untitled.brd" MM .END_HEADER */ string boardName; //get board name board(BRD) { boardName = filename(BRD.name); } return ".HEADER\n" + "BOARD_FILE 3.0 \"Eagle IDF Exporter Version " + version + "\" " + GetDateTime() + "\n" + "\"" + boardName + "\" " + GetBoardUnits() + "\n" + ".END_HEADER\n"; } //end sub string SetUsedFlag(string pt) { string fields[]; int fieldCount = strsplit(fields, pt, '\t'); return fields[0] + "\t" + fields[1] + "\t" + fields[2] + "\t" + fields[3] + "\ty\n"; } int IsUsedPoint(string pt) { string fields[]; int fieldCount = strsplit(fields, pt, '\t'); if (fields[4] == "n\n") return 0; else return 1; } string IsUsedPointDebug(string pt) { string fields[]; int fieldCount = strsplit(fields, pt, '\t'); return fields[4]; } string FlipPoints(string line) { string fields[]; int fieldCount = strsplit(fields, line, '\t'); return fields[2] + "\t" + fields[3] + "\t" + fields[0] + "\t" + fields[1] + "\t" + fields[4]; } string GetFieldFromPtString(string ptString, int index) { string fields[]; // "pt 1\ttype\tx1\ty1\tx2\ty2\tsorted_flag" int fieldCount = strsplit(fields, ptString, '\t'); if (fieldCount >= index) { return fields[index]; } else { return ""; } //end if-else } //end sub real GetX1FromPtString(string ptString) { return strtod(GetFieldFromPtString(ptString, 0)); } //end sub real GetY1FromPtString(string ptString) { return strtod(GetFieldFromPtString(ptString, 1)); } //end sub real GetX2FromPtString(string ptString) { return strtod(GetFieldFromPtString(ptString, 2)); } //end sub real GetY2FromPtString(string ptString) { return strtod(GetFieldFromPtString(ptString, 3)); } //end sub int LayerNumInUse(int layerNum) { //returns true=1 if layer Is being used. returns 0 if layer Is not used. int layerUsed = 0; board(B) { B.layers(L) { if (L.number == layerNum) { layerUsed = 1; } //end if } //layers loop } //board loop return layerUsed; } int LayerIsIDFDebug(int layerNum) { board(B) { B.layers(L) { if (L.number == layerNum && L.name == "IDFDebug") { return 1; } //end if } //layers loop } //board loop return 0; } string GetIDFDebugLayerAvailNum() { //loop thru layers and check for name IDFDebug int startSearchNumber = 100; int layerAvail = -1; for (int i = startSearchNumber; i < 256; ++i) { //if layer Is not in use then use it. also have to check if layer Is in use but Is already idf debug if (!LayerNumInUse(i)) { layerAvail = i; break; } else { //check to make sure layer Is not already idf debug if (LayerIsIDFDebug(i)) { layerAvail = i; break; } //end if } //end if-else } //end for string temp; sprintf(temp, "%d", layerAvail); return temp; } //end func string GetIDFDebugLayerNum() { //loop thru layers and check for name IDFDebug int IDFDebugLayerNum = -1; board(B) { B.layers(L) { if (L.name == "IDFDebug") { IDFDebugLayerNum = L.number; } //end if } //layers loop } //board loop string temp; sprintf(temp, "%d", IDFDebugLayerNum); return temp; } //end func string RemoveIDFDebugLayerCommands() { //check if present string debugLayerNum = GetIDFDebugLayerNum(); //if debugLayerNum Is -1 then it doesnt exist so no delete needed. if (debugLayerNum == "-1") { return ""; } else { string commands; //commands //show only IDFDebugLayer commands += "DISPLAY -20 " + debugLayerNum + ";"; //run group all command to select all circles //commands += "GROUP ALL;"; //delete circles //DELETE (x y) where x y are points on circle outline board(B) { B.circles(C) { if (C.layer == strtol(debugLayerNum)) { commands += "DELETE ( " + rtos(GetDimInBoardUnits(C.x)) + " " + rtos(GetDimInBoardUnits(C.y)) + " );"; } //end if } //end wire loop B.wires(W) { if (W.layer == strtol(debugLayerNum)) { commands += "DELETE ( " + rtos(GetDimInBoardUnits(W.x1)) + " " + rtos(GetDimInBoardUnits(W.y1)) + " );"; } //end if } //end wire loop } //board loop //delete layer now commands += "LAYER ?? -" + debugLayerNum + ";"; return commands; } //end if/else } //end fund int CountWireHoles() { int holeCount = 0; board(BRD) { /* BRD.holes(H){ holeCount++; } //end holes */ BRD.circles(C) { if (C.layer == strtol(WireLayer)) { holeCount++; } } } //end board loop return holeCount; } //end func real Tolerance = GetTolerance(); /* string getComponentOutlinePoints(){ //init vars int Debug = 1; //used for debugging and generation of log in board outline processing string logFilePath = filesetext(GetBoardPath(), ".log"); string BoardThickness = GetBoardThickness(); string boardOutlineSection = ".BOARD_OUTLINE UNOWNED\n" + BoardThickness + "\n"; int wireCount = 0; string outlinePoints[]; //holds points extracted from board string outlinePointsSorted[]; //holds points after they are sorted real deg2rad = 0.0174532925; //used to convert degress to radians for arc parsing real bx1 = 0.0, by1 = 0.0, bx2 = 0.0, by2 = 0.0; //line start and end points real startX = 0.0, startY = 0.0; int outlineNum = 0; //used for drawing arc lines real incx = 0.0, incy = 0.0; //store incremental x/y values for arc real currentAngle = 0.0, incrementAngle = 15.0; string temp; //temp string used with sprintf int newOutline = 1; string pad = " "; real minDist = -1.0; //used to keep track of min distance found real dist = 0.0; //used to store distance between two points string fromPoints, toPoints; string boardOutlineSectionDebug; int matchFound = 0; //get list of component names in board board(B){ //go into library B.libraries(LBR){ //loop thru packages in library LBR.packages(P){ dlgMessageBox( P.name ); } //end package loop } //end library loop } //end board loop exit(0); // Step 1 & 2 //loop thru layer 20 and wires on layer board(BRD) { //loop thru wires BRD.wires(W) { //only pull layer 20 wires if (W.layer == strtol(WireLayer)) { //get wire start and end points //get x/y for start and end. bx1 = GetDimInBoardUnits(W.x1), by1 = GetDimInBoardUnits(W.y1), bx2 = GetDimInBoardUnits(W.x2), by2 = GetDimInBoardUnits(W.y2); //handle arcs and lines differently if (W.arc) { //for arc Get data to generate points real angle1 = W.arc.angle1, angle2 = W.arc.angle2, centerx = GetDimInBoardUnits(W.arc.xc), centery = GetDimInBoardUnits(W.arc.yc); real endx = bx2, endy = by2; //store arc end points to close arc. //reset current angle value currentAngle = incrementAngle; //determine which direction to draw arc by Getting x/y points based on angle1 incx = (cos(deg2rad * (angle1)) * GetDimInBoardUnits(W.arc.radius)) + centerx; incy = (sin(deg2rad * (angle1)) * GetDimInBoardUnits(W.arc.radius)) + centery; //check if arc start points match calculated start points using angle1. if they do then its a counter clockwise drawn arc if (CompareReals(incx, bx1) && CompareReals(incy, by1)) { ///loop thru 5 deg increments //arc defined counter clockwise so you need to subtract the increment while ((angle1 + currentAngle) < angle2) { incx = (cos(deg2rad * (angle1 + currentAngle)) * GetDimInBoardUnits(W.arc.radius)) + centerx; incy = (sin(deg2rad * (angle1 + currentAngle)) * GetDimInBoardUnits(W.arc.radius)) + centery; sprintf(temp, "%f\t%f\t%f\t%f\tn\n", bx1, by1, incx, incy); outlinePoints[wireCount] = temp; currentAngle += incrementAngle; wireCount++; //update beginning x/y values bx1 = incx; by1 = incy; } //end while } else { ///loop thru 5 deg increments //arc defined counter clockwise so you need to subtract the increment while ((angle2 - currentAngle) > angle1) { incx = (cos(deg2rad * (angle2 - currentAngle)) * GetDimInBoardUnits(W.arc.radius)) + centerx; incy = (sin(deg2rad * (angle2 - currentAngle)) * GetDimInBoardUnits(W.arc.radius)) + centery; sprintf(temp, "%f\t%f\t%f\t%f\tn\n", bx1, by1, incx, incy); outlinePoints[wireCount] = temp; currentAngle += incrementAngle; wireCount++; //update beginning x/y values bx1 = incx; by1 = incy; } //end while } //end if/else for check on direction of arc //close arc sprintf(temp, "%f\t%f\t%f\t%f\tn\n", bx1, by1, endx, endy); outlinePoints[wireCount] = temp; wireCount++; } else { //process lines sprintf(temp, "%f\t%f\t%f\t%f\tn\n", bx1, by1, bx2, by2); outlinePoints[wireCount] = temp; wireCount++; } //end if for arc } //end if check for layer=20 } //end loop thru wires } //end loop thru board //if there are wireholes then consider those as part of the outline if ( CountWireHoles() > 0) { board(B){ B.circles(C){ if (C.layer == strtol(WireLayer)) { real angle1 = 0; real angle2 = 360; real currentAngle = 0; real radius = GetDimInBoardUnits(C.radius); real centerx = GetDimInBoardUnits(C.x); real centery = GetDimInBoardUnits(C.y); real bx1 = centerx + radius; real by1 = centery; while ((angle1 + currentAngle) <= angle2) { //handle first point where angle = 0 if( currentAngle == 0 ){ incx = (cos(deg2rad * (angle1 + (incrementAngle/2))) * radius) + centerx; incy = (sin(deg2rad * (angle1 + (incrementAngle/2))) * radius) + centery; } else{ incx = (cos(deg2rad * (angle1 + currentAngle)) * radius) + centerx; incy = (sin(deg2rad * (angle1 + currentAngle)) * radius) + centery; } //end if-else for current angle = 0 sprintf(temp, "%f\t%f\t%f\t%f\tn\n", bx1, by1, incx,incy); outlinePoints[wireCount] = temp; currentAngle += incrementAngle; wireCount++; //update beginning x/y values bx1 = incx; by1 = incy; } //end while } //end if for layer check } //end circle loop } //end board loop } //wire holes if return outlinePoints; } //end func */ string GetEmnBoardOutline() { /* Board outline logic -------------------- 1. Loop thru wires on layer 20 and parse out points based on type of wire. Wire can be either arc or line. If it Is an arc then the .arc property Is set to 1. 2. For lines extract the start and end points. For arcs you need to convert them in to a set of lines that simulates an arc by drawing a line at angle increments. 3. After looping thru the wires on layer 20 verify that wires were actually found. If none were found then exit the ulp and notify the user no lines were found on layer 20. 4. Once the points are loaded to the array they need to reordered so that they are written in the correct order in the outline. This Is due to fact that the loop thru .wires provides the wires in the order they were drawn and not necessarily in the correct order. To accomplish this you need to first fine the start point. I do this by finding the point closest to 0,0. 5. Once the line with a start point closest to 0,0 Is found I then look thru the points to find a match for the end point of the first line. The lines may not be written in end point to start point so you need to check for closet match on both the start and end. 6. After sorting the lines you can now write out the points to the board_outline section. I check for matches from end to start points. If a match Isnt found then I assume a new outline Is found and increment the counter. I also need to make sure that when a new outline Is found that the prev outline Is closed. */ //init vars int Debug = 1; //used for debugging and generation of log in board outline processing string logFilePath = filesetext(GetBoardPath(), ".log"); string BoardThickness = GetBoardThickness(); string boardOutlineSection = ".BOARD_OUTLINE UNOWNED\n" + BoardThickness + "\n"; int wireCount = 0; string outlinePoints[]; //holds points extracted from board string outlinePointsSorted[]; //holds points after they are sorted real deg2rad = 0.0174532925; //used to convert degress to radians for arc parsing real bx1 = 0.0, by1 = 0.0, bx2 = 0.0, by2 = 0.0; //line start and end points real startX = 0.0, startY = 0.0; int outlineNum = 0; //used for drawing arc lines real incx = 0.0, incy = 0.0; //store incremental x/y values for arc real currentAngle = 0.0, incrementAngle = 15.0; string temp; //temp string used with sprintf int newOutline = 1; string pad = " "; real minDist = -1.0; //used to keep track of min distance found real dist = 0.0; //used to store distance between two points string fromPoints, toPoints; string boardOutlineSectionDebug; int matchFound = 0; /* Step 1 & 2 */ //loop thru layer 20 and wires on layer board(BRD) { //loop thru wires BRD.wires(W) { //only pull layer 20 wires if (W.layer == strtol(WireLayer)) { //get wire start and end points //get x/y for start and end. bx1 = GetDimInBoardUnits(W.x1), by1 = GetDimInBoardUnits(W.y1), bx2 = GetDimInBoardUnits(W.x2), by2 = GetDimInBoardUnits(W.y2); //handle arcs and lines differently if (W.arc) { //for arc Get data to generate points real angle1 = W.arc.angle1, angle2 = W.arc.angle2, centerx = GetDimInBoardUnits(W.arc.xc), centery = GetDimInBoardUnits(W.arc.yc); real endx = bx2, endy = by2; //store arc end points to close arc. //reset current angle value currentAngle = incrementAngle; //determine which direction to draw arc by Getting x/y points based on angle1 incx = (cos(deg2rad * (angle1)) * GetDimInBoardUnits(W.arc.radius)) + centerx; incy = (sin(deg2rad * (angle1)) * GetDimInBoardUnits(W.arc.radius)) + centery; //check if arc start points match calculated start points using angle1. if they do then its a counter clockwise drawn arc if (CompareReals(incx, bx1) && CompareReals(incy, by1)) { ///loop thru 5 deg increments //arc defined counter clockwise so you need to subtract the increment while ((angle1 + currentAngle) < angle2) { incx = (cos(deg2rad * (angle1 + currentAngle)) * GetDimInBoardUnits(W.arc.radius)) + centerx; incy = (sin(deg2rad * (angle1 + currentAngle)) * GetDimInBoardUnits(W.arc.radius)) + centery; sprintf(temp, "%f\t%f\t%f\t%f\tn\n", bx1, by1, incx, incy); outlinePoints[wireCount] = temp; currentAngle += incrementAngle; wireCount++; //update beginning x/y values bx1 = incx; by1 = incy; } //end while } else { ///loop thru 5 deg increments //arc defined counter clockwise so you need to subtract the increment while ((angle2 - currentAngle) > angle1) { incx = (cos(deg2rad * (angle2 - currentAngle)) * GetDimInBoardUnits(W.arc.radius)) + centerx; incy = (sin(deg2rad * (angle2 - currentAngle)) * GetDimInBoardUnits(W.arc.radius)) + centery; sprintf(temp, "%f\t%f\t%f\t%f\tn\n", bx1, by1, incx, incy); outlinePoints[wireCount] = temp; currentAngle += incrementAngle; wireCount++; //update beginning x/y values bx1 = incx; by1 = incy; } //end while } //end if/else for check on direction of arc //close arc sprintf(temp, "%f\t%f\t%f\t%f\tn\n", bx1, by1, endx, endy); outlinePoints[wireCount] = temp; wireCount++; } else { //process lines sprintf(temp, "%f\t%f\t%f\t%f\tn\n", bx1, by1, bx2, by2); outlinePoints[wireCount] = temp; wireCount++; } //end if for arc } //end if check for layer=20 } //end loop thru wires } //end loop thru board //if there are wireholes then consider those as part of the outline if ( CountWireHoles() > 0) { board(B){ B.circles(C){ if (C.layer == strtol(WireLayer)) { real angle1 = 0; real angle2 = 360; real currentAngle = 0; real radius = GetDimInBoardUnits(C.radius); real centerx = GetDimInBoardUnits(C.x); real centery = GetDimInBoardUnits(C.y); real bx1 = centerx + radius; real by1 = centery; while ((angle1 + currentAngle) <= angle2) { //handle first point where angle = 0 if( currentAngle == 0 ){ incx = (cos(deg2rad * (angle1 + (incrementAngle/2))) * radius) + centerx; incy = (sin(deg2rad * (angle1 + (incrementAngle/2))) * radius) + centery; } else{ incx = (cos(deg2rad * (angle1 + currentAngle)) * radius) + centerx; incy = (sin(deg2rad * (angle1 + currentAngle)) * radius) + centery; } //end if-else for current angle = 0 sprintf(temp, "%f\t%f\t%f\t%f\tn\n", bx1, by1, incx,incy); outlinePoints[wireCount] = temp; currentAngle += incrementAngle; wireCount++; //update beginning x/y values bx1 = incx; by1 = incy; } //end while } //end if for layer check } //end circle loop } //end board loop } //wire holes if /* Step 3 - Verify wires found */ if (wireCount == 0) { dlgMessageBox("No wires were found on layer 20.\nUnable to detect board outline. Exiting."); //add in check if user wants to use a component as the board outline if ( dlgMessageBox("No outlines were found on layer 20.\n\nWould you like to select a component to use as outline?\n\nIf you select No then the program will exit.", "&Yes", "&No") == 0) { //call function to get from component and then assign to ouline array //outlinePoints = getComponentOutlinePoints(); } else{ exit(0); } } //end if for wirecount = 0 check if (Debug) { boardOutlineSectionDebug += "########### array before sorts ###########\n"; for (int j = 0; j < wireCount; j++) { sprintf(temp, "%d\t%s", j, outlinePoints[j]); boardOutlineSectionDebug += temp; } //end for } //end if for debug /* //calculate Tolerance based on 1/2 of smallest wire length for( int i = 0; i < wireCount; i++ ){ bx1 = GetX1FromPtString( outlinePoints[ i ] ); by1 = GetY1FromPtString( outlinePoints[ i ] ); bx2 = GetX2FromPtString( outlinePoints[ i ] ); by2 = GetY2FromPtString( outlinePoints[ i ] ); dist = GetDistBetweenPts( bx1, by1, bx2, by2 ); if( minDist == -1.0 ){ minDist = dist; } else if( dist < minDist ){ minDist = dist; } else{ //do nothing if dist Is not smaller then minDist } } //end for loop Tolerance = minDist / 2; */ /* Step 4 & 5 - Verify wires found */ //loop thru points and sort so they are in order //outer loop thru points boardOutlineSectionDebug += "Tolerance=" + rtos(Tolerance) + ".\n"; for (int i = 0; i < wireCount; i++) { //boardOutlineSection += itos( outlineNum ) + pad + // rtos( GetX1FromPtString( outlinePoints[ i ] ) ) + pad + // rtos( GetY1FromPtString( outlinePoints[ i ] ) ) + pad + "0\n"; outlinePoints[i] = SetUsedFlag(outlinePoints[i]); //get line end points bx2 = GetX2FromPtString(outlinePoints[i]); by2 = GetY2FromPtString(outlinePoints[i]); //handle initializations, new outlines, and closed outline checks. if (i == 0) { startX = GetX1FromPtString(outlinePoints[i]); startY = GetY1FromPtString(outlinePoints[i]); boardOutlineSection += itos(outlineNum) + pad + rtos(GetX1FromPtString(outlinePoints[i])) + pad + rtos(GetY1FromPtString(outlinePoints[i])) + pad + "0\n"; newOutline = 0; } else if (newOutline) { startX = GetX1FromPtString(outlinePoints[i]); startY = GetY1FromPtString(outlinePoints[i]); boardOutlineSection += itos(outlineNum) + pad + rtos(GetX1FromPtString(outlinePoints[i])) + pad + rtos(GetY1FromPtString(outlinePoints[i])) + pad + "0\n"; newOutline = 0; } //check if point closes outline else if (GetDistBetweenPts(startX, startY, GetX2FromPtString(outlinePoints[i]), GetY2FromPtString(outlinePoints[i])) == 0) { boardOutlineSection += itos(outlineNum) + pad + rtos(GetX2FromPtString(outlinePoints[i])) + pad + rtos(GetY2FromPtString(outlinePoints[i])) + pad + "0\n"; outlineNum++; newOutline = 1; continue; } //end if else if (GetDistBetweenPts(startX, startY, GetX2FromPtString(outlinePoints[i]), GetY2FromPtString(outlinePoints[i])) <= Tolerance) { boardOutlineSection += itos(outlineNum) + pad + rtos(startX) + pad + rtos(startY) + pad + "0\n"; outlineNum++; newOutline = 1; continue; } //end if matchFound = 0; //inner loop thru points to find matching line for (int j = 0; j < wireCount; j++) { //boardOutlineSectionDebug += "Checking points for i=" + itos( i ) + " and j=" + itos( j ) + ".\n"; //check if line Is already used. if it Is then skip it if (!IsUsedPoint(outlinePoints[j])) { //check for exact match on line start if (GetDistBetweenPts(bx2, by2, GetX1FromPtString(outlinePoints[j]), GetY1FromPtString(outlinePoints[j])) == 0) { boardOutlineSectionDebug += "*Exact match for i=" + itos(i) + " and j=" + itos(j) + ".\n"; boardOutlineSectionDebug += "*i point " + outlinePoints[i]; boardOutlineSection += itos(outlineNum) + pad + rtos(GetX1FromPtString(outlinePoints[j])) + pad + rtos(GetY1FromPtString(outlinePoints[j])) + pad + "0\n"; //flag points as used outlinePoints[j] = SetUsedFlag(outlinePoints[j]); //swap matched point with current point //outlinePoints = swapPoints( outlinePoints, (i+1), j ); toPoints = outlinePoints[j]; fromPoints = outlinePoints[(i + 1)]; //move toIndex to from position outlinePoints[(i + 1)] = toPoints; outlinePoints[j] = fromPoints; matchFound = 1; break; } //check for exact match on line end else if (GetDistBetweenPts(bx2, by2, GetX2FromPtString(outlinePoints[j]), GetY2FromPtString(outlinePoints[j])) == 0) { boardOutlineSectionDebug += "*Exact match for i=" + itos(i) + " and j=" + itos(j) + ".\n"; boardOutlineSectionDebug += "*i point " + outlinePoints[i]; boardOutlineSection += itos(outlineNum) + pad + rtos(GetX2FromPtString(outlinePoints[j])) + pad + rtos(GetY2FromPtString(outlinePoints[j])) + pad + "0\n"; //flag point as used outlinePoints[j] = SetUsedFlag(outlinePoints[j]); boardOutlineSectionDebug += "\t*Before flip \t" + outlinePoints[j]; //flip point outlinePoints[j] = FlipPoints(outlinePoints[j]); boardOutlineSectionDebug += "\t*After flip \t" + outlinePoints[j]; //swap matched point with current point //outlinePoints = swapPoints( outlinePoints, (i+1), j ); toPoints = outlinePoints[j]; fromPoints = outlinePoints[(i + 1)]; //move toIndex to from position outlinePoints[(i + 1)] = toPoints; outlinePoints[j] = fromPoints; matchFound = 1; break; } //check for Tolerance match on line start else if (GetDistBetweenPts(bx2, by2, GetX1FromPtString(outlinePoints[j]), GetY1FromPtString(outlinePoints[j])) <= Tolerance) { boardOutlineSectionDebug += "*Tolerance match for i=" + itos(i) + " and j=" + itos(j) + ". Dist=" + rtos( GetDistBetweenPts(bx2, by2, GetX1FromPtString(outlinePoints[j]), GetY1FromPtString( outlinePoints[j]))) + "\n"; boardOutlineSectionDebug += "*i point " + outlinePoints[i]; boardOutlineSection += itos(outlineNum) + pad + rtos(bx2) + pad + rtos(by2) + pad + "0\n"; boardOutlineSectionDebug += "\t*Before fix for Tolerance \t" + outlinePoints[j]; //update matched point start points to prev line end points to fix the gap sprintf(temp, "%f\t%f\t%f\t%f\tn\n", bx2, by2, GetX2FromPtString(outlinePoints[j]), GetY2FromPtString(outlinePoints[j])); outlinePoints[j] = temp; boardOutlineSectionDebug += "\t*After fix for Tolerance \t" + outlinePoints[j]; //flag points as used outlinePoints[j] = SetUsedFlag(outlinePoints[j]); //swap matched point with current point //outlinePoints = swapPoints( outlinePoints, (i+1), j ); toPoints = outlinePoints[j]; fromPoints = outlinePoints[(i + 1)]; //move toIndex to from position outlinePoints[(i + 1)] = toPoints; outlinePoints[j] = fromPoints; matchFound = 1; break; } //check for Tolerance match on line end else if (GetDistBetweenPts(bx2, by2, GetX2FromPtString(outlinePoints[j]), GetY2FromPtString(outlinePoints[j])) <= Tolerance) { boardOutlineSectionDebug += "*Tolerance match for i=" + itos(i) + " and j=" + itos(j) + ". Dist=" + rtos( GetDistBetweenPts(bx2, by2, GetX2FromPtString(outlinePoints[j]), GetY2FromPtString( outlinePoints[j]))) + "\n"; boardOutlineSectionDebug += "*i point " + outlinePoints[i]; boardOutlineSection += itos(outlineNum) + pad + rtos(bx2) + pad + rtos(by2) + pad + "0\n"; boardOutlineSectionDebug += "\t*Before fix for Tolerance \t" + outlinePoints[j]; //update matched point start points to prev line end points to fix the gap sprintf(temp, "%f\t%f\t%f\t%f\tn\n", GetX1FromPtString(outlinePoints[j]), GetY1FromPtString(outlinePoints[j]), bx2, by2); outlinePoints[j] = temp; boardOutlineSectionDebug += "\t*After fix for Tolerance \t" + outlinePoints[j]; //flag points as used outlinePoints[j] = SetUsedFlag(outlinePoints[j]); boardOutlineSectionDebug += "\t*Before flip \t" + outlinePoints[j]; //flip point outlinePoints[j] = FlipPoints(outlinePoints[j]); boardOutlineSectionDebug += "\t*After flip \t" + outlinePoints[j]; //swap matched point with current point //outlinePoints = swapPoints( outlinePoints, (i+1), j ); toPoints = outlinePoints[j]; fromPoints = outlinePoints[(i + 1)]; //move toIndex to from position outlinePoints[(i + 1)] = toPoints; outlinePoints[j] = fromPoints; matchFound = 1; break; } //else line has not matching point. else { //boardOutlineSectionDebug += "No match for i=" + itos( i ) + " and j=" + itos( j ) + ".\n"; } //end if-else } else { //do nothing for skipped line //boardOutlineSectionDebug += "Points skipped for i=" + itos( i ) + " and j=" + itos( j ) + ".\n"; } //end if/else } //end j loop //check to make sure a matching point was found. if not notify user and mark on board. if (!matchFound) { boardOutlineSectionDebug += "No match for i=" + itos(i) + ".\n"; //if no match found then generate circle command and exit. string circleDefectCommands; string runCommands; sprintf(circleDefectCommands, "CIRCLE (%f %f) (%f %f);", BackConvertDimensions(bx2), BackConvertDimensions(by2), BackConvertDimensions(bx2 + 2.0), BackConvertDimensions(by2)); //delete existing Debug layer if it exists runCommands += RemoveIDFDebugLayerCommands(); //get Debug layer number string debugLayerNum = GetIDFDebugLayerAvailNum(); //dlgMessageBox( "A fatal defect was found in your board outline in the form\n" + // "of a gap in the board outline. We cannot repair this defect.\n\n" + // "Defects will be shown on layer " + debugLayerNum + " with red circles." ); dlgMessageBox( "An error was found in your board outline that cannot be automatically repaired. Outlines must be enclosed loops.\n" + "Defects will be shown on layer " + debugLayerNum + " with red circles."); string wireCommands = "change width 0.01;\nSET Wire_Bend 2;\n"; if (Debug) { boardOutlineSectionDebug += "########### array after sorts ###########\n"; //build wire commands that will draw the outlines that have been matched. the last wire will be drawn but it has no match for (int j = 0; j < wireCount; j++) { sprintf(temp, "%d\t%s", j, outlinePoints[j]); boardOutlineSectionDebug += temp; if (IsUsedPoint(outlinePoints[j])) { wireCommands += "WIRE ( " + rtos( BackConvertDimensions( GetX1FromPtString( outlinePoints[j]))) + " " + rtos( BackConvertDimensions( GetY1FromPtString( outlinePoints[j]))) + ") (" + rtos( BackConvertDimensions( GetX2FromPtString( outlinePoints[j]))) + " " + rtos( BackConvertDimensions( GetY2FromPtString( outlinePoints[j]))) + ");"; } } //end for } //end if //circleDefectCommands = "LAYER 100 IDFDebug;SET COLOR_LAYER 100 red;CHANGE WIDTH 0.01;" + //removed change width comand runCommands += "LAYER " + debugLayerNum + " IDFDebug;" + "SET COLOR_LAYER " + debugLayerNum + " red;" + circleDefectCommands + wireCommands + "DISPLAY NONE;" + "DISPLAY 20 " + debugLayerNum + ";" + "WINDOW FIT;"; if (Debug) { WriteToFile(logFilePath, boardOutlineSectionDebug + "***************\n" + runCommands + "***************\n" + boardOutlineSection); } exit(runCommands); } //end if for matchfound } //end i loop if (Debug) { boardOutlineSectionDebug += "########### array after sorts ###########\n"; for (int j = 0; j < wireCount; j++) { sprintf(temp, "%d\t%s", j, outlinePoints[j]); boardOutlineSectionDebug += temp; } //end for } //end if if (Debug) { WriteToFile(logFilePath, boardOutlineSectionDebug + "***************\n" + boardOutlineSection); } boardOutlineSection += ".END_BOARD_OUTLINE\n"; return boardOutlineSection; } //end GetEmnBoardOutline string GetEmnDrilledHoles() { /* .DRILLED_HOLES 40.0 1773.0 1207.5 PTH S1 PIN ECAD 40.0 1773.0 1384.5 PTH S1 PIN ECAD 40.0 2029.0 1207.5 PTH S1 PIN ECAD ... .END_DRILLED_HOLES */ string drilledHolesSection = ".DRILLED_HOLES\n"; string temp; int holeCount = 0; board(BRD) { //loop thru holes BRD.holes(H) { sprintf(temp, "%10.4f%10.4f%10.4f NPTH BOARD OTHER UNOWNED\n", (GetDimInBoardUnits(H.drill)), (GetDimInBoardUnits(H.x)), (GetDimInBoardUnits(H.y))); drilledHolesSection += temp; holeCount++; } //end holes //get any holes from packages on elements BRD.elements(E) { E.package.holes(C) { sprintf(temp, "%10.4f%10.4f%10.4f NPTH %-4s PIN UNOWNED\n", (GetDimInBoardUnits(C.drill)), (GetDimInBoardUnits(C.x)), (GetDimInBoardUnits(C.y)), E.name); drilledHolesSection += temp; holeCount++; } //end package.holes E.package.contacts(P) { if (P.pad) { sprintf(temp, "%.2f %.2f %.2f PTH BOARD VIA UNOWNED\n", GetDimInBoardUnits(P.pad.drill), GetDimInBoardUnits(P.x), GetDimInBoardUnits(P.y)); drilledHolesSection += temp; holeCount++; } //end if } //end loop thru contacts } //end elements //get any holes on vias BRD.signals(S) { S.vias(V) { sprintf(temp, "%10.4f%10.4f%10.4f NPTH BOARD OTHER UNOWNED\n", (GetDimInBoardUnits(V.drill)), (GetDimInBoardUnits(V.x)), (GetDimInBoardUnits(V.y))); drilledHolesSection += temp; holeCount++; } } } //end board loop //close drilled holes section drilledHolesSection += ".END_DRILLED_HOLES\n"; //check if drilled holes Is empty and if so clear it if (holeCount == 0) { drilledHolesSection = ""; } return drilledHolesSection; } //end sub string GetEmnPlacements() { /* .PLACEMENT 0805 RES R4 2300.0 1275.0 0.0 180.0 TOP PLACED 0805 CAP C13 2565.0 1380.0 0.0 0.0 TOP PLACED ... .END_PLACEMENT */ string sideMap[] = { "TOP", "BOTTOM" }; string placementSection; string footprint; string altname; string ref_id; string components[]; string component; string temp; string side; string height; placementSection = ".PLACEMENT\n"; //loop thru elements on board board(BRD) { int i = 0; BRD.elements(E) { LibraryLookup = lookup(Libraries, strupr(E.package.library), 0); //logic to setup footprint an altname values if (LibraryLookup != "") { //if found in LibraryLookup then assign package name to both footprint and altname altname = E.package.name; footprint = E.package.name; } else if (E.value != "") { //if e.value Is not empty then put it in the footprint altname = E.value; footprint = E.package.name; } else { //if library Is not in list and the E.value Is blank then concat refid and package name altname = E.name + "_" + E.package.name; footprint = E.package.name; } //end if-else footprint = CleanString(footprint); altname = CleanString(altname); ref_id = E.name; ref_id = CleanString(ref_id); //get side value based on mirror value side = sideMap[E.mirror]; //check if height set for element. if not set then set to NULL_HEIGHT height = GetElementHeightAttr( E ); //if height is not set then set to default if (height == NULL_HEIGHT) { height = "1.0"; //set to value of 1.0 } //if default height then dont append to altname. otherwise put unique height on altname if( height == "1.0" ){ sprintf(temp, "%s %s %s\n%f %f 0 %f %s PLACED\n", footprint, altname, ref_id, GetDimInBoardUnits(E.x), GetDimInBoardUnits(E.y), E.angle, side); //add component to components list components[i] = footprint + "~" + altname; } else{ sprintf(temp, "%s %s %s\n%f %f 0 %f %s PLACED\n", footprint + "_" + height, altname + "_" + height, ref_id, GetDimInBoardUnits(E.x), GetDimInBoardUnits(E.y), E.angle, side); //add component to components list components[i] = footprint + "_" + height + "~" + altname + "_" + height; } //end if-else placementSection += temp; i++; } //end loop thru elements } //end loop thru board placementSection += ".END_PLACEMENT\n"; return placementSection; } //end sub string GenerateEmn() { //build emn file string emn = GetEmnHeader() + GetEmnBoardOutline() + GetEmnDrilledHoles() + GetEmnPlacements(); return emn; } //end sub string GenerateEmp() { //build emp file string emp = GetEmpHeader() + GetEmpElectrical() + GetEmpMechanical(); return emp; } //end sub //Submits emn and emp data to IDF site. IDF returns full html page. void SubmitFormData(){ //update user now as the post will take some time to start TextMessageDialog = GetHtmlText( "
3D build request submitted. Please wait for update...

















" ); dlgRedisplay(); if( 0 ){ dlgMessageBox( path_ulp[0] ); } //get board name string boardName; string boardDir; board( BRD ){ boardName = filename( BRD.name ); boardDir = filedir( BRD.name ); } //end board //get emn and emp strings string emnData = GenerateEmn() + "\n"; string empData = GenerateEmp() + "\n"; //convert to html format emnData = ParseStringToHtmlForm( emnData ); empData = ParseStringToHtmlForm( empData ); //data for url post string postResponse; string postData; int requestSubmitted = 0; string urlParams; //strings used to build some html elements string tempStr; string thumbnailImg; string pdf3dLink; string thumbnailLink; string purchaseStlLink; string purchaseDownloadLink; string complete3DDesignLink; //response data string jobId; string jobStatus; string percentComplete; string sessionKey; string sessionLink; string downloadPurchaseUrl; string stlPurchaseUrl; string complete3DDesignUrl; int jobCompleted = 0; //submit post and get response sprintf( postData,"emn=%s\nemp=%s\nboardname=%s\nversion=%s", emnData, empData, boardName, version ); if( netpost( postResponse, Service_url, postData ) >= 0 ) { //dlgMessageBox( postResponse ); //get session key response sessionKey = xmltext( postResponse, "response/session_key" ); //sessionLink = xmltext( postResponse, "response/session_link" ); //sprintf( complete3DDesignLink, "Click here to open your board.", sessionLink ); TextMessageDialog = GetHtmlText( "
Your board has been uploaded and is ready.
\ Your browser will open in the background.

























" ); //update display dlgRedisplay(); string login = Login_url + "?sid=" + sessionKey; LaunchBrowser( login ); } else{ //handle error in netpost TextMessageDialog = GetHtmlText( "We were unable to complete your request at this time. Please try submit again. \ If error persists contact support. \ Contact Support
\ Error Code:" + neterror() + "
Post Response:" + postResponse ); dlgRedisplay(); } //end if-else for posting //********** old code below **********/ /* //loop thru form post for( int i = 0; i < 50000; i++ ){ //only check every 10th loop if( i % 10 == 0 ){ //check if request has already been submitted if( !requestSubmitted ){ //load data to post parameters including secondary flag sprintf( postData,"emn=%s\nemp=%s\nboardname=%s\nsecondary=y\nversion=%s", emnData, empData, boardName, version ); } else{ //load data to post parameters sprintf( postData,"id=%s\nsecondary=y", jobId ); } //end if-else //post data and get response if( netpost( postResponse, Service_url, postData ) >= 0 ) { //after first request set to 1/true requestSubmitted = 1; //parse xml response data jobStatus = xmltext( postResponse, "response/status" ); jobId = xmltext( postResponse, "response/id" ); percentComplete = xmltext( postResponse, "response/percent_complete" ); sessionKey = xmltext( postResponse, "response/session_key" ); //get response and parse. first check status if( jobStatus == "ERROR" ){ //post error to user TextMessageDialog = GetHtmlText( "Error in netpost. Please try submit again. If error persists contact support. Contact Support" ); dlgRedisplay(); break; } else if( jobStatus == "ACTIVE" || jobStatus == "NOT STARTED" || jobStatus == "not started" ){ //sprintf( tempStr, "%s

Request is active. Percent Complete: %s

", tagImgActive, percentComplete); sprintf( tempStr, "


Request is active.


Percent Complete: %s%%













", percentComplete ); TextMessageDialog = GetHtmlText( tempStr ); } else if( jobStatus == "COMPLETE" ){ //set job complete flag jobCompleted = 1; //dlgMessageBox( postResponse ); //get link from xml response //downloadPurchaseUrl = xmltext( postResponse, "response/betaDownloadKey" ); //stlPurchaseUrl = xmltext( postResponse, "response/betaStlKey" ); //complete3DDesignUrl = xmltext( postResponse, "response/betaComplete3DDesignKey" ); complete3DDesignUrl = xmltext( postResponse, "response/complete3DDesignKey" ); //build image link sprintf( thumbnailImg, "\"If", sessionKey); sprintf( thumbnailLink, "Open Larger Thumbnail - Free", sessionKey); sprintf( pdf3dLink, "Click here", sessionKey); //links for purchase //sprintf( purchaseDownloadLink, "Click here", downloadPurchaseUrl ); //sprintf( purchaseStlLink, "Click here", stlPurchaseUrl ); sprintf( complete3DDesignLink, "Click here", complete3DDesignUrl ); TextMessageDialog = GetHtmlText( "

Generating data complete: A session to map your components with our 3D database is ready.

\ The picture shows your board and components that could be mapped automatically. \ \ \ \ \ \
" + thumbnailImg + "
Important: Automatic component mapping works only with the original EAGLE Library. Components shown in red can be mapped manually.

\     " + complete3DDesignLink + " to complete the component mapping manually \
    \
  • Use of our IDF-to-3D tool is free
  • \
  • Generate a 3D PDF of your 3D Board Assembly at no cost
  • \
  • Incorporate additional 3D Models to your design by using our IDF-to-3D model library and 3D STEP Model integration tool
  • \
\     If satisfied with your 3D image:

\     " + pdf3dLink + " to open a 3D PDF (Free)

\
" ); //update display dlgRedisplay(); break; } else{ TextMessageDialog = GetHtmlText( "There was an error in your request. Please try submit again. \ If error persists contact support. \ Contact Support
\ Error Code:" + neterror() + " - Unable to parse xml response

\ XML Response:" + postResponse ); dlgRedisplay(); //dlgMessageBox( postResponse ); break; } //this is needed to update the text display dlgRedisplay(); } else{ //handle error in netpost TextMessageDialog = GetHtmlText( "We were unable to complete your request at this time. Please try submit again. \ If error persists contact support. \ Contact Support
\ Error Code:" + neterror() + "
Post Response:" + postResponse ); dlgRedisplay(); break; } //end if-else } } //end for //alert user to job success if( jobCompleted ){ dlgMessageBox( "Your job is complete" ); } else{ dlgMessageBox( "Your job ended in error\n Job Status: " + jobStatus ); TextMessageDialog = GetHtmlText( "There was an error in your request. Please try submit again. \ If error persists contact support. \ Contact Support
\ Error Code:" + neterror() + "
" ); //force text view to update dlgRedisplay(); } //end if-else */ } //end sub //main function to generate IDF fils void GenerateEmnEmpFiles() { //update user now as the post will take some time to start TextMessageDialog = GetHtmlText( "IDF File generation in process."); HeightHtmlTable = ""; //reset //refresh display to show message dlgRedisplay(); //get board name string boardName; string boardDir; board(BRD) { boardName = filename(BRD.name); boardDir = filedir(BRD.name); } //end board //get emn and emp strings string emnData = GenerateEmn(); string empData = GenerateEmp(); //write strings to files string dirName; dirName = dlgDirectory("Select a directory to write IDF files to:", boardDir); //show path to user dlgMessageBox( "Writing IDF Files: " + dirName + GetFileSeperator() + filesetext(boardName, ".emn")); output(dirName + GetFileSeperator() + filesetext(boardName, ".emn"), "wt") { printf(emnData); } output(dirName + GetFileSeperator() + filesetext(boardName, ".emp"), "wt") { printf(empData); } //complete height table HeightHtmlTable = "The table below shows the components placed in the emn/emp files. \ The height field in the table indicates an extrusion height used in generating the components on the 3D model. \ A default value is set if a value is not set and is indicated in yellow. The values are defined as HEIGHT attribute values and can be updated in the board file. \ Heights are set at the element level and can vary by placement. This may cause unexpected behavior if you only update a single component in the file.
\ " + HeightHtmlTable + "
Ref. IDAlt. NamePackage NameHeight
"; //" + HeightHtmlTable + "
Ref. IDAlt. NamePackage NameHeight
"; TextMessageDialog = GetHtmlText( "IDF Files written successfully to " + dirName + "

" + HeightHtmlTable); } //end sub void LoadLibraries() { int libraryCount = 0; //load library string in to Libraries array libraryCount = strsplit(Libraries, LibraryLookup, ':'); } //end sub string GetHomepageBanner(){ string temp; //get banner along with version and OS data to help with support and detect unsupportedconfigs for future releases string url = "https://www.simplifiedsolutionsinc.com/EagleTo3D/eagle_to_3d_ssi_banner.php?os=" + OS_SIGNATURE + "&v=" + version; if( netget( temp, url) >= 0 ){ //pass return temp; } else{ //fail return ""; } //end if-else } string GetHomepageLinks(){ string temp; string url = "https://www.simplifiedsolutionsinc.com/EagleTo3D/eagle_to_3d_ssi_bullets.php"; if( netget( temp, url) >= 0 ){ //pass return temp; } else{ //fail return ""; } //end if-else } string RunNetworkTests(){ /* 1. Check internet 2. Check SSI site 3. Check SSI site with port */ string temp = ""; string internetSite = "http://www.google.com/"; int internetSiteResult = 0; string internetSiteError = ""; string internetSiteTroubleshootMsg = ""; string ssiSite = "https://www.simplifiedsolutionsinc.com/"; int ssiSiteResult = 0; string ssiSiteError = ""; string ssiSiteTroubleshootMsg = ""; string ssiWithPortSite = "http://www.simplifiedsolutionsinc.com:8080/"; int ssiWithPortSiteResult = 0; string ssiWithPortSiteError = ""; string ssiWithPortSiteTroubleshootMsg = ""; if( netget( temp, internetSite ) >= 0 ){ //pass internetSiteResult = 1; } else{ //fail internetSiteResult = 0; internetSiteError = neterror(); internetSiteTroubleshootMsg = "We are unable to reach the internet. Please verify your internet connectivity. If you have an internet proxy please configure it under Help->Check for Update->Configure in the Eagle Control Panel window before running the ULP"; } //end if-else if( netget( temp, ssiSite ) >= 0 ){ //pass ssiSiteResult = 1; } else{ //fail ssiSiteResult = 0; ssiSiteError = neterror(); ssiSiteTroubleshootMsg = "We are unable to reach the internet. Please verify your internet connectivity. If you have an internet proxy please configure it under Help->Check for Update->Configure in the Eagle Control Panel window before running the ULP"; } //end if-else if( netget( temp, ssiWithPortSite ) >= 0 ){ //pass ssiWithPortSiteResult = 1; } else{ //fail ssiWithPortSiteResult = 0; ssiWithPortSiteError = neterror(); ssiWithPortSiteTroubleshootMsg = "We are unable to reach the Simplified Solutions site at port 8080. Please verify your internet connectivity. You may need to request your firewall rules be opened for port 8080."; } //end if-else //check results of tests if( internetSiteResult && ssiSiteResult && ssiWithPortSiteResult ){ return "PASS"; } else{ return internetSiteError + ":" + internetSiteTroubleshootMsg + ";"; } } //----------------------------------------------------------------------------- // main section //----------------------------------------------------------------------------- void main() { //check to make sure board Is loaded if (!board) { dlgMessageBox( usage + "
ERROR: No board!

\nThis program can only work in the layout editor.

"); exit(-1); } //load Libraries array for lookups LoadLibraries(); //run network test string networkTestResults = RunNetworkTests(); string networkTestMessage = ""; //dlgMessageBox( strsub( OS_SIGNATURE, 0, 3 ) ); //if network test fails then add in note to homepage if( networkTestResults == "PASS" ){ networkTestMessage = ""; } else{ networkTestMessage ="We have detect potential network/web connection issues that may impact the execution of this ULP.
Troubleshooting:" + networkTestResults + "

"; } //get banner string homepageBanner = GetHomepageBanner(); //get links string homepageLinks= GetHomepageLinks(); string startHTML = networkTestMessage + homepageBanner + " \

IDF Options:

\
\ \ \ \
\
    \
  1. Click the \"Generate IDF Files\" button to save IDF files to your computer. Import the IDF files into a Mechanical CAD to view components in \"block\" form

  2. \
  3. Click the \"Build 3D PCB\" button to utilize IDF-to-3D to build a 3D PCB of your active .brd file. The 3D PCB will contain accurate 3D component geometry. STEP files of the 3D PCBs can be downloaded after purchasing a subscription to IDF-to-3D

  4. \
  5. Click the \"Login to IDF-to-3D\" button to continue working on an existing IDF-to-3D session.
  6. \ <\ol> \
    \
    " + homepageLinks + "
\ \
"; //set initial text view content TextMessageDialog = GetHtmlText(startHTML); //show dialog to user int mainDlgResult = dlgDialog( "Eagle IDF Exporter" ) { //main layout dlgVBoxLayout { //set width by creating a spacing hbox to drive width if( IsWindows() ) { //windows dlgHBoxLayout dlgSpacing( 800 );//sets width } else { //mac/linux dlgHBoxLayout dlgSpacing( 825 );//sets width } //end if-else //set height and text view dlgHBoxLayout { dlgTextView( TextMessageDialog ); //text view. holds html if( IsWindows() ) { //windows dlgVBoxLayout dlgSpacing( 475 );//sets height } else { //mac/linux dlgVBoxLayout dlgSpacing( 500 );//sets height } //end if-else } //end box layout //add options for IDF export dlgHBoxLayout { dlgLabel("Layer for wires (Default=20): "); dlgStringEdit( WireLayer ); dlgLabel("Board Thickness (MM): "); dlgStringEdit( BoardThickness ); } //end box layout //buttons to submit and exit dlgHBoxLayout { dlgPushButton( "Generate IDF Files" ) { //submit data to idf site GenerateEmnEmpFiles(); } //end button dlgPushButton( "Build 3D PCB" ) { //submit data to idf site SubmitFormData(); } //end button dlgPushButton( "Login to IDF-to-3D" ) { LaunchBrowser( Login_url ); } //end button dlgPushButton( "Exit" ) dlgReject(); } //end box layout } //end v box layout }; //end mainDlgResult } //end main