#usage "Generate IDF files and interface to SimplifiedSolutionsinc.com to generate 3D model.

\n" "Generates the EMN and EMP IDF files from the board.
" "IDF files are then submitted to SimplifiedSolutionsinc.com
" "and a 3D assembly model is generated. On the SimplifiedSolutionsinc.com
" "site mappings are executed from the board elements to fully rendered
" "3D models. The site then responses with a thumbnail and several free and
" "purchase options.
" "Version: 1.0 - Initial Release - 4-30-12
" "Version: 1.1 - Includes banner and dynamic library logic - 5-5-12
" "Version: 1.2 - Includes fix for board outline loop order and improved logic for component outlines - 5-9-12
" "Version: 1.3 - Includes fix for zero value max/min x/y values on component outlines - 5-10-12
" "Version: 1.4 - Includes fix for missing holes - 5-12-12
" "Version: 1.5 - Modified banner url for dynamic version based message. Fix in board outline for lines linked end to end vs end to start. Updated text in UI. - 5-13-12
" "Version: 1.6 - Fix for component outlines where max/min values are equal resulting in failures in Proe - 5-14-12
" "Version: 1.7 - Enabled purchase links to pcb-pool site. Include UI customizations for Windows and Mac. - 5-14-12
" "Version: 1.8 - Updated links shown after build completed. - 6-21-12
" "Version: 1.9 - UI updates based on feedback. Added in third link to enable user to continue working on board. - 7-1-12
" "Version: 2.0 - UI fixes for Mac/Linux - 7-2-12
" "Version: 2.1 - Updated UI on page. - 7-8-12
" "Version: 2.2 - Improved board outline algorithm. - 7-10-12
" "Version: 2.3 - Changed port number to 8080. 9-17-12
" "Version: 2.3b - Fix for space in generation of .placement section values. - 7-30-12
" "Version: 3.0 - Improvements to handling of outline defects and swap package/value in writing to IDF files. Fixed issue with unique alname/ecad outlines in emp. Includes logic to handle boards with circle outline. - 9-13-12
" "

" "Author: support@simplifiedsolutionsinc.com" #require 5.1000 string version = "3.0"; //ulp version //vars used for the text dialog string textMessageDialog; //used to store text shown to user string textMessageHeader = "

Welcome to the EAGLE-to-3D Tool

"; string textDisclaimer = "The 3D component libraries supplied by Simplified Solutions have been compiled with great care. However, with the large number of 3D models available and the occasional package changes made by suppliers, an occasional discrepancy is unavoidable. Please note, therefore, that Simplified Solutions and Beta LAYOUT takes no responsibility for the complete accuracy of information included in the 3D library components. Please verify all 3D PCB STEP file geometry prior to using the data in product designs. By choosing the \"Generate 3D Data\" button you are also agreeing to our Terms of Service"; //web request urls string host = "http://www.simplifiedsolutionsinc.com"; string service_port = "8080"; string static_port = "80"; string service_name = "EMN-EMPAdvancedApp/EagleTo3DServlet"; string static_name = "EagleTo3D/"; string service_url = host + ":" + service_port + "/" + service_name; string static_url = host + ":" + static_port + "/" + static_name; string libraries[]; //used to hold libraries from web string packageOutlines[]; //used to hold outlines of packages int packageCounter = 0; //keeps track of how many entries in packages outline array //html used in the UI string tagImgActive = ""; string tagBanner = ""; //Initial value. Once its retrieved first time it will have actual data. //debug flags int debug = 0; //general debug flag int debugIDFFile = 1; //debug to write out local idf files int debugIDFHtml = 0; //debug to write out local idf files in html format //----------------------------------------------------------------------------- // dictionary //----------------------------------------------------------------------------- string Dictionary[] = { "en\v" "de\v", "English message 1 here. \v" "German message 1 here. \v", "English message 2 here. \v" "German message 3 here. \v" }; string DlgLang = language(); if (DlgLang != "de") DlgLang = "en"; //force to en if not german int LangIdx = strstr(Dictionary[0], DlgLang) / 3; //language index ///// Translate, based on dictionary string tr(string s) { string t = lookup(Dictionary, s, LangIdx, '\v'); return t ? t : s; } //----------------------------------------------------------------------------- // 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 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(){ int unitsInt; string thickness; /* removed to force board to mm board(B) { unitsInt = B.grid.unit; } switch( unitsInt ){ case 0: thickness = "1.6"; break; case 1: thickness = "1.6"; break; case 2: thickness = "1.6"; break; case 3: thickness = "1.6"; break; } //end switch */ thickness = "1.6"; return thickness; } //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(); } 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 //count number of times a char shows up in a string. int countOccurancesOfChar( string strToScan, char toMatch ){ int count = 0; for (int i = 0; strToScan[i]; ++i) { if( strToScan[i] == toMatch ){ count++; } //end if } //end for return count; } //end sub // 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 // 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 // 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; //detect if windows or other if( isWindows() ){ os = "Windows"; } else{ os = "Mac/Linux"; } string htmlHeader = "
" + textMessageHeader + "
\ \ \ "; htmlFooter += ""; } else{ htmlFooter += "














