summaryrefslogtreecommitdiff
path: root/utils/idftools/idf2vrml.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'utils/idftools/idf2vrml.cpp')
-rw-r--r--utils/idftools/idf2vrml.cpp990
1 files changed, 990 insertions, 0 deletions
diff --git a/utils/idftools/idf2vrml.cpp b/utils/idftools/idf2vrml.cpp
new file mode 100644
index 0000000..df66e4b
--- /dev/null
+++ b/utils/idftools/idf2vrml.cpp
@@ -0,0 +1,990 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 Cirilo Bernardo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/*
+ * This program takes an IDF base name, loads the board outline
+ * and component outine files, and creates a single VRML file.
+ * The VRML file can be used to visually verify the IDF files
+ * before sending them to a mechanical designer. The output scale
+ * is 10:1; this scale was chosen because VRML was originally
+ * intended to describe large virtual worlds and rounding errors
+ * would be more likely if we used a 1:1 scale.
+ */
+
+
+#include <iostream>
+#include <iomanip>
+#include <fstream>
+#include <string>
+#include <sstream>
+#include <cmath>
+#include <cstdio>
+#include <cerrno>
+#include <list>
+#include <utility>
+#include <clocale>
+#include <vector>
+#include <cstdlib>
+#include <cstring>
+#include <algorithm>
+#include <libgen.h>
+#include <unistd.h>
+#include <boost/ptr_container/ptr_map.hpp>
+
+#include <idf_helpers.h>
+#include <idf_common.h>
+#include <idf_parser.h>
+#include <vrml_layer.h>
+
+#ifndef MIN_ANG
+#define MIN_ANG 0.01
+#endif
+
+extern char* optarg;
+extern int optopt;
+
+using namespace std;
+using namespace boost;
+
+#define CLEANUP do { \
+setlocale( LC_ALL, "C" ); \
+} while( 0 );
+
+// define colors
+struct VRML_COLOR
+{
+ double diff[3];
+ double emis[3];
+ double spec[3];
+ double ambi;
+ double tran;
+ double shin;
+};
+
+struct VRML_IDS
+{
+ int colorIndex;
+ std::string objectName;
+ bool used;
+ bool bottom;
+ double dX, dY, dZ, dA;
+
+ VRML_IDS()
+ {
+ colorIndex = 0;
+ used = false;
+ bottom = false;
+ dX = 0.0;
+ dY = 0.0;
+ dZ = 0.0;
+ dA = 0.0;
+ }
+};
+
+#define NCOLORS 7
+VRML_COLOR colors[NCOLORS] =
+{
+ { { 0, 0.82, 0.247 }, { 0, 0, 0 }, { 0, 0.82, 0.247 }, 0.9, 0, 0.1 },
+ { { 1, 0, 0 }, { 1, 0, 0 }, { 1, 0, 0 }, 0.9, 0, 0.1 },
+ { { 0.659, 0, 0.463 }, { 0, 0, 0 }, { 0.659, 0, 0.463 }, 0.9, 0, 0.1 },
+ { { 0.659, 0.294, 0 }, { 0, 0, 0 }, { 0.659, 0.294, 0 }, 0.9, 0, 0.1 },
+ { { 0, 0.918, 0.659 }, { 0, 0, 0 }, { 0, 0.918, 0.659 }, 0.9, 0, 0.1 },
+ { { 0.808, 0.733, 0.071 }, { 0, 0, 0 }, { 0.808, 0.733 , 0.071 }, 0.9, 0, 0.1 },
+ { { 0.102, 1, 0.984 }, { 0, 0, 0 }, { 0.102, 1, 0.984 }, 0.9, 0, 0.1 }
+};
+
+bool WriteHeader( IDF3_BOARD& board, std::ofstream& file );
+bool MakeBoard( IDF3_BOARD& board, std::ofstream& file );
+bool MakeComponents( IDF3_BOARD& board, std::ofstream& file, bool compact );
+bool MakeOtherOutlines( IDF3_BOARD& board, std::ofstream& file );
+bool PopulateVRML( VRML_LAYER& model, const std::list< IDF_OUTLINE* >* items, bool bottom,
+ double scale, double dX = 0.0, double dY = 0.0, double angle = 0.0 );
+bool AddSegment( VRML_LAYER& model, IDF_SEGMENT* seg, int icont, int iseg );
+bool WriteTriangles( std::ofstream& file, VRML_IDS* vID, VRML_LAYER* layer, bool plane,
+ bool top, double top_z, double bottom_z, int precision, bool compact );
+inline void TransformPoint( IDF_SEGMENT& seg, double frac, bool bottom,
+ double dX, double dY, double angle );
+VRML_IDS* GetColor( boost::ptr_map<const std::string, VRML_IDS>& cmap,
+ int& index, const std::string& uid );
+
+
+void PrintUsage( void )
+{
+ cout << "-\nUsage: idf2vrml -f input_file.emn -s scale_factor {-k} {-d} {-z} {-m}\n";
+ cout << "flags:\n";
+ cout << " -k: produce KiCad-friendly VRML output; default is compact VRML\n";
+ cout << " -d: suppress substitution of default outlines\n";
+ cout << " -z: suppress rendering of zero-height outlines\n";
+ cout << " -m: print object mapping to stdout for debugging purposes\n";
+ cout << "example to produce a model for use by KiCad: idf2vrml -f input.emn -s 0.3937008 -k\n\n";
+ return;
+}
+
+bool nozeroheights;
+bool showObjectMapping;
+
+int main( int argc, char **argv )
+{
+ // IDF implicitly requires the C locale
+ setlocale( LC_ALL, "C" );
+
+ // Essential inputs:
+ // 1. IDF file
+ // 2. Output scale: internal IDF units are mm, so 1 = 1mm per VRML unit,
+ // 0.1 = 1cm per VRML unit, 0.01 = 1m per VRML unit,
+ // 1/25.4 = 1in per VRML unit, 1/2.54 = 0.1in per VRML unit (KiCad model)
+ // 3. KiCad-friendly output (do not reuse features via DEF+USE)
+ // Render each component to VRML; if the user wants
+ // a KiCad friendly output then we must avoid DEF+USE;
+ // otherwise we employ DEF+USE to minimize file size
+
+ std::string inputFilename;
+ double scaleFactor = 1.0;
+ bool compact = true;
+ bool nooutlinesubs = false;
+ int ichar;
+
+ nozeroheights = false;
+ showObjectMapping = false;
+
+ while( ( ichar = getopt( argc, argv, ":f:s:kdzm" ) ) != -1 )
+ {
+ switch( ichar )
+ {
+ case 'f':
+ inputFilename = optarg;
+ break;
+
+ case 's':
+ do
+ {
+ errno = 0;
+ char* cp = NULL;
+ scaleFactor = strtod( optarg, &cp );
+
+ if( errno || cp == optarg )
+ {
+ cerr << "* invalid scale factor: '" << optarg << "'\n";
+ return -1;
+ }
+
+ if( scaleFactor < 0.001 || scaleFactor > 10 )
+ {
+ cerr << "* scale factor out of range (" << scaleFactor << "); range is 0.001 to 10.0\n";
+ return -1;
+ }
+
+ } while( 0 );
+ break;
+
+ case 'k':
+ compact = false;
+ break;
+
+ case 'd':
+ nooutlinesubs = true;
+ break;
+
+ case 'z':
+ nozeroheights = true;
+ break;
+
+ case 'm':
+ showObjectMapping = true;
+ break;
+
+ case ':':
+ cerr << "* Missing parameter to option '-" << ((char) optopt) << "'\n";
+ PrintUsage();
+ return -1;
+ break;
+
+ default:
+ cerr << "* Unexpected option: '-";
+
+ if( ichar == '?' )
+ cerr << ((char) optopt) << "'\n";
+ else
+ cerr << ((char) ichar) << "'\n";
+
+ PrintUsage();
+ return -1;
+ break;
+ }
+ }
+
+ if( inputFilename.empty() )
+ {
+ cerr << "* no IDF filename supplied\n";
+ PrintUsage();
+ return -1;
+ }
+
+ IDF3_BOARD pcb( IDF3::CAD_ELEC );
+
+ cout << "** Reading file: " << inputFilename << "\n";
+
+ if( !pcb.ReadFile( FROM_UTF8( inputFilename.c_str() ), nooutlinesubs ) )
+ {
+ cerr << "** Failed to read IDF data:\n";
+ cerr << pcb.GetError() << "\n\n";
+
+ return -1;
+ }
+
+ // set the scale and output precision ( scale 1 == precision 5)
+ pcb.SetUserScale( scaleFactor );
+
+ if( scaleFactor < 0.01 )
+ pcb.SetUserPrecision( 8 );
+ else if( scaleFactor < 0.1 )
+ pcb.SetUserPrecision( 7 );
+ else if( scaleFactor < 1.0 )
+ pcb.SetUserPrecision( 6 );
+ else if( scaleFactor < 10.0 )
+ pcb.SetUserPrecision( 5 );
+ else
+ pcb.SetUserPrecision( 4 );
+
+ // Create the VRML file and write the header
+ char* bnp = (char*) malloc( inputFilename.size() + 1 );
+ strcpy( bnp, inputFilename.c_str() );
+
+ std::string fname = basename( bnp );
+ free( bnp );
+ std::string::iterator itf = fname.end();
+ *(--itf) = 'l';
+ *(--itf) = 'r';
+ *(--itf) = 'w';
+
+ cout << "Writing file: '" << fname << "'\n";
+
+ std::ofstream ofile;
+ ofile.open( fname.c_str(), std::ios_base::out );
+
+ ofile << fixed; // do not use exponents in VRML output
+ WriteHeader( pcb, ofile );
+
+ // STEP 1: Render the PCB alone
+ MakeBoard( pcb, ofile );
+
+ // STEP 2: Render the components
+ MakeComponents( pcb, ofile, compact );
+
+ // STEP 3: Render the OTHER outlines
+ MakeOtherOutlines( pcb, ofile );
+
+ ofile << "]\n}\n";
+ ofile.close();
+
+ // restore the locale
+ setlocale( LC_ALL, "" );
+ return 0;
+}
+
+
+bool WriteHeader( IDF3_BOARD& board, std::ofstream& file )
+{
+ std::string bname = board.GetBoardName();
+
+ if( bname.empty() )
+ {
+ bname = "BoardWithNoName";
+ }
+ else
+ {
+ std::string::iterator ss = bname.begin();
+ std::string::iterator se = bname.end();
+
+ while( ss != se )
+ {
+ if( *ss == '/' || *ss == ' ' || *ss == ':' )
+ *ss = '_';
+
+ ++ss;
+ }
+ }
+
+ file << "#VRML V2.0 utf8\n\n";
+ file << "WorldInfo {\n";
+ file << " title \"" << bname << "\"\n}\n\n";
+ file << "Transform {\n";
+ file << "children [\n";
+
+ return !file.fail();
+}
+
+
+bool MakeBoard( IDF3_BOARD& board, std::ofstream& file )
+{
+ VRML_LAYER vpcb;
+
+ if( board.GetBoardOutlinesSize() < 1 )
+ {
+ ERROR_IDF << "\n";
+ cerr << "* Cannot proceed; no board outline in IDF object\n";
+ return false;
+ }
+
+ double scale = board.GetUserScale();
+
+ // set the arc parameters according to output scale
+ int tI;
+ double tMin, tMax;
+ vpcb.GetArcParams( tI, tMin, tMax );
+ vpcb.SetArcParams( tI, tMin * scale, tMax * scale );
+
+ if( !PopulateVRML( vpcb, board.GetBoardOutline()->GetOutlines(), false, board.GetUserScale() ) )
+ {
+ return false;
+ }
+
+ vpcb.EnsureWinding( 0, false );
+
+ int nvcont = vpcb.GetNContours() - 1;
+
+ while( nvcont > 0 )
+ vpcb.EnsureWinding( nvcont--, true );
+
+ // Add the drill holes
+ const std::list<IDF_DRILL_DATA*>* drills = &board.GetBoardDrills();
+
+ std::list<IDF_DRILL_DATA*>::const_iterator sd = drills->begin();
+ std::list<IDF_DRILL_DATA*>::const_iterator ed = drills->end();
+
+ while( sd != ed )
+ {
+ vpcb.AddCircle( (*sd)->GetDrillXPos() * scale, (*sd)->GetDrillYPos() * scale,
+ (*sd)->GetDrillDia() * scale / 2.0, true );
+ ++sd;
+ }
+
+ std::map< std::string, IDF3_COMPONENT* >*const comp = board.GetComponents();
+ std::map< std::string, IDF3_COMPONENT* >::const_iterator sc = comp->begin();
+ std::map< std::string, IDF3_COMPONENT* >::const_iterator ec = comp->end();
+
+ while( sc != ec )
+ {
+ drills = sc->second->GetDrills();
+ sd = drills->begin();
+ ed = drills->end();
+
+ while( sd != ed )
+ {
+ vpcb.AddCircle( (*sd)->GetDrillXPos() * scale, (*sd)->GetDrillYPos() * scale,
+ (*sd)->GetDrillDia() * scale / 2.0, true );
+ ++sd;
+ }
+
+ ++sc;
+ }
+
+ // tesselate and write out
+ vpcb.Tesselate( NULL );
+
+ double thick = board.GetBoardThickness() / 2.0 * scale;
+
+ VRML_IDS tvid;
+ tvid.colorIndex = 0;
+
+ WriteTriangles( file, &tvid, &vpcb, false, false,
+ thick, -thick, board.GetUserPrecision(), false );
+
+ return true;
+}
+
+bool PopulateVRML( VRML_LAYER& model, const std::list< IDF_OUTLINE* >* items, bool bottom, double scale,
+ double dX, double dY, double angle )
+{
+ // empty outlines are not unusual so we fail quietly
+ if( items->size() < 1 )
+ return false;
+
+ int nvcont = 0;
+ int iseg = 0;
+
+ std::list< IDF_OUTLINE* >::const_iterator scont = items->begin();
+ std::list< IDF_OUTLINE* >::const_iterator econt = items->end();
+ std::list<IDF_SEGMENT*>::iterator sseg;
+ std::list<IDF_SEGMENT*>::iterator eseg;
+
+ IDF_SEGMENT lseg;
+
+ while( scont != econt )
+ {
+ nvcont = model.NewContour();
+
+ if( nvcont < 0 )
+ {
+ ERROR_IDF << "\n";
+ cerr << "* cannot create an outline\n";
+ return false;
+ }
+
+ if( (*scont)->size() < 1 )
+ {
+ ERROR_IDF << "invalid contour: no vertices\n";
+ return false;
+ }
+
+ sseg = (*scont)->begin();
+ eseg = (*scont)->end();
+
+ iseg = 0;
+ while( sseg != eseg )
+ {
+ lseg = **sseg;
+ TransformPoint( lseg, scale, bottom, dX, dY, angle );
+
+ if( !AddSegment( model, &lseg, nvcont, iseg ) )
+ return false;
+
+ ++iseg;
+ ++sseg;
+ }
+
+ ++scont;
+ }
+
+ return true;
+}
+
+
+bool AddSegment( VRML_LAYER& model, IDF_SEGMENT* seg, int icont, int iseg )
+{
+ // note: in all cases we must add all but the last point in the segment
+ // to avoid redundant points
+
+ if( seg->angle != 0.0 )
+ {
+ if( seg->IsCircle() )
+ {
+ if( iseg != 0 )
+ {
+ ERROR_IDF << "adding a circle to an existing vertex list\n";
+ return false;
+ }
+
+ return model.AppendCircle( seg->center.x, seg->center.y, seg->radius, icont );
+ }
+ else
+ {
+ return model.AppendArc( seg->center.x, seg->center.y, seg->radius,
+ seg->offsetAngle, seg->angle, icont );
+ }
+ }
+
+ if( !model.AddVertex( icont, seg->startPoint.x, seg->startPoint.y ) )
+ return false;
+
+ return true;
+}
+
+
+bool WriteTriangles( std::ofstream& file, VRML_IDS* vID, VRML_LAYER* layer, bool plane,
+ bool top, double top_z, double bottom_z, int precision, bool compact )
+{
+ if( vID == NULL || layer == NULL )
+ return false;
+
+ file << "Transform {\n";
+
+ if( compact && !vID->objectName.empty() )
+ {
+ file << "translation " << setprecision( precision ) << vID->dX;
+ file << " " << vID->dY << " ";
+
+ if( vID->bottom )
+ {
+ file << -vID->dZ << "\n";
+
+ double tx, ty;
+
+ // calculate the rotation axis and angle
+ tx = cos( M_PI2 - vID->dA / 2.0 );
+ ty = sin( M_PI2 - vID->dA / 2.0 );
+
+ file << "rotation " << setprecision( precision );
+ file << tx << " " << ty << " 0 ";
+ file << setprecision(5) << M_PI << "\n";
+ }
+ else
+ {
+ file << vID->dZ << "\n";
+ file << "rotation 0 0 1 " << setprecision(5) << vID->dA << "\n";
+ }
+
+ file << "children [\n";
+
+ if( vID->used )
+ {
+ file << "USE " << vID->objectName << "\n";
+ file << "]\n";
+ file << "}\n";
+ return true;
+ }
+
+ file << "DEF " << vID->objectName << " Transform {\n";
+
+ if( !plane && top_z <= bottom_z )
+ {
+ // the height specification is faulty; make the component
+ // a bright red to highlight it
+ vID->colorIndex = 1;
+ // we don't know the scale, but 5 units is huge in most situations
+ top_z = bottom_z + 5.0;
+ }
+
+ }
+
+ VRML_COLOR* color = &colors[vID->colorIndex];
+
+ vID->used = true;
+
+ file << "children [\n";
+ file << "Group {\n";
+ file << "children [\n";
+ file << "Shape {\n";
+ file << "appearance Appearance {\n";
+ file << "material Material {\n";
+
+ // material definition
+ file << "diffuseColor " << setprecision(3) << color->diff[0] << " ";
+ file << color->diff[1] << " " << color->diff[2] << "\n";
+ file << "specularColor " << color->spec[0] << " " << color->spec[1];
+ file << " " << color->spec[2] << "\n";
+ file << "emissiveColor " << color->emis[0] << " " << color->emis[1];
+ file << " " << color->emis[2] << "\n";
+ file << "ambientIntensity " << color->ambi << "\n";
+ file << "transparency " << color->tran << "\n";
+ file << "shininess " << color->shin << "\n";
+
+ file << "}\n";
+ file << "}\n";
+ file << "geometry IndexedFaceSet {\n";
+ file << "solid TRUE\n";
+ file << "coord Coordinate {\n";
+ file << "point [\n";
+
+ // Coordinates (vertices)
+ if( plane )
+ {
+ if( !layer->WriteVertices( top_z, file, precision ) )
+ {
+ cerr << "* errors writing planar vertices to " << vID->objectName << "\n";
+ cerr << "** " << layer->GetError() << "\n";
+ }
+ }
+ else
+ {
+ if( !layer->Write3DVertices( top_z, bottom_z, file, precision ) )
+ {
+ cerr << "* errors writing 3D vertices to " << vID->objectName << "\n";
+ cerr << "** " << layer->GetError() << "\n";
+ }
+ }
+
+ file << "\n";
+
+ file << "]\n";
+ file << "}\n";
+ file << "coordIndex [\n";
+
+ // Indices
+ if( plane )
+ layer->WriteIndices( top, file );
+ else
+ layer->Write3DIndices( file );
+
+ file << "\n";
+ file << "]\n";
+ file << "}\n";
+ file << "}\n";
+ file << "]\n";
+ file << "}\n";
+ file << "]\n";
+ file << "}\n";
+
+ if( compact && !vID->objectName.empty() )
+ {
+ file << "]\n";
+ file << "}\n";
+ }
+
+ return !file.fail();
+}
+
+inline void TransformPoint( IDF_SEGMENT& seg, double frac, bool bottom,
+ double dX, double dY, double angle )
+{
+ dX *= frac;
+ dY *= frac;
+
+ if( bottom )
+ {
+ // mirror points on the Y axis
+ seg.startPoint.x = -seg.startPoint.x;
+ seg.endPoint.x = -seg.endPoint.x;
+ seg.center.x = -seg.center.x;
+ angle = -angle;
+ }
+
+ seg.startPoint.x *= frac;
+ seg.startPoint.y *= frac;
+ seg.endPoint.x *= frac;
+ seg.endPoint.y *= frac;
+ seg.center.x *= frac;
+ seg.center.y *= frac;
+
+ double tsin = 0.0;
+ double tcos = 0.0;
+
+ if( angle > MIN_ANG || angle < -MIN_ANG )
+ {
+ double ta = angle * M_PI / 180.0;
+ double tx, ty;
+
+ tsin = sin( ta );
+ tcos = cos( ta );
+
+ tx = seg.startPoint.x * tcos - seg.startPoint.y * tsin;
+ ty = seg.startPoint.x * tsin + seg.startPoint.y * tcos;
+ seg.startPoint.x = tx;
+ seg.startPoint.y = ty;
+
+ tx = seg.endPoint.x * tcos - seg.endPoint.y * tsin;
+ ty = seg.endPoint.x * tsin + seg.endPoint.y * tcos;
+ seg.endPoint.x = tx;
+ seg.endPoint.y = ty;
+
+ if( seg.angle != 0 )
+ {
+ tx = seg.center.x * tcos - seg.center.y * tsin;
+ ty = seg.center.x * tsin + seg.center.y * tcos;
+ seg.center.x = tx;
+ seg.center.y = ty;
+ }
+ }
+
+ seg.startPoint.x += dX;
+ seg.startPoint.y += dY;
+ seg.endPoint.x += dX;
+ seg.endPoint.y += dY;
+ seg.center.x += dX;
+ seg.center.y += dY;
+
+ if( seg.angle != 0 )
+ {
+ seg.radius *= frac;
+
+ if( bottom )
+ {
+ if( !seg.IsCircle() )
+ {
+ seg.angle = -seg.angle;
+ if( seg.offsetAngle > 0.0 )
+ seg.offsetAngle = 180 - seg.offsetAngle;
+ else
+ seg.offsetAngle = -seg.offsetAngle - 180;
+ }
+ }
+
+ if( angle > MIN_ANG || angle < -MIN_ANG )
+ seg.offsetAngle += angle;
+ }
+
+ return;
+}
+
+bool MakeComponents( IDF3_BOARD& board, std::ofstream& file, bool compact )
+{
+ int cidx = 2; // color index; start at 2 since 0,1 are special (board, NOGEOM_NOPART)
+
+ VRML_LAYER vpcb;
+
+ double scale = board.GetUserScale();
+ double thick = board.GetBoardThickness() / 2.0;
+
+ // set the arc parameters according to output scale
+ int tI;
+ double tMin, tMax;
+ vpcb.GetArcParams( tI, tMin, tMax );
+ vpcb.SetArcParams( tI, tMin * scale, tMax * scale );
+
+ // Add the component outlines
+ const std::map< std::string, IDF3_COMPONENT* >*const comp = board.GetComponents();
+ std::map< std::string, IDF3_COMPONENT* >::const_iterator sc = comp->begin();
+ std::map< std::string, IDF3_COMPONENT* >::const_iterator ec = comp->end();
+
+ std::list< IDF3_COMP_OUTLINE_DATA* >::const_iterator so;
+ std::list< IDF3_COMP_OUTLINE_DATA* >::const_iterator eo;
+
+ double vX, vY, vA;
+ double tX, tY, tZ, tA;
+ double top, bot;
+ bool bottom;
+ IDF3::IDF_LAYER lyr;
+
+ boost::ptr_map< const std::string, VRML_IDS> cmap; // map colors by outline UID
+ VRML_IDS* vcp;
+ IDF3_COMP_OUTLINE* pout;
+
+ while( sc != ec )
+ {
+ sc->second->GetPosition( vX, vY, vA, lyr );
+
+ if( lyr == IDF3::LYR_BOTTOM )
+ bottom = true;
+ else
+ bottom = false;
+
+ so = sc->second->GetOutlinesData()->begin();
+ eo = sc->second->GetOutlinesData()->end();
+
+ while( so != eo )
+ {
+ if( (*so)->GetOutline()->GetThickness() < 0.00000001 && nozeroheights )
+ {
+ vpcb.Clear();
+ ++so;
+ continue;
+ }
+
+ (*so)->GetOffsets( tX, tY, tZ, tA );
+ tX += vX;
+ tY += vY;
+ tA += vA;
+
+ if( ( pout = (IDF3_COMP_OUTLINE*)((*so)->GetOutline()) ) )
+ {
+ vcp = GetColor( cmap, cidx, pout->GetUID() );
+ }
+ else
+ {
+ vpcb.Clear();
+ ++so;
+ continue;
+ }
+
+ if( !compact )
+ {
+ if( !PopulateVRML( vpcb, (*so)->GetOutline()->GetOutlines(), bottom,
+ board.GetUserScale(), tX, tY, tA ) )
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if( !vcp->used && !PopulateVRML( vpcb, (*so)->GetOutline()->GetOutlines(), false,
+ board.GetUserScale() ) )
+ {
+ return false;
+ }
+
+ vcp->dX = tX * scale;
+ vcp->dY = tY * scale;
+ vcp->dZ = tZ * scale;
+ vcp->dA = tA * M_PI / 180.0;
+ }
+
+ if( !compact || !vcp->used )
+ {
+ vpcb.EnsureWinding( 0, false );
+
+ int nvcont = vpcb.GetNContours() - 1;
+
+ while( nvcont > 0 )
+ vpcb.EnsureWinding( nvcont--, true );
+
+ vpcb.Tesselate( NULL );
+ }
+
+ if( !compact )
+ {
+ if( bottom )
+ {
+ top = -thick - tZ;
+ bot = (top - (*so)->GetOutline()->GetThickness() ) * scale;
+ top *= scale;
+ }
+ else
+ {
+ bot = thick + tZ;
+ top = (bot + (*so)->GetOutline()->GetThickness() ) * scale;
+ bot *= scale;
+ }
+ }
+ else
+ {
+ bot = thick;
+ top = (bot + (*so)->GetOutline()->GetThickness() ) * scale;
+ bot *= scale;
+ }
+
+ vcp = GetColor( cmap, cidx, ((IDF3_COMP_OUTLINE*)((*so)->GetOutline()))->GetUID() );
+ vcp->bottom = bottom;
+
+ // note: this can happen because IDF allows some negative heights/thicknesses
+ if( bot > top )
+ std::swap( bot, top );
+
+ WriteTriangles( file, vcp, &vpcb, false,
+ false, top, bot, board.GetUserPrecision(), compact );
+
+ vpcb.Clear();
+ ++so;
+ }
+
+ ++sc;
+ }
+
+ return true;
+}
+
+
+VRML_IDS* GetColor( boost::ptr_map<const std::string, VRML_IDS>& cmap, int& index, const std::string& uid )
+{
+ static int refnum = 0;
+
+ if( index < 2 )
+ index = 2; // 0 and 1 are special (BOARD, UID=NOGEOM_NOPART)
+
+ boost::ptr_map<const std::string, VRML_IDS>::iterator cit = cmap.find( uid );
+
+ if( cit == cmap.end() )
+ {
+ VRML_IDS* id = new VRML_IDS;
+
+ if( !uid.compare( "NOGEOM_NOPART" ) )
+ id->colorIndex = 1;
+ else
+ id->colorIndex = index++;
+
+ std::ostringstream ostr;
+ ostr << "OBJECTn" << refnum++;
+ id->objectName = ostr.str();
+
+ if( showObjectMapping )
+ cout << "* " << ostr.str() << " = '" << uid << "'\n";
+
+ cmap.insert( uid, id );
+
+ if( index >= NCOLORS )
+ index = 2;
+
+ return id;
+ }
+
+ return cit->second;
+}
+
+
+bool MakeOtherOutlines( IDF3_BOARD& board, std::ofstream& file )
+{
+ int cidx = 2; // color index; start at 2 since 0,1 are special (board, NOGEOM_NOPART)
+
+ VRML_LAYER vpcb;
+
+ double scale = board.GetUserScale();
+ double thick = board.GetBoardThickness() / 2.0;
+
+ // set the arc parameters according to output scale
+ int tI;
+ double tMin, tMax;
+ vpcb.GetArcParams( tI, tMin, tMax );
+ vpcb.SetArcParams( tI, tMin * scale, tMax * scale );
+
+ // Add the component outlines
+ const std::map< std::string, OTHER_OUTLINE* >*const comp = board.GetOtherOutlines();
+ std::map< std::string, OTHER_OUTLINE* >::const_iterator sc = comp->begin();
+ std::map< std::string, OTHER_OUTLINE* >::const_iterator ec = comp->end();
+
+ double top, bot;
+ bool bottom;
+ int nvcont;
+
+ boost::ptr_map< const std::string, VRML_IDS> cmap; // map colors by outline UID
+ VRML_IDS* vcp;
+ OTHER_OUTLINE* pout;
+
+ while( sc != ec )
+ {
+ pout = sc->second;
+
+ if( pout->GetThickness() < 0.00000001 && nozeroheights )
+ {
+ vpcb.Clear();
+ ++sc;
+ continue;
+ }
+
+ vcp = GetColor( cmap, cidx, pout->GetOutlineIdentifier() );
+
+ if( !PopulateVRML( vpcb, pout->GetOutlines(), false,
+ board.GetUserScale(), 0, 0, 0 ) )
+ {
+ return false;
+ }
+
+ vpcb.EnsureWinding( 0, false );
+
+ nvcont = vpcb.GetNContours() - 1;
+
+ while( nvcont > 0 )
+ vpcb.EnsureWinding( nvcont--, true );
+
+ vpcb.Tesselate( NULL );
+
+ if( pout->GetSide() == IDF3::LYR_BOTTOM )
+ bottom = true;
+ else
+ bottom = false;
+
+ if( bottom )
+ {
+ top = -thick;
+ bot = ( top - pout->GetThickness() ) * scale;
+ top *= scale;
+ }
+ else
+ {
+ bot = thick;
+ top = (bot + pout->GetThickness() ) * scale;
+ bot *= scale;
+ }
+
+ // note: this can happen because IDF allows some negative heights/thicknesses
+ if( bot > top )
+ std::swap( bot, top );
+
+ vcp->bottom = bottom;
+ WriteTriangles( file, vcp, &vpcb, false,
+ false, top, bot, board.GetUserPrecision(), false );
+
+ vpcb.Clear();
+ ++sc;
+ }
+
+ return true;
+}