"; } //end if-else htmlFooter += "
"; string htmlFooter = ""; //build window based on OS if( isWindows() ){ htmlFooter += "





















"; htmlFooter += "
"; htmlFooter += "
EAGLE-to-3D is a product of Simplified Solutions Inc. and Beta LAYOUT( ULP Version: " + version + "\t, OS: " + os + " )

" + textDisclaimer + "

"; return htmlHeader + body + htmlFooter; } //end sub //general function to do url get string getWebFile( string url_path ){ return ""; } //this sub is used to get the Html banner to show on the UI. It should only need to be called once on first call void loadBanner(){ string banner; //do netget if( netget( banner, static_url + "eagle_banner.php?ulp_version=" + version + "&lang=" + language() ) ){ if( debug ){ dlgMessageBox( banner ); } //end if tagBanner = banner; } else{ tagBanner = "Not retrieved"; } //end if-else } //end sub void loadTerms(){ string term; //do netget if( netget( term, static_url + "terms.html" ) ){ if( debug ){ dlgMessageBox( term ); } //end if //tagBanner = banner; } else{ //tagBanner = "Not retrieved"; } //end if-else } //end sub void loadLibraries(){ //string libraries[]; string libResponse; int libraryCount = 0; //string defaultLib[] = { "RCL", "RESISTOR" }; //do netget if( netget( libResponse, static_url + "eagle_libraries.txt" ) ){ if( debug ){ dlgMessageBox( libResponse ); } //end if //load library string in to libraries array libraryCount = strsplit( libraries, libResponse, ':'); } //end if //in case some issue load RCL and RESISTOR as default if( libraryCount == 0 ){ libraries[0] = "RCL"; libraries[1] = "RESISTOR"; } //end if } //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 ); } } //----------------------------------------------------------------------------- // 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 //used to detect if outline should be closed by checking if point is within tolerance/radius of first point int closeOutline( real bx1, real by1, real firstX, real firstY, real radius ){ string temp; //debug if( debug ){ real xCalc = pow( ( bx1 - firstX ), 2 ); real yCalc = pow( ( by1 - firstY ), 2 ); real radCalc = pow( radius, 2 ); sprintf( temp, "%f + %f < %f", xCalc, yCalc, radCalc ); dlgMessageBox( temp ); } //(x-center_x)^2 + (y - center_y)^2 < radius^2 if( ( pow( ( bx1 - firstX ), 2 ) + pow( ( by1 - firstY ), 2 ) ) < pow( radius, 2 ) ){ return 1; } else{ return 0; } } //end sub //looks up package outline and generates text for .electical section entry string getComponentOutline( string footprint, string altname, string packageName, string units ){ //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; //loop thru all libraries to find packagename board(B) { //go into library B.libraries(LBR) { //loop thru packages in library LBR.packages(P){ if( P.name == packageName ){ //set found package flag foundPackage = 1; //used to capture first loop of data int loopCounter = 0; //loop thru wires and get max/min x/y P.wires(W){ //get first data point if( loopCounter == 0 ){ maxX = getDimInBoardUnits(W.x1); minX = getDimInBoardUnits(W.x1); maxY = getDimInBoardUnits(W.y1); minY = getDimInBoardUnits(W.y1); } if (W.arc) { //skip arcs for now } else { //max/min checks if( getDimInBoardUnits(W.x1) <= minX ){ minX = getDimInBoardUnits(W.x1); } if( getDimInBoardUnits(W.x1) >= maxX ){ maxX = getDimInBoardUnits(W.x1); } if( getDimInBoardUnits(W.y1) <= minY ){ minY = getDimInBoardUnits(W.y1); } if( getDimInBoardUnits(W.y1) >= maxY ){ maxY = getDimInBoardUnits(W.y1); } } //end if-else loopCounter++; } //end wires //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 packageOutlines[ packageCounter ] = temp; packageCounter++; } //end if for lookup check //generate outline data string outline = footprint + " " + altname + " " + units; //set default height based on units if( units == "MM" ){ outline += " 1.00\n"; } else if( units == "THOU" ){ outline += " 39.37\n"; } else{ //treat as MM outline += " 1.00\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"; } 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"; } 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 //gets the dummy component outlines for emp file based on units. string getComponentOutlineDummy( string footprint, string altname, string units ){ string outline = footprint + " " + altname + " " + units; if( units == "MM" ){ outline += " 1.00\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" + "0 1.00 -1.00 0.0\n"; } else if( units == "THOU" ){ outline += " 39.37\n" + "0 39.37 -39.37 0.0\n" + "0 -39.37 -39.37 0.0\n" + "0 -39.37 39.37 0.0\n" + "0 39.37 39.37 0.0\n" + "0 39.37 -39.37 0.0\n"; } else{ //treat as MM outline += " 1.00\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" + "0 1.00 -1.00 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 to 3D Version " + 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 libraryLookup; string components[]; string component; //loop thru elements on board board( BRD ){ int i = 0; BRD.elements( E ){ /* //get footpring footprint = E.value; if( footprint == "" ){ footprint = E.name + "_" + E.package.name ; } */ //check if library in lookup libraryLookup = lookup( libraries, strupr(E.package.library), 0 ); //debug if( debug ){ dlgMessageBox( libraryLookup ); } //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"; } //check if element has already been written out //component = lookup( components, footprint, 0 ); - fix for emp missing values 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" + //getComponentOutlineDummy( footprint, altname, "MM" ) + getComponentOutline( footprint, altname, E.package.name, getBoardUnits() ) + ".END_ELECTRICAL\n"; //add component to components list //components[ i ] = footprint; - fix for emp missing values components[ i ] = footprint + "~" + altname; i++; } //end if } //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 to 3D 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 swapPoints( string outlinePoints[], int toIndex, int fromIndex ){ //swapPoints( outlinePoints, (i+1), j ); string toPoints = outlinePoints[ toIndex ]; string fromPoints = outlinePoints[ fromIndex ]; //move toIndex to from position outlinePoints[ fromIndex ] = toPoints; outlinePoints[ toIndex ] = fromPoints; return outlinePoints; } */ 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 countWires(){ int wiresCounter = 0; board( BRD ){ //loop thru wires BRD.wires(W){ if (W.layer == 20){ if ( W.arc ){ wiresCounter++; } else{ wiresCounter++; } //end if-else } //end if for layer 20 } //end loop thru wires } //end loop thru board return wiresCounter; } int countWireHoles(){ int holeCount = 0; board( BRD ){ /* BRD.holes(H){ holeCount++; } //end holes */ BRD.circles(C){ if( C.layer == 20 ){ holeCount++; } } } //end board loop return holeCount; } //end func real tolerance = getTolerance(); 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. 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 == 20){ //get wire start and end points 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 /* Step 3 - Verify wires found */ //make sure wires were found if( wireCount == 0 ){ if( countWireHoles() > 0 ){ board( B ){ B.circles( C ){ 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 ){ incx = (cos( deg2rad * (angle1 + currentAngle) )*radius ) + centerx; incy = (sin( deg2rad * (angle1 + currentAngle) )*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 //close loop /* sprintf( temp ,"%f\t%f\t%f\t%f\tn\n", bx1, by1, (centerx + radius), centery ); outlinePoints[ wireCount ] = temp; wireCount++; */ } //end circle loop } //end board loop //exit(0); } else{ dlgMessageBox( "No wires were found on layer 20.\nUnable to detect board outline. Exiting." ); exit(0); } } //end if 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 /* //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." ); 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"; //exit(0); 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 libraryLookup; //used to check if library is in array placementSection = ".PLACEMENT\n"; //loop thru elements on board board( BRD ){ int i = 0; BRD.elements( E ){ //get footprint and altname /* 1. If an item is in the rcl or the resistor library, I would like the Package Name to be written out in the first two fields of the emn and emp files regardless of whether or not the ?value? field is empty or populated. 2. if value populated then use it for footprint 3. concatenate name (ref id) and package for footpring */ //check if package library is in list //if( strupr(E.package.library) == "RCL" || strupr(E.package.library) == "RESISTOR" ){ //look up package library in libraries array //check if element has already been written out libraryLookup = lookup( libraries, strupr(E.package.library), 0 ); //debug if( debug ){ dlgMessageBox( libraryLookup ); } //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 //make sure footprint and altname dont have spaces if( strstr( footprint, " " ) >= 0 ){ footprint = replaceSpaceWithUnderscore( footprint ); } if( strstr( altname, " " ) >= 0 ){ altname = replaceSpaceWithUnderscore( altname ); } //parse out ref id earlier and check for spaces ref_id = E.name; if( strstr( ref_id, " " ) >= 0 ){ ref_id = replaceSpaceWithUnderscore( ref_id ); } //get side value based on mirror value side = sideMap[ E.mirror ]; 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 ); //sprintf( temp, "%s %s %s\nPLACED\n", footprint, E.package.name, E.name ); placementSection += temp; //add component to components list //components[ i ] = footprint; - fix for emp missing values components[ i ] = footprint + "~" + altname; 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 /* void generateEmnEmpFiles(){ //build emn file string emn = getEmnHeader() + getEmnBoardOutline() + getEmnDrilledHoles() + getEmnPlacements(); //build emp file string emp = getEmpHeader() + getEmpElectrical() + getEmpMechanical(); //write out for now output( "C:/Users/Marco/Documents/Eagle Programs/emn.emn", "w" ){ printf( emn ); } output( "C:/Users/Marco/Documents/Eagle Programs/emp.emp", "w" ){ printf( 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( debug ){ dlgMessageBox( path_ulp[0] ); } //get board name string boardName; string boardDir; board( BRD ){ boardName = filename( BRD.name ); boardDir = filedir( BRD.name ); } //end board //demo stuff /* string demoPath = "C:/Users/Marco/Documents/Eagle Programs/"; string emnData = parseFileToHtmlForm( path_ulp[0] + "/" + "eagleto3ddemo.emn" ) + "\n"; string empData = parseFileToHtmlForm( path_ulp[0] + "/" + "eagleto3ddemo.emp" ) + "\n"; */ //get emn and emp strings string emnData = generateEmn() + "\n"; string empData = generateEmp() + "\n"; //debug if( 0 ){ //boardDir = "/Users/marcbattistello/"; //hardcode since write issues to program files boardDir = "C:/Users/Marco/Desktop/"; //show path to user dlgMessageBox( "Writing IDF Files: " + boardDir + filesetext( boardName, ".emn") ); output( boardDir + filesetext( boardName, ".emn"), "wt" ){ printf( emnData ); } output( boardDir + filesetext( boardName, ".emp"), "wt" ){ printf( empData ); } } //end if //exit(0); //convert to html format emnData = parseStringToHtmlForm( emnData ); empData = parseStringToHtmlForm( empData ); //debug if( debugIDFHtml ){ output( boardDir + filesetext( boardName, ".html_emn"), "w" ){ printf( emnData ); } output( boardDir + filesetext( boardName, ".html_emp"), "w" ){ printf( empData ); } } //end if //data for url post string postResponse; string postData; int requestSubmitted = 0; //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 downloadPurchaseUrl; string stlPurchaseUrl; string complete3DDesignUrl; int jobCompleted = 0; //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 sprintf( postData,"emn=%s\nemp=%s\nboardname=%s", emnData, empData, boardName ); } else{ //load data to post parameters sprintf( postData,"id=%s", jobId ); } //end if-else if( netpost( postResponse, service_url, postData ) >= 0 ) { if( debug ){ dlgMessageBox( postResponse ); } //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; //get link from xml response downloadPurchaseUrl = xmltext( postResponse, "response/betaDownloadKey" ); stlPurchaseUrl = xmltext( postResponse, "response/betaStlKey" ); complete3DDesignUrl = xmltext( postResponse, "response/betaComplete3DDesignKey" ); if( debug ){ dlgMessageBox( postResponse ); } if( debug ){ dlgMessageBox( stlPurchaseUrl ); } //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 ); if( debug ){ dlgMessageBox( purchaseStlLink ); } /* textMessageDialog_old = getHtmlText( "

Sending 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.

\     " + pdf3dLink + " to open a 3D PDF

\     " + purchaseDownloadLink + " to purchase the 3D-STEP file

\     " + purchaseStlLink + " to purchase a physical 3D model (Rapid Prototype)

\     " + 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 our 3D STEP Model integration tool
  • \
\
" ); */ 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
  • \
  • Get a free FITS-OR-NOT 3D Printer Rapid Prototype with every PCB-POOL(R) order
  • \
\     If satisfied with your 3D image:

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

\     " + purchaseDownloadLink + " to purchase the 3D-STEP file

\     " + purchaseStlLink + " to get a free FITS-OR-NOT Prototype with your PCB-POOL(R) order\
" ); //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() + "
" ); dlgRedisplay(); break; } //this is neded 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 } else if( i % 3 == 0 ){ //try to slow down the loop } else{ //do nothing. }//end if-else modulus test } //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 //verifies netget works and gets a valid response //0=network conection failed //1=network connection was successful int verifyNetwork(){ string temp; //make network request if( netget( temp, service_url ) ){ return 1; } else{ return 0; } //end if-else } //end sub //make network connection to service url and make sure its working int verifyIDFService(){ string temp; string serviceStatus; //make network request if( netget( temp, service_url ) ){ serviceStatus = xmltext( temp, "response/status" ); //debug line if( debug ){ dlgMessageBox( serviceStatus ); } if( serviceStatus == "available" ){ return 1; } else{ return 0; } //end if-else on status } else{ return 0; } //end if-else for netget call } //end sub //----------------------------------------------------------------------------- // main section //----------------------------------------------------------------------------- void main(){ //ulp path. //dlgMessageBox( argv[0] ); //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); } //validate connectivity if( !verifyNetwork() ){ dlgMessageBox( "ERROR: Network connection failed.

\nPlease verify your network connection.

"); exit(-1); } //validate system status if( !verifyIDFService() ){ dlgMessageBox( "ERROR: The EAGLE to 3D webservice is unavailable.

\nUnable to connect to EAGLE to 3D web service.\nPlease try again later.

"); //exit(-1); } //on open load banner and libraries loadBanner(); loadLibraries(); //analyze board and library to identify and issues that would prevent the successful generation of a emn and emp file //set initial text view content textMessageDialog = getHtmlText( tagBanner + " \ \ " ); //show dialog to user int mainDlgResult = dlgDialog( "EAGLE to 3D" ) { //main layout dlgVBoxLayout { //set width if( isWindows() ){ //windows dlgHBoxLayout dlgSpacing( 800 ); //sets width } else{ //mac/linux dlgHBoxLayout dlgSpacing( 850 ); //sets width } //end if-else //set height and text view dlgHBoxLayout { dlgTextView( textMessageDialog ); //text view. holds html if( isWindows() ){ //windows dlgVBoxLayout dlgSpacing( 525 ); //sets height } else{ //mac/linux dlgVBoxLayout dlgSpacing( 600 ); //sets height } //end if-else } //end box layout //buttons to submit and exit dlgHBoxLayout { dlgPushButton( "Generate 3D Data" ) { //build emn and emp strings //generateEmnEmpFiles(); //submit data to idf site submitFormData(); } //end button dlgPushButton( "Exit" ) dlgReject(); } //end box layout } //end v box layout }; //end mainDlgResult } //end main
Sample 3D Output - Click the \"Generate 3D Data\" button to send your data to our IDF-to-3D server.
\
We offer:
    \
  • One button click automated conversion from EAGLE to a full 3D model
  • \
  • Automated component mapping to thousands of EAGLE library components
  • \
  • Electronic Downloads (STEP Format) and rapid prototypes of your populated 3D Boards
  • \
\
\