summaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
Diffstat (limited to 'utils')
-rw-r--r--utils/CMakeLists.txt2
-rw-r--r--utils/idftools/CMakeLists.txt36
-rw-r--r--utils/idftools/dxf2idf.cpp460
-rw-r--r--utils/idftools/dxf2idf.h102
-rw-r--r--utils/idftools/dxf2idfmain.cpp190
-rw-r--r--utils/idftools/idf2vrml.cpp990
-rw-r--r--utils/idftools/idf_common.cpp1387
-rw-r--r--utils/idftools/idf_common.h711
-rw-r--r--utils/idftools/idf_cylinder.cpp681
-rw-r--r--utils/idftools/idf_examples/Arduino_MEGA_2560-Rev3.emn149
-rw-r--r--utils/idftools/idf_examples/Arduino_MEGA_2560-Rev3.emp4
-rw-r--r--utils/idftools/idf_examples/idf_example.emn269
-rw-r--r--utils/idftools/idf_examples/idf_example.emp69
-rw-r--r--utils/idftools/idf_examples/test_donut.emn45
-rw-r--r--utils/idftools/idf_examples/test_donut.emp5
-rw-r--r--utils/idftools/idf_examples/test_idf2.emn71
-rw-r--r--utils/idftools/idf_examples/test_idf2.emp290
-rw-r--r--utils/idftools/idf_helpers.cpp323
-rw-r--r--utils/idftools/idf_helpers.h175
-rw-r--r--utils/idftools/idf_outlines.cpp3614
-rw-r--r--utils/idftools/idf_outlines.h771
-rw-r--r--utils/idftools/idf_parser.cpp4288
-rw-r--r--utils/idftools/idf_parser.h721
-rw-r--r--utils/idftools/idf_rect.cpp433
-rw-r--r--utils/idftools/vrml_layer.cpp1788
-rw-r--r--utils/idftools/vrml_layer.h461
26 files changed, 18035 insertions, 0 deletions
diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt
new file mode 100644
index 0000000..04489b3
--- /dev/null
+++ b/utils/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_subdirectory( idftools )
+
diff --git a/utils/idftools/CMakeLists.txt b/utils/idftools/CMakeLists.txt
new file mode 100644
index 0000000..4334403
--- /dev/null
+++ b/utils/idftools/CMakeLists.txt
@@ -0,0 +1,36 @@
+include_directories(
+ "${CMAKE_SOURCE_DIR}/lib_dxf"
+ "${CMAKE_SOURCE_DIR}/utils/idftools"
+ ${OPENGL_INCLUDE_DIR}
+ ${Boost_INCLUDE_DIR}
+ )
+
+link_directories(
+ "${CMAKE_BINARY_DIR}/lib_dxf"
+ )
+
+add_library( idf3 STATIC
+ idf_helpers.cpp idf_common.cpp idf_outlines.cpp
+ idf_parser.cpp vrml_layer.cpp )
+
+add_executable( idfcyl idf_cylinder.cpp )
+add_executable( idfrect idf_rect.cpp )
+add_executable( dxf2idf dxf2idfmain.cpp dxf2idf.cpp )
+add_executable( idf2vrml idf2vrml.cpp )
+
+add_dependencies( idf2vrml boost )
+
+target_link_libraries( dxf2idf lib_dxf idf3 ${wxWidgets_LIBRARIES} )
+
+target_link_libraries( idf2vrml idf3 ${OPENGL_LIBRARIES} ${wxWidgets_LIBRARIES} )
+
+if( APPLE )
+ # puts binaries into the *.app bundle while linking
+ set_target_properties( idfcyl idfrect dxf2idf idf2vrml PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY ${OSX_BUNDLE_BUILD_BIN_DIR}
+ )
+else()
+ install( TARGETS idfcyl idfrect dxf2idf idf2vrml
+ DESTINATION ${KICAD_BIN}
+ COMPONENT binary )
+endif()
diff --git a/utils/idftools/dxf2idf.cpp b/utils/idftools/dxf2idf.cpp
new file mode 100644
index 0000000..7147cda
--- /dev/null
+++ b/utils/idftools/dxf2idf.cpp
@@ -0,0 +1,460 @@
+/*
+ * 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
+ */
+
+#include <cstdio>
+#include <iostream>
+#include <libdxfrw.h>
+#include <dxf2idf.h>
+
+// differences in angle smaller than MIN_ANG are considered equal
+#define MIN_ANG (0.01)
+
+// min and max bulge bracketing min. arc before transition to line segment
+// and max. arc limit
+// MIN_BULGE = 0.002 ~0.45 degrees
+// MAX_BULGE = 2000 ~89.97 degrees
+#define MIN_BULGE 0.002
+#define MAX_BULGE 2000.0
+
+DXF2IDF::~DXF2IDF()
+{
+ while( !lines.empty() )
+ {
+#ifdef DEBUG_IDF
+ IDF3::printSeg( lines.back() );
+#endif
+ delete lines.back();
+ lines.pop_back();
+ }
+}
+
+
+bool DXF2IDF::ReadDxf( const std::string aFile )
+{
+ dxfRW* reader = new dxfRW( aFile.c_str() );
+
+ if( !reader )
+ return false;
+
+ bool success = reader->read( this, true );
+
+ delete reader;
+ return success;
+}
+
+
+void DXF2IDF::addLine( const DRW_Line& data )
+{
+ IDF_POINT p1, p2;
+
+ p1.x = data.basePoint.x * m_scale;
+ p1.y = data.basePoint.y * m_scale;
+ p2.x = data.secPoint.x * m_scale;
+ p2.y = data.secPoint.y * m_scale;
+
+ insertLine( p1, p2 );
+ return;
+}
+
+
+void DXF2IDF::addCircle( const DRW_Circle& data )
+{
+ IDF_POINT p1, p2;
+
+ p1.x = data.basePoint.x * m_scale;
+ p1.y = data.basePoint.y * m_scale;
+
+ p2.x = p1.x + data.radious * m_scale;
+ p2.y = p1.y;
+
+ IDF_SEGMENT* seg = new IDF_SEGMENT( p1, p2, 360, true );
+
+ if( seg )
+ lines.push_back( seg );
+
+ return;
+}
+
+
+void DXF2IDF::addArc( const DRW_Arc& data )
+{
+ IDF_POINT p1, p2;
+
+ p1.x = data.basePoint.x * m_scale;
+ p1.y = data.basePoint.y * m_scale;
+
+ // note: DXF circles always run CCW
+ double ea = data.endangle;
+
+ while( ea < data.staangle )
+ ea += M_PI;
+
+ p2.x = p1.x + cos( data.staangle ) * data.radious * m_scale;
+ p2.y = p1.y + sin( data.staangle ) * data.radious * m_scale;
+
+ double angle = ( ea - data.staangle ) * 180.0 / M_PI;
+
+ IDF_SEGMENT* seg = new IDF_SEGMENT( p1, p2, angle, true );
+
+ if( seg )
+ lines.push_back( seg );
+
+ return;
+}
+
+
+bool DXF2IDF::WriteOutline( FILE* aFile, bool isInch )
+{
+ if( lines.empty() )
+ {
+ std::cerr << "* DXF2IDF: empty outline\n";
+ return false;
+ }
+
+ // 1. find lowest X value
+ // 2. string an outline together
+ // 3. emit warnings if more than 1 outline
+ IDF_OUTLINE outline;
+
+ IDF3::GetOutline( lines, outline );
+
+ if( outline.empty() )
+ {
+ std::cerr << "* DXF2IDF::WriteOutline(): no valid outline in file\n";
+ return false;
+ }
+
+ if( !lines.empty() )
+ {
+ std::cerr << "* DXF2IDF::WriteOutline(): WARNING: more than 1 outline in file\n";
+ std::cerr << "* Only the first outline will be used\n";
+ }
+
+ char loopDir = '1';
+
+ if( outline.IsCCW() )
+ loopDir = '0';
+
+ std::list<IDF_SEGMENT*>::iterator bo;
+ std::list<IDF_SEGMENT*>::iterator eo;
+
+ if( outline.size() == 1 )
+ {
+ if( !outline.front()->IsCircle() )
+ {
+ std::cerr << "* DXF2IDF::WriteOutline(): bad outline\n";
+ return false;
+ }
+
+ // NOTE: a circle always has an angle of 360, never -360,
+ // otherwise SolidWorks chokes on the file.
+ if( isInch )
+ {
+ fprintf( aFile, "%c %d %d 0\n", loopDir,
+ (int) (1000 * outline.front()->startPoint.x),
+ (int) (1000 * outline.front()->startPoint.y) );
+ fprintf( aFile, "%c %d %d 360\n", loopDir,
+ (int) (1000 * outline.front()->endPoint.x),
+ (int) (1000 * outline.front()->endPoint.y) );
+ }
+ else
+ {
+ fprintf( aFile, "%c %.3f %.3f 0\n", loopDir,
+ outline.front()->startPoint.x, outline.front()->startPoint.y );
+ fprintf( aFile, "%c %.3f %.3f 360\n", loopDir,
+ outline.front()->endPoint.x, outline.front()->endPoint.y );
+ }
+
+ return true;
+ }
+
+ // ensure that the very last point is the same as the very first point
+ outline.back()-> endPoint = outline.front()->startPoint;
+
+ bo = outline.begin();
+ eo = outline.end();
+
+ // for the first item we write out both points
+ if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
+ {
+ if( isInch )
+ {
+ fprintf( aFile, "%c %d %d 0\n", loopDir,
+ (int) (1000 * (*bo)->startPoint.x),
+ (int) (1000 * (*bo)->startPoint.y) );
+ fprintf( aFile, "%c %d %d 0\n", loopDir,
+ (int) (1000 * (*bo)->endPoint.x),
+ (int) (1000 * (*bo)->endPoint.y) );
+ }
+ else
+ {
+ fprintf( aFile, "%c %.3f %.3f 0\n", loopDir,
+ (*bo)->startPoint.x, (*bo)->startPoint.y );
+ fprintf( aFile, "%c %.3f %.3f 0\n", loopDir,
+ (*bo)->endPoint.x, (*bo)->endPoint.y );
+ }
+ }
+ else
+ {
+ if( isInch )
+ {
+ fprintf( aFile, "%c %d %d 0\n", loopDir,
+ (int) (1000 * (*bo)->startPoint.x),
+ (int) (1000 * (*bo)->startPoint.y) );
+ fprintf( aFile, "%c %d %d %.2f\n", loopDir,
+ (int) (1000 * (*bo)->endPoint.x),
+ (int) (1000 * (*bo)->endPoint.y),
+ (*bo)->angle );
+ }
+ else
+ {
+ fprintf( aFile, "%c %.3f %.3f 0\n", loopDir,
+ (*bo)->startPoint.x, (*bo)->startPoint.y );
+ fprintf( aFile, "%c %.3f %.3f %.2f\n", loopDir,
+ (*bo)->endPoint.x, (*bo)->endPoint.y, (*bo)->angle );
+ }
+ }
+
+ ++bo;
+
+ // for all other segments we only write out the last point
+ while( bo != eo )
+ {
+ if( isInch )
+ {
+ if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
+ {
+ fprintf( aFile, "%c %d %d 0\n", loopDir,
+ (int) (1000 * (*bo)->endPoint.x),
+ (int) (1000 * (*bo)->endPoint.y) );
+ }
+ else
+ {
+ fprintf( aFile, "%c %d %d %.2f\n", loopDir,
+ (int) (1000 * (*bo)->endPoint.x),
+ (int) (1000 * (*bo)->endPoint.y),
+ (*bo)->angle );
+ }
+ }
+ else
+ {
+ if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
+ {
+ fprintf( aFile, "%c %.5f %.5f 0\n", loopDir,
+ (*bo)->endPoint.x, (*bo)->endPoint.y );
+ }
+ else
+ {
+ fprintf( aFile, "%c %.5f %.5f %.2f\n", loopDir,
+ (*bo)->endPoint.x, (*bo)->endPoint.y, (*bo)->angle );
+ }
+ }
+
+ ++bo;
+ }
+
+ return true;
+}
+
+
+void DXF2IDF::addHeader( const DRW_Header* data )
+{
+ std::map<std::string, DRW_Variant*>::const_iterator it;
+ m_scale = 1.0; // assume no scale factor
+
+ for( it = data->vars.begin(); it != data->vars.end(); ++it )
+ {
+ std::string key = ( (*it).first ).c_str();
+
+ if( key == "$INSUNITS" )
+ {
+ DRW_Variant* var = (*it).second;
+
+ switch( var->content.i )
+ {
+ case 1: // inches
+ m_scale = 25.4;
+ break;
+
+ case 2: // feet
+ m_scale = 304.8;
+ break;
+
+ case 5: // centimeters
+ m_scale = 10.0;
+ break;
+
+ case 6: // meters
+ m_scale = 1000.0;
+ break;
+
+ case 8: // microinches
+ m_scale = 2.54e-5;
+ break;
+
+ case 9: // mils
+ m_scale = 0.0254;
+ break;
+
+ case 10: // yards
+ m_scale = 914.4;
+ break;
+
+ case 11: // Angstroms
+ m_scale = 1.0e-7;
+ break;
+
+ case 12: // nanometers
+ m_scale = 1.0e-6;
+ break;
+
+ case 13: // micrometers
+ m_scale = 1.0e-3;
+ break;
+
+ case 14: // decimeters
+ m_scale = 100.0;
+ break;
+
+ default:
+ // use the default of 1.0 for:
+ // 0: Unspecified Units
+ // 4: mm
+ // 3: miles
+ // 7: kilometers
+ // 15: decameters
+ // 16: hectometers
+ // 17: gigameters
+ // 18: AU
+ // 19: lightyears
+ // 20: parsecs
+ break;
+ }
+ }
+ }
+}
+
+
+void DXF2IDF::addLWPolyline(const DRW_LWPolyline& data )
+{
+ IDF_POINT poly_start;
+ IDF_POINT seg_start;
+ IDF_POINT seg_end;
+ double bulge = 0.0;
+
+ if( !data.vertlist.empty() )
+ {
+ DRW_Vertex2D* vertex = data.vertlist[0];
+ seg_start.x = vertex->x * m_scale;
+ seg_start.y = vertex->y * m_scale;
+ poly_start = seg_start;
+ bulge = vertex->bulge;
+ }
+
+ for( size_t i = 1; i < data.vertlist.size(); ++i )
+ {
+ DRW_Vertex2D* vertex = data.vertlist[i];
+ seg_end.x = vertex->x * m_scale;
+ seg_end.y = vertex->y * m_scale;
+
+ if( std::abs( bulge ) < MIN_BULGE )
+ insertLine( seg_start, seg_end );
+ else
+ insertArc( seg_start, seg_end, bulge );
+
+ seg_start = seg_end;
+ bulge = vertex->bulge;
+ }
+
+ // Polyline flags bit 0 indicates closed (1) or open (0) polyline
+ if( data.flags & 1 )
+ {
+ if( std::abs( bulge ) < MIN_BULGE )
+ insertLine( seg_start, poly_start );
+ else
+ insertArc( seg_start, poly_start, bulge );
+ }
+
+ return;
+}
+
+
+void DXF2IDF::addPolyline(const DRW_Polyline& data )
+{
+ IDF_POINT poly_start;
+ IDF_POINT seg_start;
+ IDF_POINT seg_end;
+
+ if( !data.vertlist.empty() )
+ {
+ DRW_Vertex* vertex = data.vertlist[0];
+ seg_start.x = vertex->basePoint.x * m_scale;
+ seg_start.y = vertex->basePoint.y * m_scale;
+ poly_start = seg_start;
+ }
+
+ for( size_t i = 1; i < data.vertlist.size(); ++i )
+ {
+ DRW_Vertex* vertex = data.vertlist[i];
+ seg_end.x = vertex->basePoint.x * m_scale;
+ seg_end.y = vertex->basePoint.y * m_scale;
+ insertLine( seg_start, seg_end );
+ seg_start = seg_end;
+ }
+
+ // Polyline flags bit 0 indicates closed (1) or open (0) polyline
+ if( data.flags & 1 )
+ insertLine( seg_start, poly_start );
+
+ return;
+}
+
+
+void DXF2IDF::insertLine( const IDF_POINT& aSegStart, const IDF_POINT& aSegEnd )
+{
+ IDF_SEGMENT* seg = new IDF_SEGMENT( aSegStart, aSegEnd );
+
+ if( seg )
+ lines.push_back( seg );
+
+ return;
+}
+
+
+void DXF2IDF::insertArc( const IDF_POINT& aSegStart, const IDF_POINT& aSegEnd,
+ double aBulge )
+{
+ if( aBulge < -MAX_BULGE )
+ aBulge = -MAX_BULGE;
+ else if( aBulge > MAX_BULGE )
+ aBulge = MAX_BULGE;
+
+ double ang = 720.0 * atan( aBulge ) / M_PI;
+
+ IDF_SEGMENT* seg = new IDF_SEGMENT( aSegStart, aSegEnd, ang, false );
+
+ if( seg )
+ lines.push_back( seg );
+
+ return;
+}
diff --git a/utils/idftools/dxf2idf.h b/utils/idftools/dxf2idf.h
new file mode 100644
index 0000000..2eb8ead
--- /dev/null
+++ b/utils/idftools/dxf2idf.h
@@ -0,0 +1,102 @@
+/*
+ * 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
+ */
+
+#ifndef DXF2IDF_H
+#define DXF2IDF_H
+
+#include <string>
+#include <drw_interface.h>
+#include <idf_common.h>
+
+class DXF2IDF : public DRW_Interface
+{
+private:
+ std::list< IDF_SEGMENT* > lines; // Unsorted list of graphical segments
+ double m_scale; // scaling factor to mm
+
+ void insertLine( const IDF_POINT& aSegStart, const IDF_POINT& aSegEnd );
+ void insertArc( const IDF_POINT& aSegStart, const IDF_POINT& aSegEnd, double aBulge );
+
+public:
+ ~DXF2IDF();
+
+ bool ReadDxf( const std::string aFile );
+ bool WriteOutline( FILE* aFile, bool isInch );
+
+private:
+ // DRW_Interface implemented callback functions
+ virtual void addHeader( const DRW_Header* data );
+ virtual void addLine(const DRW_Line& data);
+ virtual void addArc(const DRW_Arc& data );
+ virtual void addCircle(const DRW_Circle& data );
+ virtual void addLWPolyline(const DRW_LWPolyline& data );
+ virtual void addPolyline(const DRW_Polyline& data );
+
+ // DRW_Interface callbacks unsupported by DXF2IDF
+ virtual void addLType( const DRW_LType& data ){}
+ virtual void addLayer( const DRW_Layer& data ){}
+ virtual void addDimStyle( const DRW_Dimstyle& data ){}
+ virtual void addVport(const DRW_Vport& data){}
+ virtual void addTextStyle(const DRW_Textstyle& data){}
+ virtual void addBlock(const DRW_Block& data ){}
+ virtual void setBlock(const int handle){}
+ virtual void endBlock(){}
+ virtual void addPoint(const DRW_Point& data ){}
+ virtual void addRay(const DRW_Ray& data ){}
+ virtual void addXline(const DRW_Xline& data ){}
+ virtual void addEllipse(const DRW_Ellipse& data ){}
+ virtual void addSpline(const DRW_Spline* data ){}
+ virtual void addKnot(const DRW_Entity&){}
+ virtual void addInsert(const DRW_Insert& data ){}
+ virtual void addTrace(const DRW_Trace& data ){}
+ virtual void add3dFace(const DRW_3Dface& data ){}
+ virtual void addSolid(const DRW_Solid& data ){}
+ virtual void addMText(const DRW_MText& data){}
+ virtual void addText(const DRW_Text& data ){}
+ virtual void addDimAlign(const DRW_DimAligned *data ){}
+ virtual void addDimLinear(const DRW_DimLinear *data ){}
+ virtual void addDimRadial(const DRW_DimRadial *data ){}
+ virtual void addDimDiametric(const DRW_DimDiametric *data ){}
+ virtual void addDimAngular(const DRW_DimAngular *data ){}
+ virtual void addDimAngular3P(const DRW_DimAngular3p *data ){}
+ virtual void addDimOrdinate(const DRW_DimOrdinate *data ){}
+ virtual void addLeader(const DRW_Leader *data ){}
+ virtual void addHatch(const DRW_Hatch* data ){}
+ virtual void addViewport(const DRW_Viewport& data){}
+ virtual void addImage(const DRW_Image* data ){}
+ virtual void linkImage(const DRW_ImageDef* data ){}
+ virtual void addComment(const char*){}
+ virtual void writeHeader(DRW_Header& data){}
+ virtual void writeBlocks(){}
+ virtual void writeBlockRecords(){}
+ virtual void writeEntities(){}
+ virtual void writeLTypes(){}
+ virtual void writeLayers(){}
+ virtual void writeTextstyles(){}
+ virtual void writeVports(){}
+ virtual void writeDimstyles(){}
+ virtual void addAppId( const DRW_AppId& data ) {}
+ virtual void writeAppId() {}
+};
+
+#endif // DXF2IDF_H
diff --git a/utils/idftools/dxf2idfmain.cpp b/utils/idftools/dxf2idfmain.cpp
new file mode 100644
index 0000000..df804c1
--- /dev/null
+++ b/utils/idftools/dxf2idfmain.cpp
@@ -0,0 +1,190 @@
+/*
+ * 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
+ */
+
+#include <cstdio>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <list>
+#include <dxf2idf.h>
+
+using namespace std;
+
+int main( int argc, char **argv )
+{
+ list< string > comments;
+ string line;
+ stringstream tstr;
+
+ string dname; // DXF filename
+ string gname; // Geometry Name
+ string pname; // Part Name
+ double height; // extrusion height
+ bool inch = false; // true = inches, false = mm
+ bool ok;
+
+ if( argc == 1 )
+ {
+ // no arguments; print out usage information
+ cout << "dxf2idf: this program takes line, arc, and circle segments\n";
+ cout << " from a DXF file and creates an IDF component outline file.\n\n";
+ cout << "Input:\n";
+ cout << " DXF filename: the input file, must end in '.dxf'\n";
+ cout << " Units: mm, in (millimeters or inches)\n";
+ cout << " Geometry Name: string, as per IDF version 3.0 specification\n";
+ cout << " Part Name: as per IDF version 3.0 specification of Part Number\n";
+ cout << " Height: extruded height of the outline\n";
+ cout << " Comments: all non-empty lines are comments to be added to\n";
+ cout << " the IDF file. An empty line signifies the end of\n";
+ cout << " the comment block.\n";
+ cout << " File name: output filename, must end in '.idf'\n\n";
+ }
+
+ line.clear();
+ while( line.empty() || line.find( ".dxf" ) == string::npos )
+ {
+ cout << "* DXF filename: ";
+
+ line.clear();
+ std::getline( cin, line );
+ }
+ dname = line;
+
+ line.clear();
+ while( line.compare( "mm" ) && line.compare( "in" )
+ && line.compare( "MM" ) && line.compare( "IN" ) )
+ {
+ cout << "* Units (mm,in): ";
+ line.clear();
+ std::getline( cin, line );
+ }
+
+ if( line.compare( "mm" ) && line.compare( "MM" ) )
+ inch = true;
+
+ line.clear();
+ while( line.empty() )
+ {
+ cout << "* Geometry name: ";
+ line.clear();
+ std::getline( cin, line );
+
+ if( line.find( "\"" ) != string::npos )
+ {
+ cerr << "[INFO] geometry name may not contain quotation marks\n";
+ line.clear();
+ }
+ }
+ gname = line;
+
+ line.clear();
+ while( line.empty() )
+ {
+ cout << "* Part name: ";
+ line.clear();
+ std::getline( cin, line );
+
+ if( line.find( "\"" ) != string::npos )
+ {
+ cerr << "[INFO] part name may not contain quotation marks\n";
+ line.clear();
+ }
+ }
+ pname = line;
+
+ ok = false;
+ while( !ok )
+ {
+ cout << "* Height: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> height;
+ if( !tstr.fail() && height > 0.001 )
+ ok = true;
+ }
+
+ cout << "* COMMENTS: any non-blank line is a comment;\n";
+ cout << " a blank line signifies the end of comments.\n";
+ ok = false;
+ while( !ok )
+ {
+ line.clear();
+ std::getline( cin, line );
+
+ if( line.empty() )
+ {
+ ok = true;
+ }
+ else
+ {
+ if( line[0] != '#' )
+ line.insert( 0, "# " );
+
+ comments.push_back( line );
+ }
+ }
+
+ line.clear();
+ while( line.empty() || line.find( ".idf" ) == string::npos )
+ {
+ cout << "* File name (*.idf): ";
+
+ line.clear();
+ std::getline( cin, line );
+ }
+
+ DXF2IDF dxf;
+
+ dxf.ReadDxf( dname.c_str() );
+
+ FILE* fp = fopen( line.c_str(), "w" );
+
+ list< string >::const_iterator scom = comments.begin();
+ list< string >::const_iterator ecom = comments.end();
+
+ while( scom != ecom )
+ {
+ fprintf( fp, "%s\n", (*scom).c_str() );
+ ++scom;
+ }
+
+ fprintf( fp, ".ELECTRICAL\n" );
+
+ if( inch )
+ fprintf( fp, "\"%s\" \"%s\" THOU %d\n", gname.c_str(),
+ pname.c_str(), (int) (height * 1000.0) );
+ else
+ fprintf( fp, "\"%s\" \"%s\" MM %.3f\n", gname.c_str(),
+ pname.c_str(), height );
+
+ dxf.WriteOutline( fp, inch );
+
+ fprintf( fp, ".END_ELECTRICAL\n" );
+
+ return 0;
+} \ No newline at end of file
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;
+}
diff --git a/utils/idftools/idf_common.cpp b/utils/idftools/idf_common.cpp
new file mode 100644
index 0000000..8eedfa6
--- /dev/null
+++ b/utils/idftools/idf_common.cpp
@@ -0,0 +1,1387 @@
+/**
+ * file: idf_common.cpp
+ *
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013-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
+ */
+
+
+#include <list>
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <iomanip>
+#include <cerrno>
+#include <cstdio>
+#include <cmath>
+#include <idf_common.h>
+#include <idf_helpers.h>
+
+using namespace IDF3;
+using namespace std;
+
+
+std::string source;
+std::string message;
+
+IDF_ERROR::IDF_ERROR( const char* aSourceFile,
+ const char* aSourceMethod,
+ int aSourceLine,
+ const std::string& aMessage ) throw()
+{
+ ostringstream ostr;
+
+ if( aSourceFile )
+ ostr << "* " << aSourceFile << ":";
+ else
+ ostr << "* [BUG: No Source File]:";
+
+ ostr << aSourceLine << ":";
+
+ if( aSourceMethod )
+ ostr << aSourceMethod << "(): ";
+ else
+ ostr << "[BUG: No Source Method]:\n* ";
+
+ ostr << aMessage;
+ message = ostr.str();
+
+ return;
+}
+
+
+IDF_ERROR::~IDF_ERROR() throw()
+{
+ return;
+}
+
+
+const char* IDF_ERROR::what() const throw()
+{
+ return message.c_str();
+}
+
+
+IDF_NOTE::IDF_NOTE()
+{
+ xpos = 0.0;
+ ypos = 0.0;
+ height = 0.0;
+ length = 0.0;
+}
+
+
+bool IDF_NOTE::readNote( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState,
+ IDF3::IDF_UNIT aBoardUnit )
+{
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ int idx = 0;
+ bool quoted = false;
+ std::string token;
+
+ // RECORD 2: X, Y, text Height, text Length, "TEXT"
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( ( !aBoardFile.good() && !aBoardFile.eof() ) || iline.empty() )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "problems reading board notes" ) );
+ }
+
+ if( isComment )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: comment within a section (NOTES)" ) );
+ }
+
+ idx = 0;
+ GetIDFString( iline, token, quoted, idx );
+
+ if( quoted )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: X position in NOTES section must not be in quotes" ) );
+ }
+
+ if( CompareToken( ".END_NOTES", token ) )
+ return false;
+
+ istringstream istr;
+ istr.str( token );
+
+ istr >> xpos;
+ if( istr.fail() )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: X position in NOTES section is not numeric" ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: Y position in NOTES section is missing" ) );
+ }
+
+ if( quoted )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: Y position in NOTES section must not be in quotes" ) );
+ }
+
+ istr.clear();
+ istr.str( token );
+
+ istr >> ypos;
+ if( istr.fail() )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: Y position in NOTES section is not numeric" ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: text height in NOTES section is missing" ) );
+ }
+
+ if( quoted )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: text height in NOTES section must not be in quotes" ) );
+ }
+
+ istr.clear();
+ istr.str( token );
+
+ istr >> height;
+ if( istr.fail() )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: text height in NOTES section is not numeric" ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: text length in NOTES section is missing" ) );
+ }
+
+ if( quoted )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: text length in NOTES section must not be in quotes" ) );
+ }
+
+ istr.clear();
+ istr.str( token );
+
+ istr >> length;
+ if( istr.fail() )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: text length in NOTES section is not numeric" ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: text value in NOTES section is missing" ) );
+ }
+
+ text = token;
+
+ if( aBoardUnit == UNIT_THOU )
+ {
+ xpos *= IDF_THOU_TO_MM;
+ ypos *= IDF_THOU_TO_MM;
+ height *= IDF_THOU_TO_MM;
+ length *= IDF_THOU_TO_MM;
+ }
+
+ return true;
+}
+
+
+bool IDF_NOTE::writeNote( std::ofstream& aBoardFile, IDF3::IDF_UNIT aBoardUnit )
+{
+ if( aBoardUnit == UNIT_THOU )
+ {
+ aBoardFile << setiosflags(ios::fixed) << setprecision(1)
+ << (xpos / IDF_THOU_TO_MM) << " "
+ << (ypos / IDF_THOU_TO_MM) << " "
+ << (height / IDF_THOU_TO_MM) << " "
+ << (length / IDF_THOU_TO_MM) << " ";
+ }
+ else
+ {
+ aBoardFile << setiosflags(ios::fixed) << setprecision(5)
+ << xpos << " " << ypos << " " << height << " " << length << " ";
+ }
+
+ aBoardFile << "\"" << text << "\"\n";
+
+ return !aBoardFile.bad();
+}
+
+
+void IDF_NOTE::SetText( const std::string& aText )
+{
+ text = aText;
+ return;
+}
+
+void IDF_NOTE::SetPosition( double aXpos, double aYpos )
+{
+ xpos = aXpos;
+ ypos = aYpos;
+ return;
+}
+
+void IDF_NOTE::SetSize( double aHeight, double aLength )
+{
+ height = aHeight;
+ length = aLength;
+ return;
+}
+
+const std::string& IDF_NOTE::GetText( void )
+{
+ return text;
+}
+
+void IDF_NOTE::GetPosition( double& aXpos, double& aYpos )
+{
+ aXpos = xpos;
+ aYpos = ypos;
+ return;
+}
+
+void IDF_NOTE::GetSize( double& aHeight, double& aLength )
+{
+ aHeight = height;
+ aLength = length;
+ return;
+}
+
+
+/*
+ * CLASS: IDF_DRILL_DATA
+ */
+IDF_DRILL_DATA::IDF_DRILL_DATA()
+{
+ dia = 0.0;
+ x = 0.0;
+ y = 0.0;
+ plating = NPTH;
+ kref = NOREFDES;
+ khole = MTG;
+ owner = UNOWNED;
+
+ return;
+}
+
+
+IDF_DRILL_DATA::IDF_DRILL_DATA( double aDrillDia, double aPosX, double aPosY,
+ IDF3::KEY_PLATING aPlating,
+ const std::string aRefDes,
+ const std::string aHoleType,
+ IDF3::KEY_OWNER aOwner )
+{
+ if( aDrillDia < 0.3 )
+ dia = 0.3;
+ else
+ dia = aDrillDia;
+
+ x = aPosX;
+ y = aPosY;
+ plating = aPlating;
+
+ if( !aRefDes.compare( "BOARD" ) )
+ {
+ kref = BOARD;
+ }
+ else if( aRefDes.empty() || !aRefDes.compare( "NOREFDES" ) )
+ {
+ kref = NOREFDES;
+ }
+ else if( !aRefDes.compare( "PANEL" ) )
+ {
+ kref = PANEL;
+ }
+ else
+ {
+ kref = REFDES;
+ refdes = aRefDes;
+ }
+
+ if( !aHoleType.compare( "PIN" ) )
+ {
+ khole = PIN;
+ }
+ else if( !aHoleType.compare( "VIA" ) )
+ {
+ khole = VIA;
+ }
+ else if( aHoleType.empty() || !aHoleType.compare( "MTG" ) )
+ {
+ khole = MTG;
+ }
+ else if( !aHoleType.compare( "TOOL" ) )
+ {
+ khole = TOOL;
+ }
+ else
+ {
+ khole = OTHER;
+ holetype = aHoleType;
+ }
+
+ owner = aOwner;
+} // IDF_DRILL_DATA::IDF_DRILL_DATA( ... )
+
+bool IDF_DRILL_DATA::Matches( double aDrillDia, double aPosX, double aPosY )
+{
+ double ddia = aDrillDia - dia;
+ IDF_POINT p1, p2;
+
+ p1.x = x;
+ p1.y = y;
+ p2.x = aPosX;
+ p2.y = aPosY;
+
+ if( ddia > -0.00001 && ddia < 0.00001 && p1.Matches( p2, 0.00001 ) )
+ return true;
+
+ return false;
+}
+
+bool IDF_DRILL_DATA::read( std::ifstream& aBoardFile, IDF3::IDF_UNIT aBoardUnit,
+ IDF3::FILE_STATE aBoardState, IDF3::IDF_VERSION aIdfVersion )
+{
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ int idx = 0;
+ bool quoted = false;
+ std::string token;
+
+ // RECORD 2: DIA, X, Y, Plating Style, REFDES, HOLE TYPE, HOLE OWNER
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( ( !aBoardFile.good() && !aBoardFile.eof() ) || iline.empty() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading board drilled holes" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: comment within a section (DRILLED HOLES)" ) );
+
+ idx = 0;
+ GetIDFString( iline, token, quoted, idx );
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: drill diameter must not be in quotes" ) );
+
+ if( CompareToken( ".END_DRILLED_HOLES", token ) )
+ return false;
+
+ istringstream istr;
+ istr.str( token );
+
+ istr >> dia;
+ if( istr.fail() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: drill diameter is not numeric" ) );
+
+ if( ( aBoardUnit == UNIT_MM && dia < IDF_MIN_DIA_MM )
+ || ( aBoardUnit == UNIT_THOU && dia < IDF_MIN_DIA_THOU )
+ || ( aBoardUnit == UNIT_TNM && dia < IDF_MIN_DIA_TNM ) )
+ {
+ ostringstream ostr;
+ ostr << "invalid IDF file\n";
+ ostr << "* Invalid drill diameter (too small): '" << token << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: missing X position for drilled hole" ) );
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: X position in DRILLED HOLES section must not be in quotes" ) );
+
+ istr.clear();
+ istr.str( token );
+
+ istr >> x;
+ if( istr.fail() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: X position in DRILLED HOLES section is not numeric" ) );
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: missing Y position for drilled hole" ) );
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: Y position in DRILLED HOLES section must not be in quotes" ) );
+
+ istr.clear();
+ istr.str( token );
+
+ istr >> y;
+ if( istr.fail() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: Y position in DRILLED HOLES section is not numeric" ) );
+
+ if( aIdfVersion > IDF_V2 )
+ {
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: missing PLATING for drilled hole" ) );
+
+ if( CompareToken( "PTH", token ) )
+ {
+ plating = IDF3::PTH;
+ }
+ else if( CompareToken( "NPTH", token ) )
+ {
+ plating = IDF3::NPTH;
+ }
+ else
+ {
+ ostringstream ostr;
+ ostr << "invalid IDFv3 file\n";
+ ostr << "* Violation of specification: invalid PLATING type ('" << token << "')";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+ else
+ {
+ plating = IDF3::PTH;
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ if( aIdfVersion > IDF_V2 )
+ {
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: missing REFDES for drilled hole" ) );
+ }
+ else
+ {
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv2 file\n"
+ "* Violation of specification: missing HOLE TYPE for drilled hole" ) );
+ }
+ }
+
+ std::string tok1 = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ if( aIdfVersion > IDF_V2 )
+ {
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: missing HOLE TYPE for drilled hole" ) );
+ }
+ else
+ {
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv2 file\n"
+ "* Violation of specification: missing REFDES for drilled hole" ) );
+ }
+ }
+
+ std::string tok2 = token;
+
+ if( aIdfVersion > IDF_V2 )
+ token = tok1;
+
+ if( CompareToken( "BOARD", token ) )
+ {
+ kref = IDF3::BOARD;
+ }
+ else if( CompareToken( "NOREFDES", token ) )
+ {
+ kref = IDF3::NOREFDES;
+ }
+ else if( CompareToken( "PANEL", token ) )
+ {
+ kref = IDF3::PANEL;
+ }
+ else
+ {
+ kref = IDF3::REFDES;
+ refdes = token;
+ }
+
+ if( aIdfVersion > IDF_V2 )
+ token = tok2;
+ else
+ token = tok1;
+
+ if( CompareToken( "PIN", token ) )
+ {
+ khole = IDF3::PIN;
+ }
+ else if( CompareToken( "VIA", token ) )
+ {
+ khole = IDF3::VIA;
+ }
+ else if( CompareToken( "MTG", token ) )
+ {
+ khole = IDF3::MTG;
+ }
+ else if( CompareToken( "TOOL", token ) )
+ {
+ khole = IDF3::TOOL;
+ }
+ else
+ {
+ khole = IDF3::OTHER;
+ holetype = token;
+ }
+
+ if( aIdfVersion > IDF_V2 )
+ {
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: missing OWNER for drilled hole" ) );
+
+ if( !ParseOwner( token, owner ) )
+ {
+ ostringstream ostr;
+ ostr << "invalid IDFv3 file\n";
+ ostr << "* Violation of specification: invalid OWNER for drilled hole ('" << token << "')";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+ else
+ {
+ owner = IDF3::UNOWNED;
+ }
+
+ if( aBoardUnit == UNIT_THOU )
+ {
+ dia *= IDF_THOU_TO_MM;
+ x *= IDF_THOU_TO_MM;
+ y *= IDF_THOU_TO_MM;
+ }
+ else if( ( aIdfVersion == IDF_V2 ) && ( aBoardUnit == UNIT_TNM ) )
+ {
+ dia *= IDF_TNM_TO_MM;
+ x *= IDF_TNM_TO_MM;
+ y *= IDF_TNM_TO_MM;
+ }
+ else if( aBoardUnit != UNIT_MM )
+ {
+ ostringstream ostr;
+ ostr << "\n* BUG: invalid UNIT type: " << aBoardUnit;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ return true;
+}
+
+void IDF_DRILL_DATA::write( std::ofstream& aBoardFile, IDF3::IDF_UNIT aBoardUnit )
+{
+ std::string holestr;
+ std::string refstr;
+ std::string ownstr;
+ std::string pltstr;
+
+ switch( khole )
+ {
+ case PIN:
+ holestr = "PIN";
+ break;
+
+ case VIA:
+ holestr = "VIA";
+ break;
+
+ case TOOL:
+ holestr = "TOOL";
+ break;
+
+ case OTHER:
+ holestr = "\"" + holetype + "\"";
+ break;
+
+ default:
+ holestr = "MTG";
+ break;
+ }
+
+ switch( kref )
+ {
+ case BOARD:
+ refstr = "BOARD";
+ break;
+
+ case PANEL:
+ refstr = "PANEL";
+ break;
+
+ case REFDES:
+ refstr = "\"" + refdes + "\"";
+ break;
+
+ default:
+ refstr = "NOREFDES";
+ break;
+ }
+
+ if( plating == PTH )
+ pltstr = "PTH";
+ else
+ pltstr = "NPTH";
+
+ switch( owner )
+ {
+ case MCAD:
+ ownstr = "MCAD";
+ break;
+
+ case ECAD:
+ ownstr = "ECAD";
+ break;
+
+ default:
+ ownstr = "UNOWNED";
+ break;
+ }
+
+ if( aBoardUnit == UNIT_MM )
+ {
+ aBoardFile << std::setiosflags( std::ios::fixed ) << std::setprecision( 3 ) << dia << " "
+ << std::setprecision( 5 ) << x << " " << y << " "
+ << pltstr.c_str() << " " << refstr.c_str() << " "
+ << holestr.c_str() << " " << ownstr.c_str() << "\n";
+ }
+ else
+ {
+ aBoardFile << std::setiosflags( std::ios::fixed ) << std::setprecision( 1 ) << (dia / IDF_THOU_TO_MM) << " "
+ << std::setprecision( 1 ) << (x / IDF_THOU_TO_MM) << " " << (y / IDF_THOU_TO_MM) << " "
+ << pltstr.c_str() << " " << refstr.c_str() << " "
+ << holestr.c_str() << " " << ownstr.c_str() << "\n";
+ }
+
+ return;
+} // IDF_DRILL_DATA::Write( aBoardFile, unitMM )
+
+
+double IDF_DRILL_DATA::GetDrillDia()
+{
+ return dia;
+}
+
+double IDF_DRILL_DATA::GetDrillXPos()
+{
+ return x;
+}
+
+double IDF_DRILL_DATA::GetDrillYPos()
+{
+ return y;
+}
+
+IDF3::KEY_PLATING IDF_DRILL_DATA::GetDrillPlating()
+{
+ return plating;
+}
+
+const std::string& IDF_DRILL_DATA::GetDrillRefDes()
+{
+ switch( kref )
+ {
+ case BOARD:
+ refdes = "BOARD";
+ break;
+
+ case PANEL:
+ refdes = "PANEL";
+ break;
+
+ case REFDES:
+ break;
+
+ default:
+ refdes = "NOREFDES";
+ break;
+ }
+
+ return refdes;
+}
+
+const std::string& IDF_DRILL_DATA::GetDrillHoleType()
+{
+ switch( khole )
+ {
+ case PIN:
+ holetype = "PIN";
+ break;
+
+ case VIA:
+ holetype = "VIA";
+ break;
+
+ case TOOL:
+ holetype = "TOOL";
+ break;
+
+ case OTHER:
+ break;
+
+ default:
+ holetype = "MTG";
+ break;
+ }
+
+ return holetype;
+}
+
+
+#ifdef DEBUG_IDF
+void IDF3::PrintSeg( IDF_SEGMENT* aSegment )
+{
+ if( aSegment->IsCircle() )
+ {
+ fprintf(stdout, "printSeg(): CIRCLE: C(%.3f, %.3f) P(%.3f, %.3f) rad. %.3f\n",
+ aSegment->startPoint.x, aSegment->startPoint.y,
+ aSegment->endPoint.x, aSegment->endPoint.y,
+ aSegment->radius );
+ return;
+ }
+
+ if( aSegment->angle < -MIN_ANG || aSegment->angle > MIN_ANG )
+ {
+ fprintf(stdout, "printSeg(): ARC: p1(%.3f, %.3f) p2(%.3f, %.3f) ang. %.3f\n",
+ aSegment->startPoint.x, aSegment->startPoint.y,
+ aSegment->endPoint.x, aSegment->endPoint.y,
+ aSegment->angle );
+ return;
+ }
+
+ fprintf(stdout, "printSeg(): LINE: p1(%.3f, %.3f) p2(%.3f, %.3f)\n",
+ aSegment->startPoint.x, aSegment->startPoint.y,
+ aSegment->endPoint.x, aSegment->endPoint.y );
+
+ return;
+}
+#endif
+
+
+bool IDF_POINT::Matches( const IDF_POINT& aPoint, double aRadius )
+{
+ double dx = x - aPoint.x;
+ double dy = y - aPoint.y;
+
+ double d2 = dx * dx + dy * dy;
+
+ if( d2 <= aRadius * aRadius )
+ return true;
+
+ return false;
+}
+
+
+double IDF_POINT::CalcDistance( const IDF_POINT& aPoint ) const
+{
+ double dx = aPoint.x - x;
+ double dy = aPoint.y - y;
+ double dist = sqrt( dx * dx + dy * dy );
+
+ return dist;
+}
+
+
+double IDF3::CalcAngleRad( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint )
+{
+ return atan2( aEndPoint.y - aStartPoint.y, aEndPoint.x - aStartPoint.x );
+}
+
+
+double IDF3::CalcAngleDeg( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint )
+{
+ double ang = CalcAngleRad( aStartPoint, aEndPoint );
+
+ // round to thousandths of a degree
+ int iang = int (ang / M_PI * 1800000.0);
+
+ ang = iang / 10000.0;
+
+ return ang;
+}
+
+
+void IDF3::GetOutline( std::list<IDF_SEGMENT*>& aLines,
+ IDF_OUTLINE& aOutline )
+{
+ aOutline.Clear();
+
+ // NOTE: To tell if the point order is CCW or CW,
+ // sum all: (endPoint.X[n] - startPoint.X[n])*(endPoint[n] + startPoint.Y[n])
+ // If the result is >0, the direction is CW, otherwise
+ // it is CCW. Note that the result cannot be 0 unless
+ // we have a bounded area of 0.
+
+ // First we find the segment with the leftmost point
+ std::list<IDF_SEGMENT*>::iterator bl = aLines.begin();
+ std::list<IDF_SEGMENT*>::iterator el = aLines.end();
+ std::list<IDF_SEGMENT*>::iterator idx = bl++; // iterator for the object with minX
+
+ double minx = (*idx)->GetMinX();
+ double curx;
+
+ while( bl != el )
+ {
+ curx = (*bl)->GetMinX();
+
+ if( curx < minx )
+ {
+ minx = curx;
+ idx = bl;
+ }
+
+ ++bl;
+ }
+
+ aOutline.push( *idx );
+#ifdef DEBUG_IDF
+ PrintSeg( *idx );
+#endif
+ aLines.erase( idx );
+
+ // If the item is a circle then we're done
+ if( aOutline.front()->IsCircle() )
+ return;
+
+ // Assemble the loop
+ bool complete = false; // set if loop is complete
+ bool matched; // set if a segment's end point was matched
+
+ while( !complete )
+ {
+ matched = false;
+ bl = aLines.begin();
+ el = aLines.end();
+
+ while( bl != el && !matched )
+ {
+ if( (*bl)->MatchesStart( aOutline.back()->endPoint ) )
+ {
+ if( (*bl)->IsCircle() )
+ {
+ // a circle on the perimeter is pathological but we just ignore it
+ ++bl;
+ }
+ else
+ {
+ matched = true;
+#ifdef DEBUG_IDF
+ PrintSeg( *bl );
+#endif
+ aOutline.push( *bl );
+ bl = aLines.erase( bl );
+ }
+
+ continue;
+ }
+
+ ++bl;
+ }
+
+ if( !matched )
+ {
+ // attempt to match the end points
+ bl = aLines.begin();
+ el = aLines.end();
+
+ while( bl != el && !matched )
+ {
+ if( (*bl)->MatchesEnd( aOutline.back()->endPoint ) )
+ {
+ if( (*bl)->IsCircle() )
+ {
+ // a circle on the perimeter is pathological but we just ignore it
+ ++bl;
+ }
+ else
+ {
+ matched = true;
+ (*bl)->SwapEnds();
+#ifdef DEBUG_IDF
+ printSeg( *bl );
+#endif
+ aOutline.push( *bl );
+ bl = aLines.erase( bl );
+ }
+
+ continue;
+ }
+
+ ++bl;
+ }
+ }
+
+ if( !matched )
+ {
+ // still no match - attempt to close the loop
+ if( (aOutline.size() > 1) || ( aOutline.front()->angle < -MIN_ANG )
+ || ( aOutline.front()->angle > MIN_ANG ) )
+ {
+ // close the loop
+ IDF_SEGMENT* seg = new IDF_SEGMENT( aOutline.back()->endPoint,
+ aOutline.front()->startPoint );
+
+ if( seg )
+ {
+ complete = true;
+#ifdef DEBUG_IDF
+ printSeg( seg );
+#endif
+ aOutline.push( seg );
+ break;
+ }
+ }
+
+ // the outline is bad; drop the segments
+ aOutline.Clear();
+
+ return;
+ }
+
+ // check if the loop is complete
+ if( aOutline.front()->MatchesStart( aOutline.back()->endPoint ) )
+ {
+ complete = true;
+ break;
+ }
+ }
+}
+
+
+IDF_SEGMENT::IDF_SEGMENT()
+{
+ angle = 0.0;
+ offsetAngle = 0.0;
+ radius = 0.0;
+}
+
+
+IDF_SEGMENT::IDF_SEGMENT( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint )
+{
+ angle = 0.0;
+ offsetAngle = 0.0;
+ radius = 0.0;
+ startPoint = aStartPoint;
+ endPoint = aEndPoint;
+}
+
+
+IDF_SEGMENT::IDF_SEGMENT( const IDF_POINT& aStartPoint,
+ const IDF_POINT& aEndPoint,
+ double aAngle,
+ bool aFromKicad )
+{
+ double diff = abs( aAngle ) - 360.0;
+
+ if( ( diff < MIN_ANG
+ && diff > -MIN_ANG ) || ( aAngle < MIN_ANG && aAngle > -MIN_ANG ) || (!aFromKicad) )
+ {
+ angle = 0.0;
+ startPoint = aStartPoint;
+ endPoint = aEndPoint;
+
+ if( diff < MIN_ANG && diff > -MIN_ANG )
+ {
+ angle = 360.0;
+ center = aStartPoint;
+ offsetAngle = 0.0;
+ radius = aStartPoint.CalcDistance( aEndPoint );
+ }
+ else if( aAngle > MIN_ANG || aAngle < -MIN_ANG )
+ {
+ angle = aAngle;
+ CalcCenterAndRadius();
+ }
+
+ return;
+ }
+
+ // we need to convert from the KiCad arc convention
+ angle = aAngle;
+
+ center = aStartPoint;
+
+ offsetAngle = IDF3::CalcAngleDeg( aStartPoint, aEndPoint );
+
+ radius = aStartPoint.CalcDistance( aEndPoint );
+
+ startPoint = aEndPoint;
+
+ double ang = offsetAngle + aAngle;
+ ang = (ang / 180.0) * M_PI;
+
+ endPoint.x = ( radius * cos( ang ) ) + center.x;
+ endPoint.y = ( radius * sin( ang ) ) + center.y;
+}
+
+
+bool IDF_SEGMENT::MatchesStart( const IDF_POINT& aPoint, double aRadius )
+{
+ return startPoint.Matches( aPoint, aRadius );
+}
+
+
+bool IDF_SEGMENT::MatchesEnd( const IDF_POINT& aPoint, double aRadius )
+{
+ return endPoint.Matches( aPoint, aRadius );
+}
+
+
+void IDF_SEGMENT::CalcCenterAndRadius( void )
+{
+ // NOTE: this routine does not check if the points are the same
+ // or too close to be sensible in a production setting.
+
+ double offAng = IDF3::CalcAngleRad( startPoint, endPoint );
+ double d = startPoint.CalcDistance( endPoint ) / 2.0;
+ double xm = ( startPoint.x + endPoint.x ) * 0.5;
+ double ym = ( startPoint.y + endPoint.y ) * 0.5;
+
+ radius = d / sin( angle * M_PI / 360.0 );
+
+ if( radius < 0.0 )
+ {
+ radius = -radius;
+ }
+
+ // calculate the height of the triangle with base d and hypotenuse r
+ double dh2 = radius * radius - d * d;
+
+ if( dh2 < 0 )
+ {
+ // this should only ever happen due to rounding errors when r == d
+ dh2 = 0;
+ }
+
+ double h = sqrt( dh2 );
+
+ if( angle > 0.0 )
+ offAng += M_PI_2;
+ else
+ offAng -= M_PI_2;
+
+ if( angle < -180.0 )
+ offAng += M_PI;
+ else if( angle > 180 )
+ offAng -= M_PI;
+
+ center.x = h * cos( offAng ) + xm;
+ center.y = h * sin( offAng ) + ym;
+
+ offsetAngle = IDF3::CalcAngleDeg( center, startPoint );
+}
+
+
+bool IDF_SEGMENT::IsCircle( void )
+{
+ double diff = abs( angle ) - 360.0;
+
+ if( ( diff < MIN_ANG ) && ( diff > -MIN_ANG ) )
+ return true;
+
+ return false;
+}
+
+
+double IDF_SEGMENT::GetMinX( void )
+{
+ if( angle == 0.0 )
+ return std::min( startPoint.x, endPoint.x );
+
+ // Calculate the leftmost point of the circle or arc
+
+ if( IsCircle() )
+ {
+ // if only everything were this easy
+ return center.x - radius;
+ }
+
+ // cases:
+ // 1. CCW arc: if offset + included angle >= 180 deg then
+ // MinX = center.x - radius, otherwise MinX is the
+ // same as for the case of a line.
+ // 2. CW arc: if offset + included angle <= -180 deg then
+ // MinX = center.x - radius, otherwise MinX is the
+ // same as for the case of a line.
+
+ if( angle > 0 )
+ {
+ // CCW case
+ if( ( offsetAngle + angle ) >= 180.0 )
+ {
+ return center.x - radius;
+ }
+ else
+ {
+ return std::min( startPoint.x, endPoint.x );
+ }
+ }
+
+ // CW case
+ if( ( offsetAngle + angle ) <= -180.0 )
+ {
+ return center.x - radius;
+ }
+
+ return std::min( startPoint.x, endPoint.x );
+}
+
+
+void IDF_SEGMENT::SwapEnds( void )
+{
+ if( IsCircle() )
+ {
+ // reverse the direction
+ angle = -angle;
+ return;
+ }
+
+ IDF_POINT tmp = startPoint;
+ startPoint = endPoint;
+ endPoint = tmp;
+
+ if( ( angle < MIN_ANG ) && ( angle > -MIN_ANG ) )
+ return; // nothing more to do
+
+ // change the direction of the arc
+ angle = -angle;
+ // calculate the new offset angle
+ offsetAngle = IDF3::CalcAngleDeg( center, startPoint );
+}
+
+
+bool IDF_OUTLINE::IsCCW( void )
+{
+ // note: when outlines are not valid, 'false' is returned
+ switch( outline.size() )
+ {
+ case 0:
+ // no outline
+ return false;
+ break;
+
+ case 1:
+ // circles are always reported as CCW
+ if( outline.front()->IsCircle() )
+ return true;
+ else
+ return false;
+ break;
+
+ case 2:
+ // we may have a closed outline consisting of:
+ // 1. arc and line, winding depends on the arc
+ // 2. 2 arcs, winding depends on larger arc
+ {
+ double a1 = outline.front()->angle;
+ double a2 = outline.back()->angle;
+
+ if( ( a1 < -MIN_ANG || a1 > MIN_ANG )
+ && ( a2 < -MIN_ANG || a2 > MIN_ANG ) )
+ {
+ // we have 2 arcs; the winding is determined by
+ // the longer cord. although the angles are in
+ // degrees, there is no need to convert to radians
+ // to determine the longer cord.
+ if( abs( a1 * outline.front()->radius ) >=
+ abs( a2 * outline.back()->radius ) )
+ {
+ // winding depends on a1
+ if( a1 < 0.0 )
+ return false;
+ else
+ return true;
+ }
+ else
+ {
+ if( a2 < 0.0 )
+ return false;
+ else
+ return true;
+ }
+ }
+
+ // we may have a line + arc (or 2 lines)
+ if( a1 < -MIN_ANG )
+ return false;
+
+ if( a1 > MIN_ANG )
+ return true;
+
+ if( a2 < -MIN_ANG )
+ return false;
+
+ if( a2 > MIN_ANG )
+ return true;
+
+ // we have 2 lines (invalid outline)
+ return false;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ double winding = dir + ( outline.front()->startPoint.x - outline.back()->endPoint.x )
+ * ( outline.front()->startPoint.y + outline.back()->endPoint.y );
+
+ if( winding > 0.0 )
+ return false;
+
+ return true;
+}
+
+
+// returns true if the outline is a circle
+bool IDF_OUTLINE::IsCircle( void )
+{
+ if( outline.front()->IsCircle() )
+ return true;
+
+ return false;
+}
+
+
+bool IDF_OUTLINE::push( IDF_SEGMENT* item )
+{
+ if( !outline.empty() )
+ {
+ if( item->IsCircle() )
+ {
+ // not allowed
+ ERROR_IDF << "INVALID GEOMETRY\n";
+ cerr << "* a circle is being added to a non-empty outline\n";
+ return false;
+ }
+ else
+ {
+ if( outline.back()->IsCircle() )
+ {
+ // we can't add lines to a circle
+ ERROR_IDF << "INVALID GEOMETRY\n";
+ cerr << "* a line is being added to a circular outline\n";
+ return false;
+ }
+ else if( !item->MatchesStart( outline.back()->endPoint ) )
+ {
+ // startPoint[N] != endPoint[N -1]
+ ERROR_IDF << "INVALID GEOMETRY\n";
+ cerr << "* disjoint segments (current start point != last end point)\n";
+ cerr << "* start point: " << item->startPoint.x << ", " << item->startPoint.y << "\n";
+ cerr << "* end point: " << outline.back()->endPoint.x << ", " << outline.back()->endPoint.y << "\n";
+ return false;
+ }
+ }
+ }
+
+ outline.push_back( item );
+
+ double ang = outline.back()->angle;
+ double oang = outline.back()->offsetAngle;
+ double radius = outline.back()->radius;
+
+ if( ang < -MIN_ANG || ang > MIN_ANG )
+ {
+ // arcs require special consideration since the winding depends on
+ // the arc length; the arc length is adequately represented by
+ // taking 2 cords from the endpoints to the midpoint of the arc.
+ oang = (oang + ang / 2.0) * M_PI / 180.0;
+ double midx = outline.back()->center.x + radius * cos( oang );
+ double midy = outline.back()->center.y + radius * sin( oang );
+
+ dir += ( outline.back()->endPoint.x - midx )
+ * ( outline.back()->endPoint.y + midy );
+
+ dir += ( midx - outline.back()->startPoint.x )
+ * ( midy + outline.back()->startPoint.y );
+ }
+ else
+ {
+ dir += ( outline.back()->endPoint.x - outline.back()->startPoint.x )
+ * ( outline.back()->endPoint.y + outline.back()->startPoint.y );
+ }
+
+ return true;
+}
diff --git a/utils/idftools/idf_common.h b/utils/idftools/idf_common.h
new file mode 100644
index 0000000..398f8f8
--- /dev/null
+++ b/utils/idftools/idf_common.h
@@ -0,0 +1,711 @@
+/**
+ * @file idf_common.h
+ */
+
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013-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
+ */
+
+#ifndef IDF_COMMON_H
+#define IDF_COMMON_H
+
+#include <list>
+#include <fstream>
+#include <exception>
+#include <string>
+#include <cmath>
+
+// differences in angle smaller than MIN_ANG are considered equal
+#define MIN_ANG (0.01)
+
+class IDF_POINT;
+class IDF_SEGMENT;
+class IDF_DRILL_DATA;
+class IDF_OUTLINE;
+class IDF_LIB;
+
+
+struct IDF_ERROR : std::exception
+{
+ std::string message;
+
+ IDF_ERROR( const char* aSourceFile,
+ const char* aSourceMethod,
+ int aSourceLine,
+ const std::string& aMessage ) throw();
+
+ virtual ~IDF_ERROR() throw();
+
+ virtual const char* what() const throw();
+};
+
+
+namespace IDF3 {
+
+ /**
+ * ENUM FILE_STATE
+ * represents state values for the IDF parser's input
+ */
+ enum FILE_STATE
+ {
+ FILE_START = 0, // no data has been read; expecting .HEADER
+ FILE_HEADER, // header has been read; expecting .BOARD_OUTLINE
+ FILE_OUTLINE, // board outline has been read; most sections can be accepted
+ FILE_PLACEMENT, // placement has been read; no further sections can be accepted
+ FILE_INVALID, // file is invalid
+ FILE_ERROR // other errors while processing the file
+ };
+
+ /**
+ * ENUM IDF_VERSION
+ * represents the supported IDF versions (3.0 and 2.0 ONLY)
+ */
+ enum IDF_VERSION
+ {
+ IDF_V2 = 0, // version 2 has read support only; files written as IDFv3
+ IDF_V3 // version 3 has full read/write support
+ };
+
+ /**
+ * ENUM KEY_OWNER
+ * represents the type of CAD which has ownership an object
+ */
+ enum KEY_OWNER
+ {
+ UNOWNED = 0, //< either MCAD or ECAD may modify a feature
+ MCAD, //< only MCAD may modify a feature
+ ECAD //< only ECAD may modify a feature
+ };
+
+ /**
+ * ENUM KEY_HOLETYPE
+ * represents the purpose of an IDF hole
+ */
+ enum KEY_HOLETYPE
+ {
+ PIN = 0, //< drill hole is for a pin
+ VIA, //< drill hole is for a via
+ MTG, //< drill hole is for mounting
+ TOOL, //< drill hole is for tooling
+ OTHER //< user has specified a custom type
+ };
+
+ /**
+ * ENUM KEY_PLATING
+ * represents the plating condition of a hole
+ */
+ enum KEY_PLATING
+ {
+ PTH = 0, //< Plate-Through Hole
+ NPTH //< Non-Plate-Through Hole
+ };
+
+ /**
+ * ENUM KEY_REFDES
+ * represents a component's Reference Designator
+ */
+ enum KEY_REFDES
+ {
+ BOARD = 0, //< feature is associated with the board
+ NOREFDES, //< feature is associated with a component with no RefDes
+ PANEL, //< feature is associated with an IDF panel
+ REFDES //< reference designator as assigned by the CAD software
+ };
+
+ /**
+ * ENUM CAD_TYPE
+ * represents the class of CAD program which is opening or modifying a file
+ */
+ enum CAD_TYPE
+ {
+ CAD_ELEC = 0, //< An Electrical CAD is opening/modifying the file
+ CAD_MECH, //< A Mechanical CAD is opening/modifying the file
+ CAD_INVALID
+ };
+
+ /**
+ * ENUM IDF_LAYER
+ * represents the various IDF layer classes and groupings
+ */
+ enum IDF_LAYER
+ {
+ LYR_TOP = 0,
+ LYR_BOTTOM,
+ LYR_BOTH,
+ LYR_INNER,
+ LYR_ALL,
+ LYR_INVALID
+ };
+
+ /**
+ * ENUM OUTLINE_TYPE
+ * identifies the class of outline
+ */
+ enum OUTLINE_TYPE
+ {
+ OTLN_BOARD = 0,
+ OTLN_OTHER,
+ OTLN_PLACE,
+ OTLN_ROUTE,
+ OTLN_PLACE_KEEPOUT,
+ OTLN_ROUTE_KEEPOUT,
+ OTLN_VIA_KEEPOUT,
+ OTLN_GROUP_PLACE,
+ OTLN_COMPONENT,
+ OTLN_INVALID
+ };
+
+ /**
+ * ENUM COMP_TYPE
+ * identifies whether a component is a mechanical or electrical part
+ */
+ enum COMP_TYPE
+ {
+ COMP_ELEC = 0, //< Component library object is an electrical part
+ COMP_MECH, //< Component library object is a mechanical part
+ COMP_INVALID
+ };
+
+ /**
+ * ENUM IDF_UNIT
+ * represents the native unit of the board and of component outlines
+ */
+ enum IDF_UNIT
+ {
+ UNIT_MM = 0, //< Units in the file are in millimeters
+ UNIT_THOU, //< Units in the file are in mils (aka thou)
+ UNIT_TNM, //< Deprecated Ten Nanometer Units from IDFv2
+ UNIT_INVALID
+ };
+
+ /**
+ * ENUM IDF_PLACEMENT
+ * represents the placement status of a component
+ */
+ enum IDF_PLACEMENT
+ {
+ PS_UNPLACED = 0, //< component location on the board has not been specified
+ PS_PLACED, //< component location has been specified and may be modified by ECAD or MCAD
+ PS_MCAD, //< component location has been specified and may only be modified by MCAD
+ PS_ECAD, //< component location has been specified and may only be modified by ECAD
+ PS_INVALID
+ };
+
+ /**
+ * Function CalcAngleRad
+ * calculates the angle (radians) between the horizon and the segment aStartPoint to aEndPoint
+ *
+ * @param aStartPoint is the start point of a line segment
+ * @param aEndPoint is the end point of a line segment
+ *
+ * @return double: the angle in radians
+ */
+ double CalcAngleRad( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint );
+
+
+ /**
+ * Function CalcAngleDeg
+ * calculates the angle (degrees) between the horizon and the segment aStartPoint to aEndPoint
+ *
+ * @param aStartPoint is the start point of a line segment
+ * @param aEndPoint is the end point of a line segment
+ *
+ * @return double: the angle in degrees
+ */
+ double CalcAngleDeg( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint );
+
+ /**
+ * Function GetOutline
+ * takes contiguous elements from 'aLines' and stuffs them into 'aOutline'; elements put
+ * into the outline are deleted from aLines. This function is useful for sorting the jumbled
+ * mess of line segments and arcs which represent a board outline and cutouts in KiCad.
+ * The function will determine which segment element within aLines contains the leftmost
+ * point and retrieve the outline of which that segment is part.
+ *
+ * @param aLines (input/output) is a list of IDF segments which comprise an outline and
+ * cutouts.
+ * @param aOutline (output) is the ordered set of segments
+ */
+ void GetOutline( std::list<IDF_SEGMENT*>& aLines,
+ IDF_OUTLINE& aOutline );
+
+#ifdef DEBUG_IDF
+ // prints out segment information for debug purposes
+ void PrintSeg( IDF_SEGMENT* aSegment );
+#endif
+}
+
+
+/**
+ * Class IDF_NOTE
+ * represents an entry in the NOTE section of an IDF file
+ */
+class IDF_NOTE
+{
+friend class IDF3_BOARD;
+private:
+ std::string text; // note text as per IDFv3
+ double xpos; // text X position as per IDFv3
+ double ypos; // text Y position as per IDFv3
+ double height; // text height as per IDFv3
+ double length; // text length as per IDFv3
+
+ /**
+ * Function readNote
+ * reads a note entry from an IDFv3 file
+ *
+ * @param aBoardFile is an open BOARD file; the file position must be set to the start of a NOTE entry
+ * @param aBoardState is the parser's current state value
+ * @param aBoardUnit is the BOARD file's native units (MM or THOU)
+ *
+ * @return bool: true if a note item was read, false otherwise. In case of unrecoverable errors
+ * an exception is thrown
+ */
+ bool readNote( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState, IDF3::IDF_UNIT aBoardUnit );
+
+ /**
+ * Function writeNote
+ * writes a note entry to an IDFv3 file
+ *
+ * @param aBoardFile is an open BOARD file; the file position must be within a NOTE section
+ * @param aBoardUnit is the BOARD file's native units (MM or THOU)
+ *
+ * @return bool: true if the item was successfully written, false otherwise. In case of
+ * unrecoverable errors an exception is thrown
+ */
+ bool writeNote( std::ofstream& aBoardFile, IDF3::IDF_UNIT aBoardUnit );
+
+public:
+ IDF_NOTE();
+
+ /**
+ * Function SetText
+ * sets the text to be stored as a NOTE entry
+ */
+ void SetText( const std::string& aText );
+
+ /**
+ * Function SetPosition
+ * sets the position (mm) of the NOTE entry
+ */
+ void SetPosition( double aXpos, double aYpos );
+
+ /**
+ * Function SetSize
+ * sets the height and length (mm) of the NOTE entry
+ */
+ void SetSize( double aHeight, double aLength );
+
+ /**
+ * Function GetText
+ * returns the string stored in the note entry
+ */
+ const std::string& GetText( void );
+
+ /**
+ * Function GetText
+ * returns the position (mm) of the note entry
+ */
+ void GetPosition( double& aXpos, double& aYpos );
+
+ /**
+ * Function GetText
+ * returns the height and length (mm) of the note entry
+ */
+ void GetSize( double& aHeight, double& aLength );
+};
+
+
+/**
+ * @Class IDF_DRILL_DATA
+ * contains information describing a drilled hole and is responsible for
+ * writing this information to a file in compliance with the IDFv3 specification.
+ */
+class IDF_DRILL_DATA
+{
+friend class IDF3_BOARD;
+friend class IDF3_COMPONENT;
+private:
+ double dia;
+ double x;
+ double y;
+ IDF3::KEY_PLATING plating;
+ IDF3::KEY_REFDES kref;
+ IDF3::KEY_HOLETYPE khole;
+ std::string refdes;
+ std::string holetype;
+ IDF3::KEY_OWNER owner;
+
+ /**
+ * Function read
+ * read a drill entry from an IDFv3 file
+ *
+ * @param aBoardFile is an open IDFv3 file; the file position must be within the DRILLED_HOLES section
+ * @param aBoardUnit is the board file's native unit (MM or THOU)
+ * @param aBoardState is the state value of the parser
+ *
+ * @return bool: true if data was successfully read, otherwise false. In case of an
+ * unrecoverable error an exception is thrown
+ */
+ bool read( std::ifstream& aBoardFile, IDF3::IDF_UNIT aBoardUnit, IDF3::FILE_STATE aBoardState,
+ IDF3::IDF_VERSION aIdfVersion );
+
+ /**
+ * Function write
+ * writes a single line representing a hole within a .DRILLED_HOLES section
+ * In case of an unrecoverable error an exception is thrown.
+ *
+ * @param aBoardFile is an open BOARD file
+ * @param aBoardUnit is the native unit of the output file
+ */
+ void write( std::ofstream& aBoardFile, IDF3::IDF_UNIT aBoardUnit );
+
+public:
+ /**
+ * Constructor IDF_DRILL_DATA
+ * creates an empty drill entry which can be populated by the
+ * read() function
+ */
+ IDF_DRILL_DATA();
+
+ /**
+ * Constructor IDF_DRILL_DATA
+ * creates a drill entry with information compliant with the
+ * IDFv3 specifications.
+ * @param aDrillDia : drill diameter
+ * @param aPosX : X coordinate of the drill center
+ * @param aPosY : Y coordinate of the drill center
+ * @param aPlating : flag, PTH or NPTH
+ * @param aRefDes : component Reference Designator
+ * @param aHoleType : purpose of hole
+ * @param aOwner : one of MCAD, ECAD, UNOWNED
+ */
+ IDF_DRILL_DATA( double aDrillDia, double aPosX, double aPosY,
+ IDF3::KEY_PLATING aPlating,
+ const std::string aRefDes,
+ const std::string aHoleType,
+ IDF3::KEY_OWNER aOwner );
+
+ /**
+ * Function Matches
+ * returns true if the given drill diameter and location
+ * matches the diameter and location of this IDF_DRILL_DATA object
+ *
+ * @param aDrillDia is the drill diameter (mm)
+ * @param aPosX is the X position (mm) of the drilled hole
+ * @param aPosY is the Y position (mm) of the drilled hole
+ *
+ * @return bool: true if the diameter and position match this object
+ */
+ bool Matches( double aDrillDia, double aPosX, double aPosY );
+
+ /**
+ * Function GettDrillDia
+ * returns the drill diameter in mm
+ */
+ double GetDrillDia();
+
+ /**
+ * Function GettDrillXPos
+ * returns the drill's X position in mm
+ */
+ double GetDrillXPos();
+
+ /**
+ * Function GettDrillYPos
+ * returns the drill's Y position in mm
+ */
+ double GetDrillYPos();
+
+ /**
+ * Function GetDrillPlating
+ * returns the plating value (PTH, NPTH)
+ */
+ IDF3::KEY_PLATING GetDrillPlating();
+
+ /**
+ * Function GetDrillRefDes
+ * returns the reference designator of the hole; this
+ * may be a component reference designator, BOARD, or
+ * NOREFDES as per IDFv3.
+ */
+ const std::string& GetDrillRefDes();
+
+ /**
+ * Function GetDrillHoleType
+ * returns the classification of the hole; this may be one of
+ * PIN, VIA, MTG, TOOL, or a user-specified string
+ */
+ const std::string& GetDrillHoleType();
+
+ IDF3::KEY_OWNER GetDrillOwner( void )
+ {
+ return owner;
+ }
+};
+
+
+/**
+ * @Class IDF_POINT
+ * represents a point as used by the various IDF related classes
+ */
+class IDF_POINT
+{
+public:
+ double x; // < X coordinate
+ double y; // < Y coordinate
+
+ IDF_POINT()
+ {
+ x = 0.0;
+ y = 0.0;
+ }
+
+ /**
+ * Function Matches()
+ * returns true if the given coordinate point is within the given radius
+ * of the point.
+ *
+ * @param aPoint : coordinates of the point being compared
+ * @param aRadius : radius (mm) within which the points are considered the same
+ *
+ * @return bool: true if this point matches the given point
+ */
+ bool Matches( const IDF_POINT& aPoint, double aRadius = 1e-5 );
+
+ /**
+ * Function CalcDistance()
+ * returns the Euclidean distance between this point and the given point
+ *
+ * @param aPoint : coordinates of the point whose distance is to be determined
+ *
+ * @return double: distance between this point and aPoint
+ */
+ double CalcDistance( const IDF_POINT& aPoint ) const;
+};
+
+
+/**
+ * @Class IDF_SEGMENT
+ * represents a geometry segment as used in IDFv3 outlines; it may be any of
+ * an arc, line segment, or circle
+ */
+class IDF_SEGMENT
+{
+private:
+ /**
+ * Function CalcCenterAndRadius()
+ * Calculates the center, radius, and angle between center and start point given the
+ * IDF compliant points and included angle.
+ *
+ * @var startPoint, @var endPoint, and @var angle must be set prior as per IDFv3
+ */
+ void CalcCenterAndRadius( void );
+
+public:
+ IDF_POINT startPoint; ///< starting point coordinates in mm
+ IDF_POINT endPoint; ///< end point coordinates in mm
+ IDF_POINT center; ///< center of an arc or circle; internally calculated and not to be set by the user
+ double angle; ///< included angle (degrees) according to IDFv3 specification
+ double offsetAngle; ///< angle between center and start of arc; internally calculated
+ double radius; ///< radius of the arc or circle; internally calculated
+
+ /**
+ * Constructor IDF_SEGMENT
+ * initializes the internal variables
+ */
+ IDF_SEGMENT();
+
+ /**
+ * Function IDF_SEGMENT
+ * creates a straight segment
+ */
+ IDF_SEGMENT( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint );
+
+ /**
+ * Constructor IDF_SEGMENT
+ * creates a straight segment, arc, or circle depending on the angle
+ *
+ * @param aStartPoint : start point (center if using KiCad convention, otherwise IDF convention)
+ * @param aEndPoint : end point (start of arc if using KiCad convention, otherwise IDF convention)
+ * @param aAngle : included angle; the KiCad convention is equivalent to the IDF convention
+ * @param fromKicad : set true if we need to convert from KiCad to IDF convention
+ */
+ IDF_SEGMENT( const IDF_POINT& aStartPoint,
+ const IDF_POINT& aEndPoint,
+ double aAngle,
+ bool aFromKicad );
+
+ /**
+ * Function MatchesStart
+ * returns true if the given coordinate is within a radius 'rad'
+ * of the start point.
+ *
+ * @param aPoint : coordinates of the point (mm) being compared
+ * @param aRadius : radius (mm) within which the points are considered the same
+ *
+ * @return bool: true if the given point matches the start point of this segment
+ */
+ bool MatchesStart( const IDF_POINT& aPoint, double aRadius = 1e-3 );
+
+ /**
+ * Function MatchesEnd
+ * returns true if the given coordinate is within a radius 'rad'
+ * of the end point.
+ *
+ * @param aPoint : coordinates (mm) of the point being compared
+ * @param aRadius : radius (mm) within which the points are considered the same
+ *
+ * @return bool: true if the given point matches the end point of this segment
+ */
+ bool MatchesEnd( const IDF_POINT& aPoint, double aRadius = 1e-3 );
+
+ /**
+ * Function IsCircle
+ * returns true if this segment is a circle
+ */
+ bool IsCircle( void );
+
+ /**
+ * Function GetMinX()
+ * returns the minimum X coordinate of this segment
+ */
+ double GetMinX( void );
+
+ /**
+ * Function SwapEnds()
+ * Swaps the start and end points and alters internal
+ * variables as necessary for arcs
+ */
+ void SwapEnds( void );
+};
+
+
+/**
+ * @Class IDF_OUTLINE
+ * contains segment and winding information for an IDF outline
+ */
+class IDF_OUTLINE
+{
+private:
+ double dir; // accumulator to help determine winding direction
+ std::list<IDF_SEGMENT*> outline; // sequential segments comprising an outline
+
+public:
+ IDF_OUTLINE() { dir = 0.0; }
+ ~IDF_OUTLINE() { Clear(); }
+
+ /**
+ * Function IsCCW
+ * returns true if the current list of points represents a counterclockwise winding
+ */
+ bool IsCCW( void );
+
+ /**
+ * Function IsCircle
+ * returns true if this outline is a circle
+ */
+ bool IsCircle( void );
+
+ /**
+ * Function Clear
+ * clears the internal list of outline segments
+ */
+ void Clear( void )
+ {
+ dir = 0.0;
+
+ while( !outline.empty() )
+ {
+ delete outline.front();
+ outline.pop_front();
+ }
+ }
+
+ /**
+ * Function size
+ * returns the size of the internal segment list
+ */
+ size_t size( void )
+ {
+ return outline.size();
+ }
+
+ /**
+ * Function empty
+ * returns true if the internal segment list is empty
+ */
+ bool empty( void )
+ {
+ return outline.empty();
+ }
+
+ /**
+ * Function front
+ * returns the front() iterator of the internal segment list
+ */
+ IDF_SEGMENT*& front( void )
+ {
+ return outline.front();
+ }
+
+ /**
+ * Function back
+ * returns the back() iterator of the internal segment list
+ */
+ IDF_SEGMENT*& back( void )
+ {
+ return outline.back();
+ }
+
+ /**
+ * Function begin
+ * returns the begin() iterator of the internal segment list
+ */
+ std::list<IDF_SEGMENT*>::iterator begin( void )
+ {
+ return outline.begin();
+ }
+
+ /**
+ * Function end
+ * returns the end() iterator of the internal segment list
+ */
+ std::list<IDF_SEGMENT*>::iterator end( void )
+ {
+ return outline.end();
+ }
+
+ /**
+ * Function push
+ * adds a segment to the internal segment list; segments must be added
+ * in order so that startPoint[N] == endPoint[N - 1]
+ *
+ * @param item is a pointer to the segment to add to the outline
+ *
+ * @return bool: true if the segment was added, otherwise false
+ * (outline restrictions have been violated)
+ */
+ bool push( IDF_SEGMENT* item );
+};
+
+#endif // IDF_COMMON_H
diff --git a/utils/idftools/idf_cylinder.cpp b/utils/idftools/idf_cylinder.cpp
new file mode 100644
index 0000000..e7870d4
--- /dev/null
+++ b/utils/idftools/idf_cylinder.cpp
@@ -0,0 +1,681 @@
+/*
+ * 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 creates an outline for a horizontal or vertically
+ * oriented axial or radial leaded cylinder with dimensions based
+ * on the user's input.
+ */
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <sstream>
+#include <cmath>
+#include <cstdio>
+#include <list>
+#include <utility>
+#include <clocale>
+
+using namespace std;
+
+void make_vcyl( bool inch, bool axial, double dia, double length,
+ double z, double wireDia );
+
+void make_hcyl( bool inch, bool axial, double dia, double length,
+ double z, double wireDia );
+
+void writeAxialCyl( FILE* fp, bool inch, double dia, double length, double wireDia, double pitch );
+
+void writeRadialCyl( FILE* fp, bool inch, double dia, double length, double wireDia,
+ double pitch, double lead );
+
+int main( int argc, char **argv )
+{
+ // IDF implicitly requires the C locale
+ setlocale( LC_ALL, "C" );
+
+ if( argc == 1 )
+ {
+ cout << "idfcyl: This program generates an outline for a cylindrical component.\n";
+ cout << " The cylinder may be horizontal or vertical.\n";
+ cout << " A horizontal cylinder may have wires at one or both ends.\n";
+ cout << " A vertical cylinder may have at most one wire which may be\n";
+ cout << " placed on the left or right side.\n\n";
+ cout << "Input:\n";
+ cout << " Unit: mm, in (millimeters or inches)\n";
+ cout << " Orientation: V (vertical)\n";
+ cout << " Lead type: X, R (axial, radial)\n";
+ cout << " Diameter of body\n";
+ cout << " Length of body\n";
+ cout << " Board offset\n";
+ cout << " * Wire diameter\n";
+ cout << " * Pitch\n";
+ cout << " ** Wire side: L, R (left, right)\n";
+ cout << " *** Lead length\n";
+ cout << " File name (must end in *.idf)\n\n";
+ cout << " NOTES:\n";
+ cout << " * only required for horizontal orientation or\n";
+ cout << " vertical orientation with axial leads\n\n";
+ cout << " ** only required for vertical orientation with axial leads\n\n";
+ cout << " *** only required for horizontal orientation with radial leads\n\n";
+ }
+
+ char orientation = '\0';
+ bool inch = false; // default mm
+ double dia = 0.0;
+ double length = 0.0;
+ double extraZ = 0.0;
+ double wireDia = 0.0;
+ bool axial = false;
+
+ stringstream tstr;
+ string line;
+
+ line.clear();
+ while( line.compare( "mm" ) && line.compare( "in" ) )
+ {
+ cout << "* Units (mm,in): ";
+ line.clear();
+ std::getline( cin, line );
+ }
+
+ if( line.compare( "mm" ) )
+ inch = true;
+
+ line.clear();
+ while( line.compare( "H" ) && line.compare( "h" )
+ && line.compare( "V" ) && line.compare( "v" ) )
+ {
+ cout << "* Orientation (H,V): ";
+ line.clear();
+ std::getline( cin, line );
+ }
+
+ if( line.compare( "H" ) && line.compare( "h" ) )
+ orientation = 'v';
+ else
+ orientation = 'h';
+
+ bool ok = false;
+
+ while( !ok )
+ {
+ cout << "* Axial or Radial (X,R): ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ if( !line.compare( "x" ) || !line.compare( "X" ) )
+ {
+ axial = true;
+ ok = true;
+ }
+ else if( !line.compare( "r" ) || !line.compare( "R" ) )
+ {
+ axial = false;
+ ok = true;
+ }
+ }
+
+ // cylinder dimensions
+ ok = false;
+ while( !ok )
+ {
+ cout << "* Diameter: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> dia;
+ if( !tstr.fail() && dia > 0.0 )
+ ok = true;
+ }
+
+ ok = false;
+ while( !ok )
+ {
+ cout << "* Length: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> length;
+ if( !tstr.fail() && length > 0.0 )
+ ok = true;
+ }
+
+ ok = false;
+ while( !ok )
+ {
+ cout << "* Board offset: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> extraZ;
+ if( !tstr.fail() && extraZ >= 0.0 )
+ ok = true;
+ }
+
+ ok = false;
+ while( ( axial || orientation == 'h' ) && !ok )
+ {
+ cout << "* Wire diameter: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> wireDia;
+ if( !tstr.fail() && wireDia > 0.0 )
+ {
+ if( wireDia < dia )
+ ok = true;
+ else
+ cout << "* WARNING: wire diameter must be < cylinder diameter\n";
+ }
+ }
+
+ switch( orientation )
+ {
+ case 'v':
+ make_vcyl( inch, axial, dia, length, extraZ, wireDia );
+ break;
+ case 'h':
+ make_hcyl( inch, axial, dia, length, extraZ, wireDia );
+ break;
+ default:
+ break;
+ }
+
+ setlocale( LC_ALL, "" );
+ return 0;
+}
+
+
+void make_vcyl( bool inch, bool axial, double dia, double length,
+ double z, double wireDia )
+{
+ bool ok = false;
+ bool left = false;
+ stringstream tstr;
+ string line;
+
+ double pitch = 0.0;
+
+ while( axial && !ok )
+ {
+ cout << "* Pitch: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> pitch;
+ if( !tstr.fail() && pitch > 0.0 )
+ {
+ if( (pitch - wireDia) <= (dia / 2.0) )
+ {
+ cout << "* WARNING: Pitch must be > dia/2 + wireDia\n";
+ }
+ else
+ {
+ ok = true;
+ }
+ }
+ }
+
+ ok = false;
+ while( axial && !ok )
+ {
+ cout << "* Pin side (L,R): ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ if( !line.compare( "l" ) || !line.compare( "L" ) )
+ {
+ left = true;
+ ok = true;
+ }
+ else if( !line.compare( "r" ) || !line.compare( "R" ) )
+ ok = true;
+ }
+
+ line.clear();
+ while( line.empty() || line.find( ".idf" ) == string::npos )
+ {
+ cout << "* File name (*.idf): ";
+
+ line.clear();
+ std::getline( cin, line );
+ }
+
+ FILE* fp = fopen( line.c_str(), "w" );
+
+ if( !fp )
+ {
+ cerr << "Could not open output file: " << line << "\n";
+ return;
+ }
+
+ fprintf( fp, "# cylindrical outline, vertical, " );
+
+ if( !axial )
+ fprintf( fp, "radial leads\n" );
+ else
+ fprintf( fp, "axial lead on %s\n", left ? "left" : "right" );
+
+ fprintf( fp, "# file: \"%s\"\n", line.c_str() );
+
+ if( inch )
+ {
+ fprintf( fp, "# dia: %d THOU\n", (int) (dia * 1000) );
+ fprintf( fp, "# length: %d THOU\n", (int) (length * 1000) );
+ fprintf( fp, "# board offset: %d THOU\n", (int) (z * 1000) );
+
+ if( axial )
+ {
+ fprintf( fp, "# wire dia: %d THOU\n", (int) (wireDia * 1000) );
+ fprintf( fp, "# pitch: %d THOU\n", (int) (pitch * 1000) );
+ }
+ }
+ else
+ {
+ fprintf( fp, "# dia: %.3f mm\n", dia );
+ fprintf( fp, "# length: %.3f mm\n", length );
+ fprintf( fp, "# board offset: %.3f mm\n", z );
+
+ if( axial )
+ {
+ fprintf( fp, "# wire dia: %.3f mm\n", wireDia );
+ fprintf( fp, "# pitch: %.3f mm\n", pitch );
+ }
+ }
+
+ fprintf( fp, ".ELECTRICAL\n" );
+
+ if( !axial )
+ {
+ fprintf( fp, "\"CYLV_%s_RAD\" \"D%.3f_H%.3f_Z%.3f\" ", inch ? "IN" : "MM",
+ dia, length, z );
+ }
+ else
+ {
+ fprintf( fp, "\"CYLV_%s_AX%s\" \"D%.3f_H%.3f_Z%.3f_WD%.3f_P%.3f\" ", inch ? "IN" : "MM",
+ left ? "L" : "R", dia, length, z, wireDia, pitch );
+ }
+
+ if( inch )
+ fprintf( fp, "THOU %d\n", (int) ((length + z) * 1000) );
+ else
+ fprintf( fp, "MM %.3f\n", length + z );
+
+ if( !axial )
+ {
+ fprintf( fp, "0 0 0 0\n" );
+
+ if( inch )
+ fprintf( fp, "0 %d 0 360\n", (int) (dia * 500) );
+ else
+ fprintf( fp, "0 %.3f 0 360\n", dia / 2.0 );
+
+ fprintf( fp, ".END_ELECTRICAL\n" );
+ fclose( fp );
+ return;
+ }
+
+ double px[4], py[4];
+
+ // points are:
+ // [0] = upper point on cylinder perimeter
+ // [1] = lower point on cylinder perimeter
+ // [2] = point beneath wire center
+ // [3] = point above wire center
+
+ if( inch )
+ {
+ dia *= 1000.0;
+ pitch *= 1000.0;
+ wireDia *= 1000.0;
+ }
+
+ double ang = asin( wireDia / dia );
+ px[0] = dia * cos( ang ) / 2.0 - pitch / 2.0;
+ px[1] = px[0];
+ px[2] = pitch / 2.0;
+ px[3] = px[2];
+
+ py[0] = wireDia / 2.0;
+ py[1] = -py[0];
+ py[2] = py[1];
+ py[3] = py[0];
+
+ char li = '0';
+
+ double fullAng = 360.0;
+
+ if( left )
+ {
+ li = '1';
+ fullAng = -360.0;
+ for( int i = 0; i < 4; ++i ) px[i] = -px[i];
+ }
+
+
+ if( inch )
+ {
+ fprintf( fp, "%c %d %d 0\n", li, (int) px[0], (int) py[0] );
+ fprintf( fp, "%c %d %d %.3f\n", li, (int) px[1], (int) py[1],
+ fullAng * ( 1 - ang / M_PI ) );
+ fprintf( fp, "%c %d %d 0\n", li, (int) px[2], (int) py[2] );
+ fprintf( fp, "%c %d %d %s\n", li, (int) px[3], (int) py[3],
+ left ? "-180" : "180" );
+ fprintf( fp, "%c %d %d 0\n", li, (int) px[0], (int) py[0] );
+ }
+ else
+ {
+ fprintf( fp, "%c %.3f %.3f 0\n", li, px[0], py[0] );
+ fprintf( fp, "%c %.3f %.3f %.3f\n", li, px[1], py[1], fullAng * ( 1 - ang / M_PI ) );
+ fprintf( fp, "%c %.3f %.3f 0\n", li, px[2], py[2] );
+ fprintf( fp, "%c %.3f %.3f %s\n", li, px[3], py[3],
+ left ? "-180" : "180" );
+ fprintf( fp, "%c %.3f %.3f 0\n", li, px[0], py[0] );
+ }
+
+ fprintf( fp, ".END_ELECTRICAL\n" );
+ fclose( fp );
+ return;
+}
+
+
+void make_hcyl( bool inch, bool axial, double dia, double length,
+ double z, double wireDia )
+{
+ stringstream tstr;
+ string line;
+
+ double pitch = 0.0;
+ double lead = 0.0; // lead length for radial leads
+
+ bool ok = false;
+ while( !ok )
+ {
+ if( axial )
+ cout << "* Axial pitch: ";
+ else
+ cout << "* Radial pitch: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> pitch;
+ if( !tstr.fail() && pitch > 0.0 )
+ {
+ if( axial )
+ {
+ if( (pitch - wireDia) <= length )
+ {
+ cout << "* WARNING: Axial pitch must be > length + wireDia\n";
+ }
+ else
+ {
+ ok = true;
+ }
+ }
+ else
+ {
+ if( (pitch + wireDia) >= dia )
+ {
+ cout << "* WARNING: Radial pitch must be < dia - wireDia\n";
+ }
+ else if( pitch <= wireDia )
+ {
+ cout << "* WARNING: Radial pitch must be > wireDia\n";
+ }
+ else
+ {
+ ok = true;
+ }
+ }
+ }
+ }
+
+ ok = false;
+ while( !axial && !ok )
+ {
+ cout << "* Lead length: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> lead;
+ if( !tstr.fail() && lead > 0.0 )
+ {
+ if( lead < wireDia )
+ cout << "* WARNING: lead length must be >= wireDia\n";
+ else
+ ok = true;
+ }
+ }
+
+ line.clear();
+ while( line.empty() || line.find( ".idf" ) == string::npos )
+ {
+ cout << "* File name (*.idf): ";
+
+ line.clear();
+ std::getline( cin, line );
+ }
+
+ FILE* fp = fopen( line.c_str(), "w" );
+
+ if( !fp )
+ {
+ cerr << "Could not open output file: " << line << "\n";
+ return;
+ }
+
+ fprintf( fp, "# cylindrical outline, horiz., " );
+
+ fprintf( fp, "%s pins\n", axial ? "axial" : "radial" );
+
+ fprintf( fp, "# file: \"%s\"\n", line.c_str() );
+
+ if( inch )
+ {
+ fprintf( fp, "# dia: %d THOU\n", (int) (dia * 1000) );
+ fprintf( fp, "# length: %d THOU\n", (int) (length * 1000) );
+ fprintf( fp, "# extra height: %d THOU\n", (int) (z * 1000) );
+ fprintf( fp, "# wire dia: %d THOU\n", (int) (wireDia * 1000) );
+ fprintf( fp, "# pitch: %d THOU\n", (int) (pitch * 1000) );
+ if( !axial )
+ fprintf( fp, "# lead: %d THOU\n", (int) (lead * 1000) );
+ }
+ else
+ {
+ fprintf( fp, "# dia: %.3f mm\n", dia );
+ fprintf( fp, "# length: %.3f mm\n", length );
+ fprintf( fp, "# extra height: %.3f mm\n", z );
+ fprintf( fp, "# wire dia: %.3f mm\n", wireDia );
+ fprintf( fp, "# pitch: %.3f mm\n", pitch );
+ if( !axial )
+ fprintf( fp, "# lead: %.3f mm\n", lead );
+ }
+
+ fprintf( fp, ".ELECTRICAL\n" );
+
+ if( axial )
+ {
+ fprintf( fp, "\"CYLH_%s_AXI\" \"D%.3f_H%.3f_Z%.3f_WD%.3f_P%.3f\" ",
+ inch ? "IN" : "MM", dia, length, z, wireDia, pitch );
+ }
+ else
+ {
+ fprintf( fp, "\"CYLH_%s_RAD\" \"D%.3f_H%.3f_Z%.3f_WD%.3f_P%.3f_L%.3f\" ",
+ inch ? "IN" : "MM", dia, length, z, wireDia, pitch, lead );
+ }
+
+ if( inch )
+ {
+ fprintf( fp, "THOU %d\n", (int) ((dia + z) * 1000) );
+ dia *= 1000.0;
+ length *= 1000.0;
+ wireDia *= 1000.0;
+ pitch *= 1000.0;
+ if( !axial )
+ lead *= 1000.0;
+ }
+ else
+ {
+ fprintf( fp, "MM %.3f\n", dia + z );
+ }
+
+ if( axial )
+ writeAxialCyl( fp, inch, dia, length, wireDia, pitch );
+ else
+ writeRadialCyl( fp, inch, dia, length, wireDia, pitch, lead );
+
+ fprintf( fp, ".END_ELECTRICAL\n" );
+ fclose( fp );
+ return;
+ return;
+}
+
+void writeAxialCyl( FILE* fp, bool inch, double dia, double length,
+ double wireDia, double pitch )
+{
+ double x1, y1;
+ double x2, y2;
+
+ x1 = -length / 2.0;
+ x2 = -pitch / 2.0;
+ y1 = dia / 2.0;
+ y2 = wireDia / 2.0;
+
+ if( inch )
+ {
+ fprintf( fp, "0 %d %d 0\n", (int) x1, (int) y1 );
+ fprintf( fp, "0 %d %d 0\n", (int) x1, (int) y2 );
+ fprintf( fp, "0 %d %d 0\n", (int) x2, (int) y2 );
+ fprintf( fp, "0 %d %d 180\n", (int) x2, (int) -y2 );
+ fprintf( fp, "0 %d %d 0\n", (int) x1, (int) -y2 );
+ fprintf( fp, "0 %d %d 0\n", (int) x1, (int) -y1 );
+ fprintf( fp, "0 %d %d 0\n", (int) -x1, (int) -y1 );
+ fprintf( fp, "0 %d %d 0\n", (int) -x1, (int) -y2 );
+ fprintf( fp, "0 %d %d 0\n", (int) -x2, (int) -y2 );
+ fprintf( fp, "0 %d %d 180\n", (int) -x2, (int) y2 );
+ fprintf( fp, "0 %d %d 0\n", (int) -x1, (int) y2 );
+ fprintf( fp, "0 %d %d 0\n", (int) -x1, (int) y1 );
+ fprintf( fp, "0 %d %d 0\n", (int) x1, (int) y1 );
+ }
+ else
+ {
+ fprintf( fp, "0 %.3f %.3f 0\n", x1, y1 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x1, y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x2, y2 );
+ fprintf( fp, "0 %.3f %.3f 180\n", x2, -y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x1, -y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x1, -y1 );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x1, -y1 );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x1, -y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x2, -y2 );
+ fprintf( fp, "0 %.3f %.3f 180\n", -x2, y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x1, y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x1, y1 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x1, y1 );
+ }
+
+ return;
+}
+
+void writeRadialCyl( FILE* fp, bool inch, double dia, double length,
+ double wireDia, double pitch, double lead )
+{
+ double x1, y1;
+ double x2, y2;
+ double x3;
+
+ // center is between the mounting holes
+ // which are on a horizontal line
+ y1 = lead + length;
+ y2 = lead;
+ x1 = dia / 2.0;
+ x2 = ( pitch + wireDia ) /2.0;
+ x3 = x2 - wireDia;
+
+ if( inch )
+ {
+ fprintf( fp, "0 %d %d 0\n", (int) -x1, (int) y1 );
+ fprintf( fp, "0 %d %d 0\n", (int) -x1, (int) y2 );
+ fprintf( fp, "0 %d %d 0\n", (int) -x2, (int) y2 );
+ fprintf( fp, "0 %d 0 0\n", (int) -x2 );
+ fprintf( fp, "0 %d 0 180\n", (int) -x3 );
+ fprintf( fp, "0 %d %d 0\n", (int) -x3, (int) y2 );
+ fprintf( fp, "0 %d %d 0\n", (int) x3, (int) y2 );
+ fprintf( fp, "0 %d 0 0\n", (int) x3 );
+ fprintf( fp, "0 %d 0 180\n", (int) x2 );
+ fprintf( fp, "0 %d %d 0\n", (int) x2, (int) y2 );
+ fprintf( fp, "0 %d %d 0\n", (int) x1, (int) y2 );
+ fprintf( fp, "0 %d %d 0\n", (int) x1, (int) y1 );
+ fprintf( fp, "0 %d %d 0\n", (int) -x1, (int) y1 );
+ }
+ else
+ {
+ fprintf( fp, "0 %.3f %.3f 0\n", -x1, y1 );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x1, y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x2, y2 );
+ fprintf( fp, "0 %.3f 0 0\n", -x2 );
+ fprintf( fp, "0 %.3f 0 180\n", -x3 );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x3, y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x3, y2 );
+ fprintf( fp, "0 %.3f 0 0\n", x3 );
+ fprintf( fp, "0 %.3f 0 180\n", x2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x2, y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x1, y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x1, y1 );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x1, y1 );
+ }
+
+ return;
+}
diff --git a/utils/idftools/idf_examples/Arduino_MEGA_2560-Rev3.emn b/utils/idftools/idf_examples/Arduino_MEGA_2560-Rev3.emn
new file mode 100644
index 0000000..4c21583
--- /dev/null
+++ b/utils/idftools/idf_examples/Arduino_MEGA_2560-Rev3.emn
@@ -0,0 +1,149 @@
+.HEADER
+BOARD_FILE 3.0 "Created by KiCad (2014-01-21 BZR 4629)-product" 2014/01/23.09:19:46 1
+"Arduino_MEGA_2560-Rev3.kicad_pcb" MM
+.END_HEADER
+
+.BOARD_OUTLINE ECAD
+1.60000
+0 246.20220 -25.67000 0
+0 247.20220 -26.67000 0
+0 344.26220 -26.67000 0
+0 345.26220 -25.67000 0
+0 345.26220 -25.40000 0
+0 347.80220 -22.86000 0
+0 347.80220 11.43000 0
+0 345.26220 13.97000 0
+0 345.26220 24.13000 0
+0 342.72220 26.67000 0
+0 247.20220 26.67000 0
+0 246.20220 25.67000 0
+0 246.20220 -25.67000 0
+.END_BOARD_OUTLINE
+
+.DRILLED_HOLES
+3.200 342.72220 -24.13000 NPTH "@HOLE0" MTG ECAD
+3.200 261.44220 24.13000 NPTH "@HOLE1" MTG ECAD
+3.200 336.37220 24.13000 NPTH "@HOLE2" MTG ECAD
+3.200 260.17220 -24.13000 NPTH "@HOLE3" MTG ECAD
+3.200 312.24220 8.89000 NPTH "@HOLE4" MTG ECAD
+3.200 312.24220 -19.05000 NPTH "@HOLE5" MTG ECAD
+0.950 309.82920 3.81000 PTH "ICSP" PIN ECAD
+0.950 312.36920 3.81000 PTH "ICSP" PIN ECAD
+0.950 309.82920 1.27000 PTH "ICSP" PIN ECAD
+0.950 312.36920 1.27000 PTH "ICSP" PIN ECAD
+0.950 309.82920 -1.27000 PTH "ICSP" PIN ECAD
+0.950 312.36920 -1.27000 PTH "ICSP" PIN ECAD
+0.850 309.70220 24.13000 PTH "PWML" PIN ECAD
+0.850 307.16220 24.13000 PTH "PWML" PIN ECAD
+0.850 304.62220 24.13000 PTH "PWML" PIN ECAD
+0.850 302.08220 24.13000 PTH "PWML" PIN ECAD
+0.850 299.54220 24.13000 PTH "PWML" PIN ECAD
+0.850 297.00220 24.13000 PTH "PWML" PIN ECAD
+0.850 294.46220 24.13000 PTH "PWML" PIN ECAD
+0.850 291.92220 24.13000 PTH "PWML" PIN ECAD
+1.400 254.88900 -23.36800 PTH "X1" PIN ECAD
+1.400 257.88620 -18.36420 PTH "X1" PIN ECAD
+1.400 251.89180 -18.36420 PTH "X1" PIN ECAD
+1.400 255.65100 -23.36800 PTH "X1" PIN ECAD
+1.400 254.12700 -23.36800 PTH "X1" PIN ECAD
+1.400 251.89180 -19.12620 PTH "X1" PIN ECAD
+1.400 251.89180 -17.60220 PTH "X1" PIN ECAD
+1.400 257.88620 -19.12620 PTH "X1" PIN ECAD
+1.400 257.88620 -17.60220 PTH "X1" PIN ECAD
+0.850 297.00220 -24.13000 PTH "ADCL" PIN ECAD
+0.850 299.54220 -24.13000 PTH "ADCL" PIN ECAD
+0.850 302.08220 -24.13000 PTH "ADCL" PIN ECAD
+0.850 304.62220 -24.13000 PTH "ADCL" PIN ECAD
+0.850 307.16220 -24.13000 PTH "ADCL" PIN ECAD
+0.850 309.70220 -24.13000 PTH "ADCL" PIN ECAD
+0.850 312.24220 -24.13000 PTH "ADCL" PIN ECAD
+0.850 314.78220 -24.13000 PTH "ADCL" PIN ECAD
+0.850 332.56220 24.13000 PTH "COMMUNICATION" PIN ECAD
+0.850 330.02220 24.13000 PTH "COMMUNICATION" PIN ECAD
+0.850 327.48220 24.13000 PTH "COMMUNICATION" PIN ECAD
+0.850 324.94220 24.13000 PTH "COMMUNICATION" PIN ECAD
+0.850 322.40220 24.13000 PTH "COMMUNICATION" PIN ECAD
+0.850 319.86220 24.13000 PTH "COMMUNICATION" PIN ECAD
+0.850 317.32220 24.13000 PTH "COMMUNICATION" PIN ECAD
+0.850 314.78220 24.13000 PTH "COMMUNICATION" PIN ECAD
+0.850 319.86220 -24.13000 PTH "ADCH" PIN ECAD
+0.850 322.40220 -24.13000 PTH "ADCH" PIN ECAD
+0.850 324.94220 -24.13000 PTH "ADCH" PIN ECAD
+0.850 327.48220 -24.13000 PTH "ADCH" PIN ECAD
+0.850 330.02220 -24.13000 PTH "ADCH" PIN ECAD
+0.850 332.56220 -24.13000 PTH "ADCH" PIN ECAD
+0.850 335.10220 -24.13000 PTH "ADCH" PIN ECAD
+0.850 337.64220 -24.13000 PTH "ADCH" PIN ECAD
+0.950 254.72220 10.18000 PTH "X2" PIN ECAD
+0.950 254.72220 12.68000 PTH "X2" PIN ECAD
+0.950 252.72220 12.68000 PTH "X2" PIN ECAD
+0.950 252.72220 10.18000 PTH "X2" PIN ECAD
+2.200 250.01220 17.43000 PTH "X2" PIN ECAD
+2.200 250.01220 5.43000 PTH "X2" PIN ECAD
+0.950 267.03020 20.82800 PTH "ICSP1" PIN ECAD
+0.950 267.03020 18.28800 PTH "ICSP1" PIN ECAD
+0.950 264.49020 20.82800 PTH "ICSP1" PIN ECAD
+0.950 264.49020 18.28800 PTH "ICSP1" PIN ECAD
+0.950 261.95020 20.82800 PTH "ICSP1" PIN ECAD
+0.950 261.95020 18.28800 PTH "ICSP1" PIN ECAD
+0.850 267.15720 -0.63500 PTH "Y2" PIN ECAD
+0.850 262.07720 -0.63500 PTH "Y2" PIN ECAD
+0.950 267.03020 13.20800 PTH "JP5" PIN ECAD
+0.950 264.49020 13.20800 PTH "JP5" PIN ECAD
+0.950 267.03020 15.74800 PTH "JP5" PIN ECAD
+0.950 264.49020 15.74800 PTH "JP5" PIN ECAD
+0.950 340.18220 24.13000 PTH "XIO" PIN ECAD
+0.950 342.72220 24.13000 PTH "XIO" PIN ECAD
+0.950 340.18220 21.59000 PTH "XIO" PIN ECAD
+0.950 342.72220 21.59000 PTH "XIO" PIN ECAD
+0.950 340.18220 19.05000 PTH "XIO" PIN ECAD
+0.950 342.72220 19.05000 PTH "XIO" PIN ECAD
+0.950 340.18220 16.51000 PTH "XIO" PIN ECAD
+0.950 342.72220 16.51000 PTH "XIO" PIN ECAD
+0.950 340.18220 13.97000 PTH "XIO" PIN ECAD
+0.950 342.72220 13.97000 PTH "XIO" PIN ECAD
+0.950 340.18220 11.43000 PTH "XIO" PIN ECAD
+0.950 342.72220 11.43000 PTH "XIO" PIN ECAD
+0.950 340.18220 8.89000 PTH "XIO" PIN ECAD
+0.950 342.72220 8.89000 PTH "XIO" PIN ECAD
+0.950 340.18220 6.35000 PTH "XIO" PIN ECAD
+0.950 342.72220 6.35000 PTH "XIO" PIN ECAD
+0.950 340.18220 3.81000 PTH "XIO" PIN ECAD
+0.950 342.72220 3.81000 PTH "XIO" PIN ECAD
+0.950 340.18220 1.27000 PTH "XIO" PIN ECAD
+0.950 342.72220 1.27000 PTH "XIO" PIN ECAD
+0.950 340.18220 -1.27000 PTH "XIO" PIN ECAD
+0.950 342.72220 -1.27000 PTH "XIO" PIN ECAD
+0.950 340.18220 -3.81000 PTH "XIO" PIN ECAD
+0.950 342.72220 -3.81000 PTH "XIO" PIN ECAD
+0.950 340.18220 -6.35000 PTH "XIO" PIN ECAD
+0.950 342.72220 -6.35000 PTH "XIO" PIN ECAD
+0.950 340.18220 -8.89000 PTH "XIO" PIN ECAD
+0.950 342.72220 -8.89000 PTH "XIO" PIN ECAD
+0.950 340.18220 -11.43000 PTH "XIO" PIN ECAD
+0.950 342.72220 -11.43000 PTH "XIO" PIN ECAD
+0.950 340.18220 -13.97000 PTH "XIO" PIN ECAD
+0.950 342.72220 -13.97000 PTH "XIO" PIN ECAD
+0.950 340.18220 -16.51000 PTH "XIO" PIN ECAD
+0.950 342.72220 -16.51000 PTH "XIO" PIN ECAD
+0.950 340.18220 -19.05000 PTH "XIO" PIN ECAD
+0.950 342.72220 -19.05000 PTH "XIO" PIN ECAD
+0.850 264.99820 24.13000 PTH "JP6" PIN ECAD
+0.850 267.53820 24.13000 PTH "JP6" PIN ECAD
+0.850 270.07820 24.13000 PTH "JP6" PIN ECAD
+0.850 272.61820 24.13000 PTH "JP6" PIN ECAD
+0.850 275.15820 24.13000 PTH "JP6" PIN ECAD
+0.850 277.69820 24.13000 PTH "JP6" PIN ECAD
+0.850 280.23820 24.13000 PTH "JP6" PIN ECAD
+0.850 282.77820 24.13000 PTH "JP6" PIN ECAD
+0.850 285.31820 24.13000 PTH "JP6" PIN ECAD
+0.850 287.85820 24.13000 PTH "JP6" PIN ECAD
+0.850 274.14220 -24.13000 PTH "POWER" PIN ECAD
+0.850 276.68220 -24.13000 PTH "POWER" PIN ECAD
+0.850 279.22220 -24.13000 PTH "POWER" PIN ECAD
+0.850 281.76220 -24.13000 PTH "POWER" PIN ECAD
+0.850 284.30220 -24.13000 PTH "POWER" PIN ECAD
+0.850 286.84220 -24.13000 PTH "POWER" PIN ECAD
+0.850 289.38220 -24.13000 PTH "POWER" PIN ECAD
+0.850 291.92220 -24.13000 PTH "POWER" PIN ECAD
+.END_DRILLED_HOLES
diff --git a/utils/idftools/idf_examples/Arduino_MEGA_2560-Rev3.emp b/utils/idftools/idf_examples/Arduino_MEGA_2560-Rev3.emp
new file mode 100644
index 0000000..693ae28
--- /dev/null
+++ b/utils/idftools/idf_examples/Arduino_MEGA_2560-Rev3.emp
@@ -0,0 +1,4 @@
+.HEADER
+LIBRARY_FILE 3.0 "Created by KiCad (2014-01-21 BZR 4629)-product" 2014/01/23.09:19:46 1
+.END_HEADER
+
diff --git a/utils/idftools/idf_examples/idf_example.emn b/utils/idftools/idf_examples/idf_example.emn
new file mode 100644
index 0000000..417e5dd
--- /dev/null
+++ b/utils/idftools/idf_examples/idf_example.emn
@@ -0,0 +1,269 @@
+.HEADER
+BOARD_FILE 3.0 "Sample File Generator" 10/22/96.16:02:44 1
+sample_board THOU
+.END_HEADER
+
+# This is the first BOARD section
+# SEC1-0
+# SEC1-1
+.BOARD_OUTLINE MCAD
+62.0
+0 5030.5 -120.0 0.0
+0 5187.5 -120.0 0.0
+0 5187.5 2130.0 0.0
+0 5155.0 2130.0 0.0
+0 5155.0 2550.0 -180.0
+0 5187.5 2550.0 0.0
+0 5187.5 4935.0 0.0
+0 4945.0 5145.0 0.0
+0 4945.0 5420.0 0.0
+0 4865.0 5500.0 0.0
+0 210.0 5500.0 0.0
+0 130.0 5420.0 0.0
+0 130.0 5145.0 0.0
+0 -112.5 4935.0 0.0
+0 -112.5 2550.0 0.0
+0 -80.0 2550.0 0.0
+0 -80.0 2130.0 -180.0
+0 -112.5 2130.0 0.0
+0 -112.5 -140.0 0.0
+0 45.5 -140.0 0.0
+0 45.5 -400.0 0.0
+0 2442.5 -400.0 0.0
+0 2442.5 -140.0 0.0
+0 2631.5 -140.0 0.0
+0 2631.5 -400.0 0.0
+0 5030.5 -400.0 0.0
+0 5030.5 -120.0 0.0
+1 2650.0 2350.0 0.0
+1 3000.0 2350.0 360.0
+.END_BOARD_OUTLINE
+
+
+# This is the second BOARD section
+# SEC2-0
+# SEC2-1
+# NOT SEC1-1
+.ROUTE_OUTLINE ECAD
+ALL
+0 5112.5 150.0 0.0
+0 5112.5 2058.2 0.0
+0 5112.5 2621.8 -162.9
+0 5112.5 4863.2 0.0
+0 4878.8 5075.0 0.0
+0 226.4 5075.0 0.0
+0 138.0 4910.3 0.0
+0 138.0 4800.0 0.0
+0 -37.5 4662.5 0.0
+0 -37.5 2621.8 0.0
+0 -37.5 2058.2 -162.9
+0 -37.5 150.0 0.0
+0 162.5 0.0 0.0
+0 4912.5 0.0 0.0
+0 5112.5 150.0 0.0
+.END_ROUTE_OUTLINE
+
+
+# This is the third BOARD section
+# SEC3-0
+# SEC3-1
+.PLACE_OUTLINE MCAD
+TOP 1000.0
+0 5080.0 2034.9 0.0
+0 5080.0 2645.1 -152.9
+0 5080.0 4837.3 0.0
+0 4855.3 5042.5 0.0
+0 252.9 5042.5 0.0
+0 170.5 4896.9 0.0
+0 170.5 4798.4 0.0
+0 -5.0 4659.0 0.0
+0 -5.0 2645.1 0.0
+0 -5.0 2034.9 -152.9
+0 -5.0 182.5 0.0
+0 192.0 32.5 0.0
+0 4883.1 32.5 0.0
+0 5080.0 182.5 0.0
+0 5080.0 2034.9 0.0
+.END_PLACE_OUTLINE
+
+# This is the fourth BOARD section
+# SEC4-0
+# SEC4-1
+.PLACE_OUTLINE UNOWNED
+BOTTOM 200.0
+0 300.0 200.0 0.0
+0 4800.0 200.0 0.0
+0 4800.0 4800.0 0.0
+0 300.0 4800.0 0.0
+0 300.0 200.0 0.0
+.END_PLACE_OUTLINE
+
+
+# This is the fifth BOARD section
+# SEC5-0
+# SEC5-1
+.ROUTE_KEEPOUT ECAD
+ALL
+0 2650.0 2350.0 0.0
+0 3100.0 2350.0 360.0
+.END_ROUTE_KEEPOUT
+
+# This is the sixth BOARD section
+# SEC6-0
+# SEC6-1
+.PLACE_KEEPOUT MCAD
+BOTH 0.0
+0 2650.0 2350.0 0.0
+0 3100.0 2350.0 360.0
+.END_PLACE_KEEPOUT
+
+# This is the seventh BOARD section
+# SEC7-0
+# SEC7-1
+.PLACE_KEEPOUT MCAD
+TOP 300.0
+0 3700.0 5000.0 0.0
+0 3700.0 4300.0 0.0
+0 4000.0 4300.0 0.0
+0 4000.0 3700.0 0.0
+0 5000.0 3700.0 0.0
+0 5000.0 4800.0 0.0
+0 4800.0 5000.0 0.0
+0 3700.0 5000.0 0.0
+.END_PLACE_KEEPOUT
+
+
+# This is the eighth BOARD section
+# SEC8-0
+# SEC8-1
+.DRILLED_HOLES
+30.0 1800.0 100.0 PTH J1 PIN ECAD
+30.0 1700.0 100.0 PTH J1 PIN ECAD
+30.0 1600.0 100.0 PTH J1 PIN ECAD
+30.0 1500.0 100.0 PTH J1 PIN ECAD
+30.0 1400.0 100.0 PTH J1 PIN ECAD
+30.0 1300.0 100.0 PTH J1 PIN ECAD
+30.0 1200.0 100.0 PTH J1 PIN ECAD
+30.0 1100.0 100.0 PTH J1 PIN ECAD
+30.0 1000.0 100.0 PTH J1 PIN ECAD
+30.0 0900.0 100.0 PTH J1 PIN ECAD
+30.0 0800.0 100.0 PTH J1 PIN ECAD
+30.0 0700.0 100.0 PTH J1 PIN ECAD
+30.0 0700.0 200.0 PTH J1 PIN ECAD
+30.0 0800.0 200.0 PTH J1 PIN ECAD
+30.0 0900.0 200.0 PTH J1 PIN ECAD
+30.0 1000.0 200.0 PTH J1 PIN ECAD
+30.0 1100.0 200.0 PTH J1 PIN ECAD
+30.0 1200.0 200.0 PTH J1 PIN ECAD
+30.0 1300.0 200.0 PTH J1 PIN ECAD
+30.0 1400.0 200.0 PTH J1 PIN ECAD
+30.0 1500.0 200.0 PTH J1 PIN ECAD
+30 1600 200 PTH J1 PIN ECAD
+30 1700 200 PTH J1 PIN ECAD
+30 1800 200 PTH J1 PIN ECAD
+30 4400 100 PTH J2 PIN ECAD
+30 4300 100 PTH J2 PIN ECAD
+30 4200 100 PTH J2 PIN ECAD
+30 4100 100 PTH J2 PIN ECAD
+30 4000 100 PTH J2 PIN ECAD
+30 3900 100 PTH J2 PIN ECAD
+30 3800 100 PTH J2 PIN ECAD
+30 3700 100 PTH J2 PIN ECAD
+30 3600 100 PTH J2 PIN ECAD
+30 3500 100 PTH J2 PIN ECAD
+30 3400 100 PTH J2 PIN ECAD
+30 3300 100 PTH J2 PIN ECAD
+30 3300 200 PTH J2 PIN ECAD
+30 3400 200 PTH J2 PIN ECAD
+30 3500 200 PTH J2 PIN ECAD
+30 3600 200 PTH J2 PIN ECAD
+30 3700 200 PTH J2 PIN ECAD
+30 3800 200 PTH J2 PIN ECAD
+30 3900 200 PTH J2 PIN ECAD
+30 4000 200 PTH J2 PIN ECAD
+30 4100 200 PTH J2 PIN ECAD
+30 4200 200 PTH J2 PIN ECAD
+30 4300 200 PTH J2 PIN ECAD
+30 4400 200 PTH J2 PIN ECAD
+30 3000 3300 PTH U3 PIN ECAD
+30 3024.2 3203 PTH U3 PIN ECAD
+30 3048.4 3105.9 PTH U3 PIN ECAD
+30 3072.6 3008.9 PTH U3 PIN ECAD
+30 3096.8 2911.9 PTH U3 PIN ECAD
+30 3121 2814.9 PTH U3 PIN ECAD
+30 3145.2 2717.8 PTH U3 PIN ECAD
+30 3436.2 2790.4 PTH U3 PIN ECAD
+30 3412.1 2887.4 PTH U3 PIN ECAD
+30 3387.9 2984.5 PTH U3 PIN ECAD
+30 3363.7 3081.5 PTH U3 PIN ECAD
+30 3339.5 3178.5 PTH U3 PIN ECAD
+30 3315.3 3275.6 PTH U3 PIN ECAD
+30 3291.1 3372.6 PTH U3 PIN ECAD
+30 2200 2500 PTH U4 PIN ECAD
+30 2100 2500 PTH U4 PIN ECAD
+30 2000 2500 PTH U4 PIN ECAD
+30 1900 2500 PTH U4 PIN ECAD
+30 1800 2500 PTH U4 PIN ECAD
+30 1700 2500 PTH U4 PIN ECAD
+30 1600 2500 PTH U4 PIN ECAD
+30 1600 2200 PTH U4 PIN ECAD
+30 1700 2200 PTH U4 PIN ECAD
+30 1800 2200 PTH U4 PIN ECAD
+30 1900 2200 PTH U4 PIN ECAD
+30 2000 2200 PTH U4 PIN ECAD
+30 2100 2200 PTH U4 PIN ECAD
+30 2200 2200 PTH U4 PIN ECAD
+20 2500 3100 PTH BOARD VIA ECAD
+20 2500 3200 PTH BOARD VIA ECAD
+20 2500 3300 PTH BOARD VIA ECAD
+20 2000 1600 PTH BOARD VIA ECAD
+20 1100 900 PTH BOARD VIA ECAD
+20 1200 1600 PTH BOARD VIA ECAD
+20 3900 3800 PTH BOARD VIA ECAD
+20 3900 2300 PTH BOARD VIA ECAD
+100.0 3100.0 -50.0 NPTH J2 MTG ECAD
+100.0 4600.0 -50.0 NPTH J2 MTG ECAD
+100.0 500.0 -50.0 NPTH J1 MTG ECAD
+100.0 2000.0 -50.0 NPTH J1 MTG ECAD
+93.0 5075.0 0.0 PTH BOARD MTG UNOWNED
+93.0 0.0 4800.0 NPTH BOARD TOOL MCAD
+93.0 0.0 0.0 PTH BOARD MTG UNOWNED
+.END_DRILLED_HOLES
+
+
+# This is the ninth BOARD section
+# SEC9-0
+# SEC9-1
+.NOTES
+3500.0 3300.0 75.0 2500.0 "This component rotated 14 degrees"
+400.0 4400.0 75.0 3200.0 "Component height limited by enclosure latch"
+1800.0 300.0 75.0 1700.0 "Do not move connectors!"
+.END_NOTES
+
+# This is the tenth and ALWAYS FINAL BOARD section
+# SEC10-0
+# SEC10-1
+.PLACEMENT
+cs13_a pn-cap C1
+4000.0 1000.0 100.0 0.0 TOP PLACED
+cc1210 pn-cc1210 C2
+3000.0 3500.0 0.0 0.0 TOP PLACED
+cc1210 pn-cc1210 C3
+3200.0 1800.0 0.0 0.0 BOTTOM PLACED
+cc1210 pn-cc1210 C4
+1400.0 2300.0 0.0 270.0 TOP PLACED
+cc1210 pn-cc1210 C5
+1799.5 3518.1 0.0 0.0 BOTTOM PLACED
+conn_din24 connector J1
+1800.0 100.0 0.0 0.0 TOP MCAD
+conn_din24 connector J2
+4400.0 100.0 0.0 0.0 TOP MCAD
+plcc_20 pn-pal16l8-plcc U1
+1800.0 3200.0 0.0 0.0 BOTTOM ECAD
+plcc_20 pn-pal16l8-plcc U2
+3200.0 1800.0 0.0 0.0 TOP PLACED
+dip_14w pn-hs346-dip U3
+3000.0 3300.0 0.0 14.0 TOP PLACED
+dip_14w pn-hs346-dip U4
+2200.0 2500.0 0.0 270.0 TOP PLACED
+.END_PLACEMENT
diff --git a/utils/idftools/idf_examples/idf_example.emp b/utils/idftools/idf_examples/idf_example.emp
new file mode 100644
index 0000000..b25b000
--- /dev/null
+++ b/utils/idftools/idf_examples/idf_example.emp
@@ -0,0 +1,69 @@
+.HEADER
+LIBRARY_file 3.0 "Sample File Generator" 10/22/96.16:41:37 1
+.END_HEADER
+
+# Component #1/5
+.ELECTRICAL
+cs13_a pn-cap THOU 150.0
+0 -55.0 55.0 0.0
+0 -55.0 -55.0 0.0
+0 135.0 -55.0 0.0
+0 135.0 -80.0 0.0
+0 565.0 -80.0 0.0
+0 565.0 -55.0 0.0
+0 755.0 -55.0 0.0
+0 755.0 55.0 0.0
+0 565.0 55.0 0.0
+0 565.0 80.0 0.0
+0 135.0 80.0 0.0
+0 135.0 55.0 0.0
+0 -55.0 55.0 0.0
+PROP CAPACITANCE 100.0
+PROP TOLERANCE 5.0
+.END_ELECTRICAL
+
+
+# Component #2/5
+.ELECTRICAL
+cc1210 pn-cc1210 THOU 67.0
+0 -40.0 56.0 0.0
+0 -40.0 -56.0 0.0
+0 182.0 -56.0 0.0
+0 182.0 56.0 0.0
+0 -40.0 56.0 0.0
+PROP CAPACITANCE 0.1
+PROP TOLERANCE 5.0
+.END_ELECTRICAL
+
+# Component #3/5
+.ELECTRICAL
+conn_din24 connector THOU 435.0
+0 -1400.0 -500.0 0.0
+0 300.0 -500.0 0.0
+0 300.0 150.0 0.0
+0 -1400.0 150.0 0.0
+0 -1400.0 -500.0 0.0
+.END_ELECTRICAL
+
+
+# Component #4/5
+.ELECTRICAL
+dip_14w pn-hs346-dip THOU 200.0
+0 350.0 50.0 0.0
+0 -50.0 50.0 0.0
+0 -50.0 -650.0 0.0
+0 350.0 -650.0 0.0
+0 350.0 50.0 0.0
+.END_ELECTRICAL
+
+
+# Component #5/5
+.ELECTRICAL
+plcc_20 pn-pal16l8-plcc THOU 14.0
+0 -200.0 240.0 0.0
+0 -240.0 200.0 0.0
+0 -240.0 -240.0 0.0
+0 240.0 -240.0 0.0
+0 240.0 240.0 0.0
+0 -200.0 240.0 0.0
+.END_ELECTRICAL
diff --git a/utils/idftools/idf_examples/test_donut.emn b/utils/idftools/idf_examples/test_donut.emn
new file mode 100644
index 0000000..fd5b87f
--- /dev/null
+++ b/utils/idftools/idf_examples/test_donut.emn
@@ -0,0 +1,45 @@
+.HEADER
+BOARD_FILE 3.0 "Created by some software" 2014/02/01.15:09:15 1
+"test_donut" MM
+.END_HEADER
+
+# The board outline is a simple square with a small hole in it
+.BOARD_OUTLINE ECAD
+1.60000
+0 -100 100 0
+0 -100 -100 0
+0 100 -100 0
+0 100 100 0
+0 -100 100 0
+1 0 0 0
+1 5 0 360
+.END_BOARD_OUTLINE
+
+# This OTHER OUTLINE is a square toroid
+.OTHER_OUTLINE UNOWNED
+MY_DONUT 30 TOP
+0 0 0 0
+0 75 0 360
+1 0 0 0
+1 30 0 360
+.END_OTHER_OUTLINE
+
+# This OTHER OUTLINE is a square with a hole
+.OTHER_OUTLINE UNOWNED
+MY_NOT_DONUT 2 BOTTOM
+0 -50 50 0
+0 -50 -50 0
+0 50 -50 0
+0 50 50 0
+0 -50 50 0
+1 0 0 0
+1 10 0 360
+2 0 50 0
+2 0 75 360
+3 50 0 0
+3 75 0 360
+4 0 -50 0
+4 0 -75 360
+5 -50 0 0
+5 -75 0 360
+.END_OTHER_OUTLINE
diff --git a/utils/idftools/idf_examples/test_donut.emp b/utils/idftools/idf_examples/test_donut.emp
new file mode 100644
index 0000000..d3c09b7
--- /dev/null
+++ b/utils/idftools/idf_examples/test_donut.emp
@@ -0,0 +1,5 @@
+.HEADER
+LIBRARY_FILE 3.0 "Created by some software" 2014/02/01.15:09:15 1
+.END_HEADER
+
+# This file contains no component outlines \ No newline at end of file
diff --git a/utils/idftools/idf_examples/test_idf2.emn b/utils/idftools/idf_examples/test_idf2.emn
new file mode 100644
index 0000000..b317a00
--- /dev/null
+++ b/utils/idftools/idf_examples/test_idf2.emn
@@ -0,0 +1,71 @@
+.HEADER
+BOARD_FILE 3.0 "Created by KiCad (2014-01-25 BZR 4633)-product" 2014/02/01.15:09:15 1
+"test_idf2.kicad_pcb" MM
+.END_HEADER
+
+.BOARD_OUTLINE ECAD
+1.60000
+0 -86.00000 42.00000 0
+0 -86.00000 -42.00000 0
+0 86.00000 -42.00000 0
+0 86.00000 42.00000 0
+0 -86.00000 42.00000 0
+.END_BOARD_OUTLINE
+
+.DRILLED_HOLES
+0.800 -74.00000 16.00000 PTH BOARD PIN ECAD
+0.800 -74.00000 -28.00000 PTH BOARD PIN ECAD
+0.850 -55.75000 16.00000 PTH BOARD PIN ECAD
+0.850 -52.25000 16.00000 PTH BOARD PIN ECAD
+0.850 -35.75000 16.00000 PTH BOARD PIN ECAD
+0.850 -32.25000 16.00000 PTH BOARD PIN ECAD
+1.575 -57.17500 -28.00000 PTH BOARD PIN ECAD
+1.575 -50.82500 -28.00000 PTH BOARD PIN ECAD
+1.575 -37.17500 -28.00000 PTH BOARD PIN ECAD
+1.575 -30.82500 -28.00000 PTH BOARD PIN ECAD
+0.800 -14.00000 16.00000 PTH BOARD PIN ECAD
+0.800 -14.00000 -28.00000 PTH BOARD PIN ECAD
+0.800 6.00000 16.00000 PTH BOARD PIN ECAD
+0.800 6.00000 -28.00000 PTH BOARD PIN ECAD
+0.800 26.00000 16.00000 PTH BOARD PIN ECAD
+0.800 26.00000 -28.00000 PTH BOARD PIN ECAD
+0.800 46.00000 16.00000 PTH BOARD PIN ECAD
+0.800 46.00000 -28.00000 PTH BOARD PIN ECAD
+0.800 66.00000 16.00000 PTH BOARD PIN ECAD
+0.800 66.00000 -28.00000 PTH BOARD PIN ECAD
+.END_DRILLED_HOLES
+
+.PLACEMENT
+"CYLV_MM" "D5.000_H8.000_Z3.000" "NOREFDES_0"
+-74.000000 16.000000 0.000000 0.000 TOP ECAD
+"CYLV_IN" "D0.250_H0.250_Z0.127" "NOREFDES_1"
+-74.000000 -28.000000 0.000000 0.000 TOP ECAD
+"CYLV_MM_L" "D5.000_H8.000_Z3.000_WD0.800_P3.500" "NOREFDES_2"
+-54.000000 16.000000 0.000000 0.000 TOP ECAD
+"CYLV_MM_R" "D5.000_H8.000_Z3.000_WD0.800_P3.500" "NOREFDES_3"
+-34.000000 16.000000 0.000000 0.000 TOP ECAD
+"CYLV_IN_L" "D0.250_H0.250_Z0.127_WD0.062_P0.250" "NOREFDES_4"
+-54.000000 -28.000000 0.000000 0.000 TOP ECAD
+"CYLV_IN_R" "D0.250_H0.250_Z0.127_WD0.062_P0.250" "NOREFDES_5"
+-34.000000 -28.000000 0.000000 0.000 TOP ECAD
+"CYLH_MM_AXI" "D2.500_H4.000_Z0.500_WD0.600_P8.000" "NOREFDES_6"
+-14.000000 16.000000 0.000000 0.000 TOP ECAD
+"CYLH_IN_AXI" "D0.098_H0.157_Z0.020_WD0.024_P0.315" "NOREFDES_7"
+-14.000000 -28.000000 0.000000 0.000 TOP ECAD
+"CYLH_MM_RAD" "D5.000_H6.000_Z0.200_WD0.600_P2.500_L3.000" "NOREFDES_8"
+6.000000 16.000000 0.000000 0.000 TOP ECAD
+"CYLH_IN_RAD" "D0.197_H0.236_Z0.008_WD0.024_P0.098_L0.118" "NOREFDES_9"
+6.000000 -28.000000 0.000000 0.000 TOP ECAD
+"RECTMM" "W10.000_L10.000_H6.000_C0.000" "NOREFDES_10"
+26.000000 16.000000 0.000000 0.000 TOP ECAD
+"RECTIN" "W393_L393_H236_C0" "NOREFDES_11"
+26.000000 -28.000000 0.000000 0.000 TOP ECAD
+"RECTMM" "W10.000_L10.000_H2.000_C0.500" "NOREFDES_12"
+46.000000 16.000000 0.000000 0.000 TOP ECAD
+"RECTIN" "W393_L393_H78_C19" "NOREFDES_13"
+46.000000 -28.000000 0.000000 0.000 TOP ECAD
+"RECTLMM" "W10.000_L10.000_H12.000_D0.800_P6.000" "NOREFDES_14"
+66.000000 16.000000 0.000000 0.000 TOP ECAD
+"RECTLIN" "W393_L393_H472_D31_P236" "NOREFDES_15"
+66.000000 -28.000000 0.000000 0.000 TOP ECAD
+.END_PLACEMENT
diff --git a/utils/idftools/idf_examples/test_idf2.emp b/utils/idftools/idf_examples/test_idf2.emp
new file mode 100644
index 0000000..9777025
--- /dev/null
+++ b/utils/idftools/idf_examples/test_idf2.emp
@@ -0,0 +1,290 @@
+.HEADER
+LIBRARY_FILE 3.0 "Created by KiCad (2014-01-25 BZR 4633)-product" 2014/02/01.15:09:15 1
+.END_HEADER
+
+# cylindrical outline, vertical, no pins
+# file: "cylvmm_0_D5_L8_Z3.idf"
+# dia: 5.000 mm
+# length: 8.000 mm
+# extra height: 3.000 mm
+.ELECTRICAL
+"CYLV_MM" "D5.000_H8.000_Z3.000" MM 11.000
+0 0 0 0
+0 5.000 0 360
+.END_ELECTRICAL
+
+# cylindrical outline, vertical, no pins
+# file: "cylvin_0_D0.25_L0.25_Z0.127.idf"
+# dia: 250 THOU
+# length: 250 THOU
+# extra height: 127 THOU
+.ELECTRICAL
+"CYLV_IN" "D0.250_H0.250_Z0.127" THOU 377
+0 0 0 0
+0 250 0 360
+.END_ELECTRICAL
+
+# cylindrical outline, vertical, 1 pin on left
+# file: "cylvmm_1L_D5_L8_Z3_WD0.8_P3.5.idf"
+# dia: 5.000 mm
+# length: 8.000 mm
+# extra height: 3.000 mm
+# wire dia: 0.800 mm
+# pitch: 3.500 mm
+.ELECTRICAL
+"CYLV_MM_L" "D5.000_H8.000_Z3.000_WD0.800_P3.500" MM 11.000
+1 -0.718 0.400 0
+1 -0.718 -0.400 -341.586
+1 -1.750 -0.400 0
+1 -1.750 0.400 -180
+1 -0.718 0.400 0
+.END_ELECTRICAL
+
+# cylindrical outline, vertical, 1 pin on right
+# file: "cylvmm_1R_D5_L8_Z3_WD0.8_P3.5.idf"
+# dia: 5.000 mm
+# length: 8.000 mm
+# extra height: 3.000 mm
+# wire dia: 0.800 mm
+# pitch: 3.500 mm
+.ELECTRICAL
+"CYLV_MM_R" "D5.000_H8.000_Z3.000_WD0.800_P3.500" MM 11.000
+0 0.718 0.400 0
+0 0.718 -0.400 341.586
+0 1.750 -0.400 0
+0 1.750 0.400 180
+0 0.718 0.400 0
+.END_ELECTRICAL
+
+# cylindrical outline, vertical, 1 pin on left
+# file: "cylvin_1L_D0.25_L0.25_Z0.127_WD0.062_P0.25.idf"
+# dia: 250 THOU
+# length: 250 THOU
+# extra height: 127 THOU
+# wire dia: 62 THOU
+# pitch: 250 THOU
+.ELECTRICAL
+"CYLV_IN_L" "D0.250_H0.250_Z0.127_WD0.062_P0.250" THOU 377
+1 3 31 0
+1 3 -31 -331.282
+1 -125 -31 0
+1 -125 31 -180
+1 3 31 0
+.END_ELECTRICAL
+
+# cylindrical outline, vertical, 1 pin on right
+# file: "cylvin_1R_D0.25_L0.25_Z0.127_WD0.062_P0.25.idf"
+# dia: 250 THOU
+# length: 250 THOU
+# extra height: 127 THOU
+# wire dia: 62 THOU
+# pitch: 250 THOU
+.ELECTRICAL
+"CYLV_IN_R" "D0.250_H0.250_Z0.127_WD0.062_P0.250" THOU 377
+0 -3 31 0
+0 -3 -31 331.282
+0 125 -31 0
+0 125 31 180
+0 -3 31 0
+.END_ELECTRICAL
+
+# cylindrical outline, horiz., axial pins
+# file: "resistor.idf"
+# dia: 2.500 mm
+# length: 4.000 mm
+# extra height: 0.500 mm
+# wire dia: 0.600 mm
+# pitch: 8.000 mm
+.ELECTRICAL
+"CYLH_MM_AXI" "D2.500_H4.000_Z0.500_WD0.600_P8.000" MM 3.000
+0 -2.000 1.250 0
+0 -2.000 0.300 0
+0 -4.000 0.300 0
+0 -4.000 -0.300 180
+0 -2.000 -0.300 0
+0 -2.000 -1.250 0
+0 2.000 -1.250 0
+0 2.000 -0.300 0
+0 4.000 -0.300 0
+0 4.000 0.300 180
+0 2.000 0.300 0
+0 2.000 1.250 0
+0 -2.000 1.250 0
+.END_ELECTRICAL
+
+# cylindrical outline, horiz., axial pins
+# file: "resistor_in.idf"
+# dia: 98 THOU
+# length: 157 THOU
+# extra height: 20 THOU
+# wire dia: 24 THOU
+# pitch: 315 THOU
+.ELECTRICAL
+"CYLH_IN_AXI" "D0.098_H0.157_Z0.020_WD0.024_P0.315" THOU 118
+0 -78 49 0
+0 -78 12 0
+0 -157 12 0
+0 -157 -12 180
+0 -78 -12 0
+0 -78 -49 0
+0 78 -49 0
+0 78 -12 0
+0 157 -12 0
+0 157 12 180
+0 78 12 0
+0 78 49 0
+0 -78 49 0
+.END_ELECTRICAL
+
+# cylindrical outline, horiz., radial pins
+# file: "capacitor.idf"
+# dia: 5.000 mm
+# length: 6.000 mm
+# extra height: 0.200 mm
+# wire dia: 0.600 mm
+# pitch: 2.500 mm
+# lead: 3.000 mm
+.ELECTRICAL
+"CYLH_MM_RAD" "D5.000_H6.000_Z0.200_WD0.600_P2.500_L3.000" MM 5.200
+0 -2.500 9.000 0
+0 -2.500 3.000 0
+0 -1.550 3.000 0
+0 -1.550 0 0
+0 -0.950 0 180
+0 -0.950 3.000 0
+0 0.950 3.000 0
+0 0.950 0 0
+0 1.550 0 180
+0 1.550 3.000 0
+0 2.500 3.000 0
+0 2.500 9.000 0
+0 -2.500 9.000 0
+.END_ELECTRICAL
+
+# cylindrical outline, horiz., radial pins
+# file: "capacitor_in.idf"
+# dia: 197 THOU
+# length: 236 THOU
+# extra height: 8 THOU
+# wire dia: 24 THOU
+# pitch: 98 THOU
+# lead: 118 THOU
+.ELECTRICAL
+"CYLH_IN_RAD" "D0.197_H0.236_Z0.008_WD0.024_P0.098_L0.118" THOU 205
+0 -98 354 0
+0 -98 118 0
+0 -61 118 0
+0 -61 0 0
+0 -37 0 180
+0 -37 118 0
+0 37 118 0
+0 37 0 0
+0 61 0 180
+0 61 118 0
+0 98 118 0
+0 98 354 0
+0 -98 354 0
+.END_ELECTRICAL
+
+# rectangular outline
+# file: "rectMM_10x10x6_C0.idf"
+# width: 10.000 mm
+# length: 10.000 mm
+# height: 6.000 mm
+# chamfer: 0.000 mm
+.ELECTRICAL
+"RECTMM" "W10.000_L10.000_H6.000_C0.000" MM 6.000
+0 5.000 5.000 0
+0 -5.000 5.000 0
+0 -5.000 -5.000 0
+0 5.000 -5.000 0
+0 5.000 5.000 0
+.END_ELECTRICAL
+
+# rectangular outline
+# file: "rectIN_10x10x6mm_C0mm.idf"
+# width: 393 THOU
+# length: 393 THOU
+# height: 236 THOU
+# chamfer: 0 THOU
+.ELECTRICAL
+"RECTIN" "W393_L393_H236_C0" THOU 236
+0 196 196 0
+0 -196 196 0
+0 -196 -196 0
+0 196 -196 0
+0 196 196 0
+.END_ELECTRICAL
+
+# rectangular outline
+# file: "rectMM_10x10x2_C0.5.idf"
+# width: 10.000 mm
+# length: 10.000 mm
+# height: 2.000 mm
+# chamfer: 0.500 mm
+.ELECTRICAL
+"RECTMM" "W10.000_L10.000_H2.000_C0.500" MM 2.000
+0 5.000 5.000 0
+0 -4.500 5.000 0
+0 -5.000 4.500 0
+0 -5.000 -5.000 0
+0 5.000 -5.000 0
+0 5.000 5.000 0
+.END_ELECTRICAL
+
+# rectangular outline
+# file: "rectIN_10x10x2mm_C0.5mm.idf"
+# width: 393 THOU
+# length: 393 THOU
+# height: 78 THOU
+# chamfer: 19 THOU
+.ELECTRICAL
+"RECTIN" "W393_L393_H78_C19" THOU 78
+0 196 196 0
+0 -176 196 0
+0 -196 176 0
+0 -196 -196 0
+0 196 -196 0
+0 196 196 0
+.END_ELECTRICAL
+
+# rectangular outline, leaded
+# file: "rectLMM_10x10x12_D0.8_P6.0.idf"
+# width: 10.000 mm
+# length: 10.000 mm
+# height: 12.000 mm
+# wire dia: 0.800 mm
+# pitch: 6.000 mm
+.ELECTRICAL
+"RECTLMM" "W10.000_L10.000_H12.000_D0.800_P6.000" MM 12.000
+0 3.000 0.400 0
+0 2.000 0.400 0
+0 2.000 5.000 0
+0 -8.000 5.000 0
+0 -8.000 -5.000 0
+0 2.000 -5.000 0
+0 2.000 -0.400 0
+0 3.000 -0.400 0
+0 3.000 0.400 180
+.END_ELECTRICAL
+
+# rectangular outline, leaded
+# file: "rectLIN_10x10x12mm_D0.8mm_P6.0mm.idf"
+# width: 393 THOU
+# length: 393 THOU
+# height: 472 THOU
+# wire dia: 31 THOU
+# pitch: 236 THOU
+.ELECTRICAL
+"RECTLIN" "W393_L393_H472_D31_P236" THOU 472
+0 118 15 0
+0 78 15 0
+0 78 196 0
+0 -315 196 0
+0 -315 -196 0
+0 78 -196 0
+0 78 -15 0
+0 118 -15 0
+0 118 15 180
+.END_ELECTRICAL
+
diff --git a/utils/idftools/idf_helpers.cpp b/utils/idftools/idf_helpers.cpp
new file mode 100644
index 0000000..cad1852
--- /dev/null
+++ b/utils/idftools/idf_helpers.cpp
@@ -0,0 +1,323 @@
+/*
+ * 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
+ */
+
+#include <cctype>
+#include <iostream>
+#include <sstream>
+
+#include <idf_common.h>
+#include <idf_helpers.h>
+
+using namespace std;
+using namespace IDF3;
+
+// fetch a line from the given input file and trim the ends
+bool IDF3::FetchIDFLine( std::ifstream& aModel, std::string& aLine, bool& isComment, std::streampos& aFilePos )
+{
+ aLine = "";
+ aFilePos = aModel.tellg();
+
+ if( aFilePos == -1 )
+ return false;
+
+ std::getline( aModel, aLine );
+
+ isComment = false;
+
+ // A comment begins with a '#' and must be the first character on the line
+ if( aLine[0] == '#' )
+ {
+ // opening '#' is stripped
+ isComment = true;
+ aLine.erase( aLine.begin() );
+ }
+
+ // strip leading and trailing spaces
+ while( !aLine.empty() && isspace( *aLine.begin() ) )
+ aLine.erase( aLine.begin() );
+
+ while( !aLine.empty() && isspace( *aLine.rbegin() ) )
+ aLine.erase( --aLine.end() );
+
+ // a comment line may be empty to improve human readability
+ if( aLine.empty() && !isComment )
+ return false;
+
+ return true;
+}
+
+
+// extract an IDF string and move the index to point to the character after the substring
+bool IDF3::GetIDFString( const std::string& aLine, std::string& aIDFString,
+ bool& hasQuotes, int& aIndex )
+{
+ // 1. drop all leading spaces
+ // 2. if the first character is '"', read until the next '"',
+ // otherwise read until the next space or EOL.
+
+ std::ostringstream ostr;
+
+ int len = aLine.length();
+ int idx = aIndex;
+
+ if( idx < 0 || idx >= len )
+ return false;
+
+ while( isspace( aLine[idx] ) && idx < len ) ++idx;
+
+ if( idx == len )
+ {
+ aIndex = idx;
+ return false;
+ }
+
+ if( aLine[idx] == '"' )
+ {
+ hasQuotes = true;
+ ++idx;
+ while( aLine[idx] != '"' && idx < len )
+ ostr << aLine[idx++];
+
+ if( idx == len )
+ {
+ ERROR_IDF << "unterminated quote mark in line:\n";
+ std::cerr << "LINE: " << aLine << "\n";
+ aIndex = idx;
+ return false;
+ }
+
+ ++idx;
+ }
+ else
+ {
+ hasQuotes = false;
+
+ while( !isspace( aLine[idx] ) && idx < len )
+ ostr << aLine[idx++];
+
+ }
+
+ aIDFString = ostr.str();
+ aIndex = idx;
+
+ return true;
+}
+
+
+// perform a comparison between a fixed token string and an input string.
+// the token is assumed to be an upper case IDF token and the input string
+// is data from an IDF file. Since IDF tokens are case-insensitive, we cannot
+// assume anything about the case of the input string.
+bool IDF3::CompareToken( const char* aTokenString, const std::string& aInputString )
+{
+ std::string::size_type i, j;
+ std::string bigToken = aInputString;
+ j = aInputString.length();
+
+ for( i = 0; i < j; ++i )
+ bigToken[i] = std::toupper( bigToken[i] );
+
+ if( !bigToken.compare( aTokenString ) )
+ return true;
+
+ return false;
+}
+
+
+// parse a string for an IDF3::KEY_OWNER
+bool IDF3::ParseOwner( const std::string& aToken, IDF3::KEY_OWNER& aOwner )
+{
+ if( CompareToken( "UNOWNED", aToken ) )
+ {
+ aOwner = UNOWNED;
+ return true;
+ }
+ else if( CompareToken( "ECAD", aToken ) )
+ {
+ aOwner = ECAD;
+ return true;
+ }
+ else if( CompareToken( "MCAD", aToken ) )
+ {
+ aOwner = MCAD;
+ return true;
+ }
+
+ ERROR_IDF << "unrecognized IDF OWNER: '" << aToken << "'\n";
+
+ return false;
+}
+
+
+bool IDF3::ParseIDFLayer( const std::string& aToken, IDF3::IDF_LAYER& aLayer )
+{
+ if( CompareToken( "TOP", aToken ) )
+ {
+ aLayer = LYR_TOP;
+ return true;
+ }
+ else if( CompareToken( "BOTTOM", aToken ) )
+ {
+ aLayer = LYR_BOTTOM;
+ return true;
+ }
+ else if( CompareToken( "BOTH", aToken ) )
+ {
+ aLayer = LYR_BOTH;
+ return true;
+ }
+ else if( CompareToken( "INNER", aToken ) )
+ {
+ aLayer = LYR_INNER;
+ return true;
+ }
+ else if( CompareToken( "ALL", aToken ) )
+ {
+ aLayer = LYR_ALL;
+ return true;
+ }
+
+ ERROR_IDF << "unrecognized IDF LAYER: '" << aToken << "'\n";
+
+ aLayer = LYR_INVALID;
+ return false;
+}
+
+
+bool IDF3::WriteLayersText( std::ofstream& aBoardFile, IDF3::IDF_LAYER aLayer )
+{
+ switch( aLayer )
+ {
+ case LYR_TOP:
+ aBoardFile << "TOP";
+ break;
+
+ case LYR_BOTTOM:
+ aBoardFile << "BOTTOM";
+ break;
+
+ case LYR_BOTH:
+ aBoardFile << "BOTH";
+ break;
+
+ case LYR_INNER:
+ aBoardFile << "INNER";
+ break;
+
+ case LYR_ALL:
+ aBoardFile << "ALL";
+ break;
+
+ default:
+ do{
+ std::ostringstream ostr;
+ ostr << "invalid IDF layer: " << aLayer;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ } while( 0 );
+
+ break;
+ }
+
+ return !aBoardFile.fail();
+}
+
+
+std::string IDF3::GetPlacementString( IDF3::IDF_PLACEMENT aPlacement )
+{
+ switch( aPlacement )
+ {
+ case PS_UNPLACED:
+ return "UNPLACED";
+
+ case PS_PLACED:
+ return "PLACED";
+
+ case PS_MCAD:
+ return "MCAD";
+
+ case PS_ECAD:
+ return "ECAD";
+
+ default:
+ break;
+ }
+
+ std::ostringstream ostr;
+ ostr << "[INVALID PLACEMENT VALUE]:" << aPlacement;
+
+ return ostr.str();
+}
+
+
+std::string IDF3::GetLayerString( IDF3::IDF_LAYER aLayer )
+{
+ switch( aLayer )
+ {
+ case LYR_TOP:
+ return "TOP";
+
+ case LYR_BOTTOM:
+ return "BOTTOM";
+
+ case LYR_BOTH:
+ return "BOTH";
+
+ case LYR_INNER:
+ return "INNER";
+
+ case LYR_ALL:
+ return "ALL";
+
+ default:
+ break;
+ }
+
+ std::ostringstream ostr;
+ ostr << "[INVALID LAYER VALUE]:" << aLayer;
+
+ return ostr.str();
+}
+
+std::string IDF3::GetOwnerString( IDF3::KEY_OWNER aOwner )
+{
+ switch( aOwner )
+ {
+ case IDF3::UNOWNED:
+ return "UNOWNED";
+
+ case IDF3::MCAD:
+ return "MCAD";
+
+ case IDF3::ECAD:
+ return "ECAD";
+
+ default:
+ break;
+ }
+
+ ostringstream ostr;
+ ostr << "UNKNOWN: " << aOwner;
+
+ return ostr.str();
+}
diff --git a/utils/idftools/idf_helpers.h b/utils/idftools/idf_helpers.h
new file mode 100644
index 0000000..9b0c33f
--- /dev/null
+++ b/utils/idftools/idf_helpers.h
@@ -0,0 +1,175 @@
+/*
+ * 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
+ */
+
+#ifndef IDF_HELPERS_H
+#define IDF_HELPERS_H
+
+#include <wx/wx.h>
+#include <fstream>
+#include <string>
+#include <idf_common.h>
+
+/**
+ * Macro TO_UTF8
+ * converts a wxString to a UTF8 encoded C string for all wxWidgets build modes.
+ * wxstring is a wxString, not a wxT() or _(). The scope of the return value
+ * is very limited and volatile, but can be used with printf() style functions well.
+ * NOTE: Taken from KiCad include/macros.h
+ */
+#define TO_UTF8( wxstring ) ( (const char*) (wxstring).utf8_str() )
+
+/**
+ * function FROM_UTF8
+ * converts a UTF8 encoded C string to a wxString for all wxWidgets build modes.
+ * NOTE: Taken from KiCad include/macros.h
+ */
+static inline wxString FROM_UTF8( const char* cstring )
+{
+ wxString line = wxString::FromUTF8( cstring );
+
+ if( line.IsEmpty() ) // happens when cstring is not a valid UTF8 sequence
+ line = wxConvCurrent->cMB2WC( cstring ); // try to use locale conversion
+
+ return line;
+}
+
+
+#define ERROR_IDF std::cerr << "* " << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): "
+
+// minimum drill diameters / slot widths to be represented in the IDF output
+#define IDF_MIN_DIA_MM ( 0.001 )
+#define IDF_MIN_DIA_THOU ( 0.00039 )
+#define IDF_MIN_DIA_TNM ( 100 )
+
+// conversion from thou to mm
+#define IDF_THOU_TO_MM 0.0254
+// conversion from TNM to mm
+#define IDF_TNM_TO_MM 0.00001
+
+namespace IDF3
+{
+
+/**
+ * Function FetchIDFLine
+ * retrieves a single line from an IDF file and performs minimal processing. If a comment symbol
+ * is encountered then it is removed and a single leading space is removed if present; all trailing
+ * spaces are removed. If the line is not a comment then all leading and trailing spaces are stripped.
+ *
+ * @param aModel is an open IDFv3 file
+ * @param aLine (output) is the line retrieved from the file
+ * @param isComment (output) is set to true if the line is a comment
+ * @param aFilePos (output) is set to the beginning of the line in case the file needs to be rewound
+ *
+ * @return bool: true if a line was read and was not empty; otherwise false
+ */
+bool FetchIDFLine( std::ifstream& aModel, std::string& aLine, bool& isComment, std::streampos& aFilePos );
+
+
+/**
+ * Function GetIDFString
+ * parses a line retrieved via FetchIDFLine() and returns the first IDF string found from the starting
+ * point aIndex
+ *
+ * @param aLine is the line to parse
+ * @param aIDFString (output) is the IDF string retrieved
+ * @param hasQuotes (output) is true if the string was in quotation marks
+ * @param aIndex (input/output) is the index into the input line
+ *
+ * @return bool: true if a string was retrieved, otherwise false
+ */
+bool GetIDFString( const std::string& aLine, std::string& aIDFString,
+ bool& hasQuotes, int& aIndex );
+
+/**
+ * Function CompareToken
+ * performs a case-insensitive comparison of a token string and an input string
+ *
+ * @param aToken is an IDF token such as ".HEADER"
+ * @param aInputString is a string typically retrieved via GetIDFString
+ *
+ * @return bool: true if the token and input string match
+ */
+bool CompareToken( const char* aTokenString, const std::string& aInputString );
+
+
+/**
+ * Function ParseOwner
+ * parses the input string for a valid IDF Owner type
+ *
+ * @param aToken is the string to be parsed
+ * @param aOwner (output) is the IDF Owner class
+ *
+ * @return bool: true if a valid OWNER was found, otherwise false
+ */
+bool ParseOwner( const std::string& aToken, IDF3::KEY_OWNER& aOwner );
+
+
+/**
+ * Function ParseIDFLayer
+ * parses an input string for a valid IDF layer specification
+ *
+ * @param aToken is the string to be parsed
+ * @param aLayer (output) is the IDF Layer type or group
+ *
+ * @return bool: true if a valid IDF Layer type was found, otherwise false
+ */
+bool ParseIDFLayer( const std::string& aToken, IDF3::IDF_LAYER& aLayer );
+
+
+/**
+ * Function WriteLayersText
+ * writes out text corresponding to the given IDF Layer type
+ *
+ * @param aBoardFile is an IDFv3 file open for output
+ * @param aLayer is the IDF Layer type
+ *
+ * @return bool: true if the data was successfully written, otherwise false
+ */
+bool WriteLayersText( std::ofstream& aBoardFile, IDF3::IDF_LAYER aLayer );
+
+
+/**
+ * Function GetPlacementString
+ * returns a string representing the given IDF Placement type
+ *
+ * @param aPlacement is the IDF placement type to encode as a string
+ *
+ * @return string: the string representation of aPlacement
+ */
+std::string GetPlacementString( IDF3::IDF_PLACEMENT aPlacement );
+
+
+/**
+ * Function GetLayerString
+ * returns a string representing the given IDF Layer type
+ *
+ * @param aLayer is the IDF layer type to encode as a string
+ *
+ * @return string: the string representation of aLayer
+ */
+std::string GetLayerString( IDF3::IDF_LAYER aLayer );
+
+std::string GetOwnerString( IDF3::KEY_OWNER aOwner );
+}
+
+#endif // IDF_HELPERS_H
diff --git a/utils/idftools/idf_outlines.cpp b/utils/idftools/idf_outlines.cpp
new file mode 100644
index 0000000..f1541d2
--- /dev/null
+++ b/utils/idftools/idf_outlines.cpp
@@ -0,0 +1,3614 @@
+/*
+ * 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
+ */
+
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <cmath>
+
+#include <idf_helpers.h>
+#include <idf_outlines.h>
+#include <idf_parser.h>
+
+using namespace IDF3;
+using namespace std;
+
+
+static std::string GetOutlineTypeString( IDF3::OUTLINE_TYPE aOutlineType )
+{
+ switch( aOutlineType )
+ {
+ case OTLN_BOARD:
+ return ".BOARD_OUTLINE";
+
+ case OTLN_OTHER:
+ return ".OTHER_OUTLINE";
+
+ case OTLN_PLACE:
+ return ".PLACEMENT_OUTLINE";
+
+ case OTLN_ROUTE:
+ return ".ROUTE_OUTLINE";
+
+ case OTLN_PLACE_KEEPOUT:
+ return ".PLACE_KEEPOUT";
+
+ case OTLN_ROUTE_KEEPOUT:
+ return ".ROUTE_KEEPOUT";
+
+ case OTLN_VIA_KEEPOUT:
+ return ".VIA_KEEPOUT";
+
+ case OTLN_GROUP_PLACE:
+ return ".PLACE_REGION";
+
+ case OTLN_COMPONENT:
+ return "COMPONENT OUTLINE";
+
+ default:
+ break;
+ }
+
+ std::ostringstream ostr;
+ ostr << "[INVALID OUTLINE TYPE VALUE]:" << aOutlineType;
+
+ return ostr.str();
+}
+
+#ifndef DISABLE_IDF_OWNERSHIP
+static bool CheckOwnership( int aSourceLine, const char* aSourceFunc,
+ IDF3_BOARD* aParent, IDF3::KEY_OWNER aOwnerCAD,
+ IDF3::OUTLINE_TYPE aOutlineType, std::string& aErrorString )
+{
+ if( aParent == NULL )
+ {
+ ostringstream ostr;
+ ostr << "* " << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
+ ostr << "* BUG: outline's parent not set; cannot enforce ownership rules\n";
+ ostr << "* outline type: " << GetOutlineTypeString( aOutlineType );
+ aErrorString = ostr.str();
+
+ return false;
+ }
+
+ // note: component outlines have no owner so we don't care about
+ // who modifies them
+ if( aOwnerCAD == UNOWNED || aOutlineType == IDF3::OTLN_COMPONENT )
+ return true;
+
+ IDF3::CAD_TYPE parentCAD = aParent->GetCadType();
+
+ if( aOwnerCAD == MCAD && parentCAD == CAD_MECH )
+ return true;
+
+ if( aOwnerCAD == ECAD && parentCAD == CAD_ELEC )
+ return true;
+
+ do
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
+ ostr << "* ownership violation; CAD type is ";
+
+ if( parentCAD == CAD_MECH )
+ ostr << "MCAD ";
+ else
+ ostr << "ECAD ";
+
+ ostr << "while outline owner is " << GetOwnerString( aOwnerCAD ) << "\n";
+ ostr << "* outline type: " << GetOutlineTypeString( aOutlineType );
+ aErrorString = ostr.str();
+
+ } while( 0 );
+
+ return false;
+}
+#endif
+
+
+/*
+ * CLASS: BOARD OUTLINE
+ */
+BOARD_OUTLINE::BOARD_OUTLINE()
+{
+ outlineType = OTLN_BOARD;
+ single = false;
+ owner = UNOWNED;
+ parent = NULL;
+ thickness = 0.0;
+ unit = UNIT_MM;
+ return;
+}
+
+BOARD_OUTLINE::~BOARD_OUTLINE()
+{
+ clear();
+ return;
+}
+
+IDF3::OUTLINE_TYPE BOARD_OUTLINE::GetOutlineType( void )
+{
+ return outlineType;
+}
+
+void BOARD_OUTLINE::readOutlines( std::ifstream& aBoardFile, IDF3::IDF_VERSION aIdfVersion )
+{
+ // reads the outline data from a file
+ double x, y, ang;
+ double dLoc = 1e-5; // distances are equal when closer than 0.1 micron
+ bool comment = false;
+ bool quoted = false;
+ bool closed = false;
+ int idx = 0;
+ int loopidx = -1;
+ int tmp = 0;
+ int npts = 0;
+ std::string iline;
+ std::string entry;
+ std::stringstream tstr;
+ IDF_OUTLINE* op = NULL;
+ IDF_SEGMENT* sp = NULL;
+ IDF_POINT prePt;
+ IDF_POINT curPt;
+ std::streampos pos;
+
+ // destroy any existing outline data
+ clearOutlines();
+
+ while( aBoardFile.good() )
+ {
+ if( !FetchIDFLine( aBoardFile, iline, comment, pos ) )
+ continue;
+
+ idx = 0;
+ GetIDFString( iline, entry, quoted, idx );
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 1 of " << GetOutlineTypeString( outlineType );
+ ostr << " is quoted\n";
+ ostr << "* line: '" << iline << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ // check for the end of the section
+ if( entry.size() >= 5 && CompareToken( ".END_", entry.substr( 0, 5 ) ) )
+ {
+ // rewind to the start of the last line; the routine invoking
+ // this is responsible for checking that the current '.END_ ...'
+ // matches the section header.
+ if(aBoardFile.eof())
+ aBoardFile.clear();
+
+ aBoardFile.seekg( pos );
+
+ if( outlines.size() > 0 )
+ {
+ if( npts > 0 && !closed )
+ {
+ ostringstream ostr;
+ ostr << "invalid outline (not closed)\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ // verify winding
+ if( !single )
+ {
+ if( !outlines.front()->IsCCW() )
+ {
+ ERROR_IDF << "invalid IDF3 file (BOARD_OUTLINE)\n";
+ cerr << "* WARNING: first outline is not in CCW order\n";
+ return;
+ }
+
+ if( outlines.size() > 1 && outlines.back()->IsCCW() && !outlines.back()->IsCircle() )
+ {
+ ERROR_IDF << "invalid IDF3 file (BOARD_OUTLINE)\n";
+ cerr << "* WARNING: final cutout does not have points in CW order\n";
+ cerr << "* file position: " << pos << "\n";
+ return;
+ }
+ }
+ }
+
+ return;
+ }
+
+ tstr.clear();
+ tstr << entry;
+
+ tstr >> tmp;
+ if( tstr.fail() )
+ {
+ if( outlineType == OTLN_COMPONENT && CompareToken( "PROP", entry ) )
+ {
+ aBoardFile.seekg( pos );
+ return;
+ }
+
+ do{
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 1 of " << GetOutlineTypeString( outlineType );
+ ostr << " is not numeric\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+
+ } while( 0 );
+ }
+
+ if( tmp != loopidx )
+ {
+ // index change
+ if( npts > 0 && !closed )
+ {
+ ostringstream ostr;
+ ostr << "invalid outline ( outline # " << loopidx << " not closed)\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( tmp < 0 )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 1 of " << GetOutlineTypeString( outlineType );
+ ostr << " is invalid\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( loopidx == -1 )
+ {
+ // first outline
+ if( single )
+ {
+ // outline may have a Loop Index of 0 or 1
+ if( tmp == 0 || tmp == 1 )
+ {
+ op = new IDF_OUTLINE;
+
+ if( op == NULL )
+ {
+ clearOutlines();
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "memory allocation failed" ) );
+ }
+
+ outlines.push_back( op );
+ loopidx = tmp;
+ }
+ else
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 1 of " << GetOutlineTypeString( outlineType );
+ ostr << " is invalid (must be 0 or 1)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+ else
+ {
+ // outline *MUST* have a Loop Index of 0
+ if( tmp != 0 )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 1 of " << GetOutlineTypeString( outlineType );
+ ostr << " is invalid (must be 0)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ op = new IDF_OUTLINE;
+
+ if( op == NULL )
+ {
+ clearOutlines();
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "memory allocation failed" ) );
+ }
+
+ outlines.push_back( op );
+ loopidx = tmp;
+ }
+ // end of block for first outline
+ }
+ else
+ {
+ // outline for cutout
+ if( single )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType );
+ ostr << " section may only have one outline\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( tmp - loopidx != 1 )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType );
+ ostr << " section must have cutouts in numeric order from 1 onwards\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ // verify winding of previous outline
+ if( ( loopidx == 0 && !op->IsCCW() )
+ || ( loopidx > 0 && op->IsCCW() && !op->IsCircle() ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation of loop point order rules by Loop Index " << loopidx << "\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ op = new IDF_OUTLINE;
+
+ if( op == NULL )
+ {
+ clearOutlines();
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "memory allocation failed" ) );
+ }
+
+ outlines.push_back( op );
+ loopidx = tmp;
+ }
+ // end of index change code
+ npts = 0;
+ closed = false;
+ }
+
+ if( op == NULL )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 1 of " << GetOutlineTypeString( outlineType );
+ ostr << " is invalid\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, entry, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 2 of ";
+ ostr << GetOutlineTypeString( outlineType ) << " does not exist\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 2 of ";
+ ostr << GetOutlineTypeString( outlineType ) << " must not be in quotes\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ tstr.clear();
+ tstr << entry;
+
+ tstr >> x;
+ if( tstr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 2 of ";
+ ostr << GetOutlineTypeString( outlineType ) << " is an invalid X value\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, entry, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 3 of ";
+ ostr << GetOutlineTypeString( outlineType ) << " does not exist\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 3 of ";
+ ostr << GetOutlineTypeString( outlineType ) << " must not be in quotes\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ tstr.clear();
+ tstr << entry;
+
+ tstr >> y;
+ if( tstr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 3 of ";
+ ostr << GetOutlineTypeString( outlineType ) << " is an invalid Y value\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, entry, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 4 of ";
+ ostr << GetOutlineTypeString( outlineType ) << " does not exist\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 4 of ";
+ ostr << GetOutlineTypeString( outlineType ) << " must not be in quotes\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ tstr.clear();
+ tstr << entry;
+
+ tstr >> ang;
+ if( tstr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 4 of ";
+ ostr << GetOutlineTypeString( outlineType ) << " is not a valid angle\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ // the line was successfully read; convert to mm if necessary
+ if( unit == UNIT_THOU )
+ {
+ x *= IDF_THOU_TO_MM;
+ y *= IDF_THOU_TO_MM;
+ }
+ else if( ( aIdfVersion == IDF_V2 ) && ( unit == UNIT_TNM ) )
+ {
+ x *= IDF_TNM_TO_MM;
+ y *= IDF_TNM_TO_MM;
+ }
+ else if( unit != UNIT_MM )
+ {
+ ostringstream ostr;
+ ostr << "\n* BUG: invalid UNIT type: " << unit;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( npts++ == 0 )
+ {
+ // first point
+ prePt.x = x;
+ prePt.y = y;
+
+ // ensure that the first point is not an arc specification
+ if( ang < -MIN_ANG || ang > MIN_ANG )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3 of ";
+ ostr << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: first point of an outline has a non-zero angle\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+ else
+ {
+ // Nth point
+ if( closed )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3 of ";
+ ostr << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: adding a segment to a closed outline\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ curPt.x = x;
+ curPt.y = y;
+
+ if( ang > -MIN_ANG && ang < MIN_ANG )
+ {
+ sp = new IDF_SEGMENT( prePt, curPt );
+ }
+ else
+ {
+ sp = new IDF_SEGMENT( prePt, curPt, ang, false );
+ }
+
+ if( sp == NULL )
+ {
+ clearOutlines();
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "memory allocation failed" ) );
+ }
+
+ if( sp->IsCircle() )
+ {
+ // this is a circle; the loop is closed
+ if( op->size() != 0 )
+ {
+ delete sp;
+
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3 of ";
+ ostr << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: adding a circle to a non-empty outline\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ closed = true;
+ }
+ else if( op->size() != 0 )
+ {
+ if( curPt.Matches( op->front()->startPoint, dLoc ) )
+ closed = true;
+ }
+
+ op->push( sp );
+ prePt.x = x;
+ prePt.y = y;
+ }
+ } // while( aBoardFile.good() )
+
+ // NOTE:
+ // 1. ideally we would ensure that there are no arcs with a radius of 0
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading file (premature end of outline)" ) );
+
+ return;
+}
+
+bool BOARD_OUTLINE::writeComments( std::ofstream& aBoardFile )
+{
+ if( comments.empty() )
+ return true;
+
+ list< string >::const_iterator itS = comments.begin();
+ list< string >::const_iterator itE = comments.end();
+
+ while( itS != itE )
+ {
+ aBoardFile << "# " << *itS << "\n";
+ ++itS;
+ }
+
+ return !aBoardFile.fail();
+}
+
+bool BOARD_OUTLINE::writeOwner( std::ofstream& aBoardFile )
+{
+ switch( owner )
+ {
+ case ECAD:
+ aBoardFile << "ECAD\n";
+ break;
+
+ case MCAD:
+ aBoardFile << "MCAD\n";
+ break;
+
+ default:
+ aBoardFile << "UNOWNED\n";
+ break;
+ }
+
+ return !aBoardFile.fail();
+}
+
+void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutline, size_t aIndex )
+{
+ std::list<IDF_SEGMENT*>::iterator bo;
+ std::list<IDF_SEGMENT*>::iterator eo;
+
+ if( !aOutline )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: NULL outline pointer" ) );
+
+ if( aOutline->size() == 1 )
+ {
+ if( !aOutline->front()->IsCircle() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "bad outline (single segment item, not circle)" ) );
+
+ if( single )
+ aIndex = 0;
+
+ // NOTE: a circle always has an angle of 360, never -360,
+ // otherwise SolidWorks chokes on the file.
+ if( unit != UNIT_THOU )
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << aOutline->front()->startPoint.x << " "
+ << aOutline->front()->startPoint.y << " 0\n";
+
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << aOutline->front()->endPoint.x << " "
+ << aOutline->front()->endPoint.y << " 360\n";
+ }
+ else
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << (aOutline->front()->startPoint.x / IDF_THOU_TO_MM) << " "
+ << (aOutline->front()->startPoint.y / IDF_THOU_TO_MM) << " 0\n";
+
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << (aOutline->front()->endPoint.x / IDF_THOU_TO_MM) << " "
+ << (aOutline->front()->endPoint.y / IDF_THOU_TO_MM) << " 360\n";
+ }
+
+ return;
+ }
+
+ if( single )
+ {
+ // only indices 0 (CCW) and 1 (CW) are valid; set the index according to
+ // the outline's winding
+ if( aOutline->IsCCW() )
+ aIndex = 0;
+ else
+ aIndex = 1;
+ }
+
+
+ // check if we must reverse things
+ if( ( aOutline->IsCCW() && ( aIndex > 0 ) )
+ || ( ( !aOutline->IsCCW() ) && ( aIndex == 0 ) ) )
+ {
+ eo = aOutline->begin();
+ bo = aOutline->end();
+ --bo;
+
+ // ensure that the very last point is the same as the very first point
+ if( aOutline->size() > 1 )
+ {
+ std::list<IDF_SEGMENT*>::iterator to = eo;
+ ++to;
+ (*to)->startPoint = (*eo)->endPoint;
+ }
+
+ // for the first item we write out both points
+ if( unit != UNIT_THOU )
+ {
+ if( aOutline->front()->angle < MIN_ANG && aOutline->front()->angle > -MIN_ANG )
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << aOutline->front()->endPoint.x << " "
+ << aOutline->front()->endPoint.y << " 0\n";
+
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << aOutline->front()->startPoint.x << " "
+ << aOutline->front()->startPoint.y << " 0\n";
+ }
+ else
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << aOutline->front()->endPoint.x << " "
+ << aOutline->front()->endPoint.y << " 0\n";
+
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << aOutline->front()->startPoint.x << " "
+ << aOutline->front()->startPoint.y << " "
+ << setprecision(3) << -aOutline->front()->angle << "\n";
+ }
+ }
+ else
+ {
+ if( aOutline->front()->angle < MIN_ANG && aOutline->front()->angle > -MIN_ANG )
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << (aOutline->front()->endPoint.x / IDF_THOU_TO_MM) << " "
+ << (aOutline->front()->endPoint.y / IDF_THOU_TO_MM) << " 0\n";
+
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << (aOutline->front()->startPoint.x / IDF_THOU_TO_MM) << " "
+ << (aOutline->front()->startPoint.y / IDF_THOU_TO_MM) << " 0\n";
+ }
+ else
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << (aOutline->front()->endPoint.x / IDF_THOU_TO_MM) << " "
+ << (aOutline->front()->endPoint.y / IDF_THOU_TO_MM) << " 0\n";
+
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << (aOutline->front()->startPoint.x / IDF_THOU_TO_MM) << " "
+ << (aOutline->front()->startPoint.y / IDF_THOU_TO_MM) << " "
+ << setprecision(3) << -aOutline->front()->angle << "\n";
+ }
+ }
+
+ // for all other segments we only write out the start point
+ while( bo != eo )
+ {
+ if( unit != UNIT_THOU )
+ {
+ if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << (*bo)->startPoint.x << " "
+ << (*bo)->startPoint.y << " 0\n";
+ }
+ else
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << (*bo)->startPoint.x << " "
+ << (*bo)->startPoint.y << " "
+ << setprecision(3) << -(*bo)->angle << "\n";
+ }
+ }
+ else
+ {
+ if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << ((*bo)->startPoint.x / IDF_THOU_TO_MM) << " "
+ << ((*bo)->startPoint.y / IDF_THOU_TO_MM) << " 0\n";
+ }
+ else
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << ((*bo)->startPoint.x / IDF_THOU_TO_MM) << " "
+ << ((*bo)->startPoint.y / IDF_THOU_TO_MM) << " "
+ << setprecision(3) << -(*bo)->angle << "\n";
+ }
+ }
+
+ --bo;
+ }
+ }
+ else
+ {
+ // ensure that the very last point is the same as the very first point
+ if( aOutline->size() > 1 )
+ aOutline->back()-> endPoint = aOutline->front()->startPoint;
+
+ bo = aOutline->begin();
+ eo = aOutline->end();
+
+ // for the first item we write out both points
+ if( unit != UNIT_THOU )
+ {
+ if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << (*bo)->startPoint.x << " "
+ << (*bo)->startPoint.y << " 0\n";
+
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << (*bo)->endPoint.x << " "
+ << (*bo)->endPoint.y << " 0\n";
+ }
+ else
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << (*bo)->startPoint.x << " "
+ << (*bo)->startPoint.y << " 0\n";
+
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << (*bo)->endPoint.x << " "
+ << (*bo)->endPoint.y << " "
+ << setprecision(3) << (*bo)->angle << "\n";
+ }
+ }
+ else
+ {
+ if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << ((*bo)->startPoint.x / IDF_THOU_TO_MM) << " "
+ << ((*bo)->startPoint.y / IDF_THOU_TO_MM) << " 0\n";
+
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << ((*bo)->endPoint.x / IDF_THOU_TO_MM) << " "
+ << ((*bo)->endPoint.y / IDF_THOU_TO_MM) << " 0\n";
+ }
+ else
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << ((*bo)->startPoint.x / IDF_THOU_TO_MM) << " "
+ << ((*bo)->startPoint.y / IDF_THOU_TO_MM) << " 0\n";
+
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << ((*bo)->endPoint.x / IDF_THOU_TO_MM) << " "
+ << ((*bo)->endPoint.y / IDF_THOU_TO_MM) << " "
+ << setprecision(3) << (*bo)->angle << "\n";
+ }
+ }
+
+ ++bo;
+
+ // for all other segments we only write out the last point
+ while( bo != eo )
+ {
+ if( unit != UNIT_THOU )
+ {
+ if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << (*bo)->endPoint.x << " "
+ << (*bo)->endPoint.y << " 0\n";
+ }
+ else
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << (*bo)->endPoint.x << " "
+ << (*bo)->endPoint.y << " "
+ << setprecision(3) << (*bo)->angle << "\n";
+ }
+ }
+ else
+ {
+ if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << ((*bo)->endPoint.x / IDF_THOU_TO_MM) << " "
+ << ((*bo)->endPoint.y / IDF_THOU_TO_MM) << " 0\n";
+ }
+ else
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << ((*bo)->endPoint.x / IDF_THOU_TO_MM) << " "
+ << ((*bo)->endPoint.y / IDF_THOU_TO_MM) << " "
+ << setprecision(3) << (*bo)->angle << "\n";
+ }
+ }
+
+ ++bo;
+ }
+ }
+
+ return;
+}
+
+void BOARD_OUTLINE::writeOutlines( std::ofstream& aBoardFile )
+{
+ if( outlines.empty() )
+ return;
+
+ int idx = 0;
+ std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
+ std::list< IDF_OUTLINE* >::iterator itE = outlines.end();
+
+ while( itS != itE )
+ {
+ writeOutline( aBoardFile, *itS, idx++ );
+ ++itS;
+ }
+
+ return;
+}
+
+bool BOARD_OUTLINE::SetUnit( IDF3::IDF_UNIT aUnit )
+{
+ // note: although UNIT_TNM is accepted here without reservation,
+ // this can only affect data being read from a file.
+ if( aUnit != UNIT_MM && aUnit != UNIT_THOU && aUnit != UNIT_TNM )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: invalid IDF UNIT (must be one of UNIT_MM or UNIT_THOU): " << aUnit << "\n";
+ ostr << "* outline type: " << GetOutlineTypeString( outlineType );
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ unit = aUnit;
+ return true;
+}
+
+IDF3::IDF_UNIT BOARD_OUTLINE::GetUnit( void )
+{
+ return unit;
+}
+
+bool BOARD_OUTLINE::setThickness( double aThickness )
+{
+ if( aThickness < 0.0 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: aThickness < 0.0\n";
+ ostr << "* outline type: " << GetOutlineTypeString( outlineType );
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ thickness = aThickness;
+ return true;
+}
+
+bool BOARD_OUTLINE::SetThickness( double aThickness )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ return setThickness( aThickness );
+}
+
+double BOARD_OUTLINE::GetThickness( void )
+{
+ return thickness;
+}
+
+void BOARD_OUTLINE::readData( std::ifstream& aBoardFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion )
+{
+ // BOARD_OUTLINE (PANEL_OUTLINE)
+ // .BOARD_OUTLINE [OWNER]
+ // [thickness]
+ // [outlines]
+
+ // check RECORD 1
+ std::string token;
+ bool quoted = false;
+ int idx = 0;
+ std::streampos pos;
+
+ pos = aBoardFile.tellg();
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid invocation: blank header line" ) );
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: section names may not be in quotes\n";
+ ostr << "* line: '" << aHeader << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !CompareToken( ".BOARD_OUTLINE", token ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: not a board outline\n";
+ ostr << "* line: '" << aHeader << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ {
+ if( aIdfVersion > IDF_V2 )
+ ERROR_IDF << "no OWNER; setting to UNOWNED\n";
+
+ owner = UNOWNED;
+ }
+ else
+ {
+ if( !ParseOwner( token, owner ) )
+ {
+ ERROR_IDF << "invalid OWNER (reverting to UNOWNED): " << token << "\n";
+ owner = UNOWNED;
+ }
+ }
+
+ // check RECORD 2
+ std::string iline;
+ bool comment = false;
+ while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
+
+ if( ( !aBoardFile.good() && !aBoardFile.eof() ) || iline.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within .BOARD_OUTLINE section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no thickness specified\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ std::stringstream teststr;
+ teststr << token;
+
+ teststr >> thickness;
+ if( teststr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: invalid RECORD 2 (thickness)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( unit == UNIT_THOU )
+ {
+ thickness *= IDF_THOU_TO_MM;
+ }
+ else if( ( aIdfVersion == IDF_V2 ) && ( unit == UNIT_TNM ) )
+ {
+ thickness *= IDF_TNM_TO_MM;
+ }
+ else if( unit != UNIT_MM )
+ {
+ ostringstream ostr;
+ ostr << "\n* BUG: invalid UNIT type: " << unit;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ // for some unknown reason IDF allows 0 or negative thickness, but this
+ // is a problem so we fix it here
+ if( thickness <= 0.0 )
+ {
+ if( thickness == 0.0 )
+ {
+ ERROR_IDF << "\n* WARNING: setting board thickness to default 1.6mm (";
+ cerr << thickness << ")\n";
+ thickness = 1.6;
+ }
+ else
+ {
+ thickness = -thickness;
+ ERROR_IDF << "\n* WARNING: setting board thickness to positive number (";
+ cerr << thickness << ")\n";
+ }
+ }
+
+ // read RECORD 3 values
+ readOutlines( aBoardFile, aIdfVersion );
+
+ // check RECORD 4
+ while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
+
+ if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !CompareToken( ".END_BOARD_OUTLINE", iline ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no .END_BOARD_OUTLINE found\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ return;
+}
+
+
+void BOARD_OUTLINE::writeData( std::ofstream& aBoardFile )
+{
+ writeComments( aBoardFile );
+
+ // note: a BOARD_OUTLINE section is required, even if it is empty
+ aBoardFile << ".BOARD_OUTLINE ";
+
+ writeOwner( aBoardFile );
+
+ if( unit != UNIT_THOU )
+ aBoardFile << setiosflags(ios::fixed) << setprecision(5) << thickness << "\n";
+ else
+ aBoardFile << setiosflags(ios::fixed) << setprecision(1) << (thickness / IDF_THOU_TO_MM) << "\n";
+
+ writeOutlines( aBoardFile );
+
+ aBoardFile << ".END_BOARD_OUTLINE\n\n";
+
+ return;
+}
+
+void BOARD_OUTLINE::clear( void )
+{
+ comments.clear();
+ clearOutlines();
+
+ owner = UNOWNED;
+ return;
+}
+
+bool BOARD_OUTLINE::Clear( void )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ clear();
+
+ return true;
+}
+
+void BOARD_OUTLINE::setParent( IDF3_BOARD* aParent )
+{
+ parent = aParent;
+}
+
+IDF3_BOARD* BOARD_OUTLINE::GetParent( void )
+{
+ return parent;
+}
+
+bool BOARD_OUTLINE::addOutline( IDF_OUTLINE* aOutline )
+{
+ std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
+ std::list< IDF_OUTLINE* >::iterator itE = outlines.end();
+
+ try
+ {
+ while( itS != itE )
+ {
+ if( *itS == aOutline )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "duplicate outline pointer" ) );
+
+ ++itS;
+ }
+
+ outlines.push_back( aOutline );
+
+ }
+ catch( const std::exception& e )
+ {
+ errormsg = e.what();
+
+ return false;
+ }
+
+ return true;
+}
+
+bool BOARD_OUTLINE::AddOutline( IDF_OUTLINE* aOutline )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ return addOutline( aOutline );
+}
+
+bool BOARD_OUTLINE::DelOutline( IDF_OUTLINE* aOutline )
+{
+ std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
+ std::list< IDF_OUTLINE* >::iterator itE = outlines.end();
+
+ if( !aOutline )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: NULL aOutline pointer\n";
+ ostr << "* outline type: " << GetOutlineTypeString( outlineType );
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ if( outlines.empty() )
+ {
+ errormsg.clear();
+ return false;
+ }
+
+ // if there are more than 1 outlines it makes no sense to delete
+ // the first outline (board outline) since that would have the
+ // undesirable effect of substituting a cutout outline as the board outline
+ if( aOutline == outlines.front() )
+ {
+ if( outlines.size() > 1 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: attempting to delete first outline in list\n";
+ ostr << "* outline type: " << GetOutlineTypeString( outlineType );
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ outlines.clear();
+ return true;
+ }
+
+ while( itS != itE )
+ {
+ if( *itS == aOutline )
+ {
+ outlines.erase( itS );
+ return true;
+ }
+
+ ++itS;
+ }
+
+ errormsg.clear();
+ return false;
+}
+
+
+bool BOARD_OUTLINE::DelOutline( size_t aIndex )
+{
+ std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
+
+ if( outlines.empty() )
+ {
+ errormsg.clear();
+ return false;
+ }
+
+ if( aIndex >= outlines.size() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: index out of bounds (" << aIndex << " / " << outlines.size() << ")\n";
+ ostr << "* outline type: " << GetOutlineTypeString( outlineType );
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ if( aIndex == 0 )
+ {
+ // if there are more than 1 outlines it makes no sense to delete
+ // the first outline (board outline) since that would have the
+ // undesirable effect of substituting a cutout outline as the board outline
+ if( outlines.size() > 1 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: attempting to delete first outline in list\n";
+ ostr << "* outline type: " << GetOutlineTypeString( outlineType );
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ delete *itS;
+ outlines.clear();
+
+ return true;
+ }
+
+ for( ; aIndex > 0; --aIndex )
+ ++itS;
+
+ delete *itS;
+ outlines.erase( itS );
+
+ return true;
+}
+
+const std::list< IDF_OUTLINE* >*const BOARD_OUTLINE::GetOutlines( void )
+{
+ return &outlines;
+}
+
+size_t BOARD_OUTLINE::OutlinesSize( void )
+{
+ return outlines.size();
+}
+
+IDF_OUTLINE* BOARD_OUTLINE::GetOutline( size_t aIndex )
+{
+ if( aIndex >= outlines.size() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* aIndex (" << aIndex << ") is out of range (" << outlines.size() << ")";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
+
+ for( ; aIndex > 0; --aIndex )
+ ++itS;
+
+ return *itS;
+}
+
+IDF3::KEY_OWNER BOARD_OUTLINE::GetOwner( void )
+{
+ return owner;
+}
+
+bool BOARD_OUTLINE::SetOwner( IDF3::KEY_OWNER aOwner )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ owner = aOwner;
+ return true;
+}
+
+bool BOARD_OUTLINE::IsSingle( void )
+{
+ return single;
+}
+
+void BOARD_OUTLINE::clearOutlines( void )
+{
+ std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
+ std::list< IDF_OUTLINE* >::iterator itE = outlines.end();
+
+ while( itS != itE )
+ {
+ delete *itS;
+ ++itS;
+ }
+
+ outlines.clear();
+ return;
+}
+
+void BOARD_OUTLINE::AddComment( const std::string& aComment )
+{
+ if( aComment.empty() )
+ return;
+
+ comments.push_back( aComment );
+ return;
+}
+
+size_t BOARD_OUTLINE::CommentsSize( void )
+{
+ return comments.size();
+}
+
+std::list< std::string >* BOARD_OUTLINE::GetComments( void )
+{
+ return &comments;
+}
+
+const std::string* BOARD_OUTLINE::GetComment( size_t aIndex )
+{
+ if( aIndex >= comments.size() )
+ return NULL;
+
+ std::list< std::string >::iterator itS = comments.begin();
+
+ for( ; aIndex > 0; --aIndex )
+ ++itS;
+
+ return &(*itS);
+}
+
+bool BOARD_OUTLINE::DeleteComment( size_t aIndex )
+{
+ if( aIndex >= comments.size() )
+ return false;
+
+ std::list< std::string >::iterator itS = comments.begin();
+
+ for( ; aIndex > 0; --aIndex )
+ ++itS;
+
+ comments.erase( itS );
+ return true;
+}
+
+void BOARD_OUTLINE::ClearComments( void )
+{
+ comments.clear();
+ return;
+}
+
+
+/*
+ * CLASS: OTHER_OUTLINE
+ */
+OTHER_OUTLINE::OTHER_OUTLINE( IDF3_BOARD* aParent )
+{
+ setParent( aParent );
+ outlineType = OTLN_OTHER;
+ side = LYR_INVALID;
+ single = false;
+
+ return;
+}
+
+bool OTHER_OUTLINE::SetOutlineIdentifier( const std::string aUniqueID )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ uniqueID = aUniqueID;
+
+ return true;
+}
+
+const std::string& OTHER_OUTLINE::GetOutlineIdentifier( void )
+{
+ return uniqueID;
+}
+
+bool OTHER_OUTLINE::SetSide( IDF3::IDF_LAYER aSide )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ switch( aSide )
+ {
+ case LYR_TOP:
+ case LYR_BOTTOM:
+ side = aSide;
+ break;
+
+ default:
+ do{
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: invalid side (" << aSide << "); must be one of TOP/BOTTOM\n";
+ ostr << "* outline type: " << GetOutlineTypeString( outlineType );
+ errormsg = ostr.str();
+ } while( 0 );
+
+ side = LYR_INVALID;
+ return false;
+
+ break;
+ }
+
+ return true;
+}
+
+IDF3::IDF_LAYER OTHER_OUTLINE::GetSide( void )
+{
+ return side;
+}
+
+void OTHER_OUTLINE::readData( std::ifstream& aBoardFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion )
+{
+ // OTHER_OUTLINE/VIA_KEEPOUT
+ // .OTHER_OUTLINE [OWNER]
+ // [outline identifier] [thickness] [board side: Top/Bot] {not present in VA\IA KEEPOUT}
+ // [outline]
+
+ // check RECORD 1
+ std::string token;
+ bool quoted = false;
+ int idx = 0;
+ std::streampos pos = aBoardFile.tellg();
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+ ostr << "\n* BUG: invalid invocation: blank header line\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: section names must not be in quotes\n";
+ ostr << "* line: '" << aHeader << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( outlineType == OTLN_OTHER )
+ {
+ if( !CompareToken( ".OTHER_OUTLINE", token ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* BUG: not an .OTHER outline\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+ else
+ {
+ if( !CompareToken( ".VIA_KEEPOUT", token ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* BUG: not a .VIA_KEEPOUT outline\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ {
+ if( aIdfVersion > IDF_V2 )
+ ERROR_IDF << "no OWNER; setting to UNOWNED\n";
+
+ owner = UNOWNED;
+ }
+ else
+ {
+ if( !ParseOwner( token, owner ) )
+ {
+ ERROR_IDF << "invalid OWNER (reverting to UNOWNED): " << token << "\n";
+ owner = UNOWNED;
+ }
+ }
+
+ std::string iline;
+ bool comment = false;
+
+ if( outlineType == OTLN_OTHER )
+ {
+ // check RECORD 2
+ // [outline identifier] [thickness] [board side: Top/Bot]
+ while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
+
+ if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within .OTHER_OUTLINE section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no outline identifier\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ uniqueID = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no thickness\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ std::stringstream teststr;
+ teststr << token;
+
+ teststr >> thickness;
+ if( teststr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: invalid thickness\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( unit == UNIT_THOU )
+ {
+ thickness *= IDF_THOU_TO_MM;
+ }
+ else if( ( aIdfVersion == IDF_V2 ) && ( unit == UNIT_TNM ) )
+ {
+ thickness *= IDF_TNM_TO_MM;
+ }
+ else if( unit != UNIT_MM )
+ {
+ ostringstream ostr;
+ ostr << "\n* BUG: invalid UNIT type: " << unit;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( aIdfVersion == IDF_V2 )
+ {
+ side = LYR_TOP;
+ }
+ else
+ {
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no board side\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !ParseIDFLayer( token, side ) || ( side != LYR_TOP && side != LYR_BOTTOM ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: invalid side (must be TOP or BOTTOM only)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+
+ }
+
+ // read RECORD 3 values
+ readOutlines( aBoardFile, aIdfVersion );
+
+ // check RECORD 4
+ while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
+
+ if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( outlineType == OTLN_OTHER )
+ {
+ if( !CompareToken( ".END_OTHER_OUTLINE", iline ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no .END_OTHER_OUTLINE found\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+ else
+ {
+ if( !CompareToken( ".END_VIA_KEEPOUT", iline ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no .END_VIA_KEEPOUT found\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+
+ return;
+}
+
+void OTHER_OUTLINE::writeData( std::ofstream& aBoardFile )
+{
+ // this section is optional; do not write if not required
+ if( outlines.empty() )
+ return;
+
+ writeComments( aBoardFile );
+
+ // write RECORD 1
+ if( outlineType == OTLN_OTHER )
+ aBoardFile << ".OTHER_OUTLINE ";
+ else
+ aBoardFile << ".VIA_KEEPOUT ";
+
+ writeOwner( aBoardFile );
+
+ // write RECORD 2
+ if( outlineType == OTLN_OTHER )
+ {
+ aBoardFile << "\"" << uniqueID << "\" ";
+
+ if( unit != UNIT_THOU )
+ aBoardFile << setiosflags(ios::fixed) << setprecision(5) << thickness << " ";
+ else
+ aBoardFile << setiosflags(ios::fixed) << setprecision(1) << (thickness / IDF_THOU_TO_MM) << " ";
+
+ switch( side )
+ {
+ case LYR_TOP:
+ case LYR_BOTTOM:
+ WriteLayersText( aBoardFile, side );
+ break;
+
+ default:
+ do{
+ ostringstream ostr;
+ ostr << "\n* invalid OTHER_OUTLINE side (neither top nor bottom): ";
+ ostr << side;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ } while( 0 );
+
+ break;
+ }
+ }
+
+ // write RECORD 3
+ writeOutlines( aBoardFile );
+
+ // write RECORD 4
+ if( outlineType == OTLN_OTHER )
+ aBoardFile << ".END_OTHER_OUTLINE\n\n";
+ else
+ aBoardFile << ".END_VIA_KEEPOUT\n\n";
+
+ return;
+}
+
+
+bool OTHER_OUTLINE::Clear( void )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ clear();
+ side = LYR_INVALID;
+ uniqueID.clear();
+
+ return true;
+}
+
+
+/*
+ * CLASS: ROUTE_OUTLINE
+ */
+ROUTE_OUTLINE::ROUTE_OUTLINE( IDF3_BOARD* aParent )
+{
+ setParent( aParent );
+ outlineType = OTLN_ROUTE;
+ single = true;
+ layers = LYR_INVALID;
+}
+
+bool ROUTE_OUTLINE::SetLayers( IDF3::IDF_LAYER aLayer )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ layers = aLayer;
+
+ return true;
+}
+
+IDF3::IDF_LAYER ROUTE_OUTLINE::GetLayers( void )
+{
+ return layers;
+}
+
+void ROUTE_OUTLINE::readData( std::ifstream& aBoardFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion )
+{
+ // ROUTE_OUTLINE (or ROUTE_KEEPOUT)
+ // .ROUTE_OUTLINE [OWNER]
+ // [layers]
+ // [outline]
+
+ // check RECORD 1
+ std::string token;
+ bool quoted = false;
+ int idx = 0;
+ std::streampos pos = aBoardFile.tellg();
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ {
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: invalid invocation; blank header line" ) );
+ }
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: section names must not be in quotes\n";
+ ostr << "* line: '" << aHeader << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( outlineType == OTLN_ROUTE )
+ {
+ if( !CompareToken( ".ROUTE_OUTLINE", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: not a ROUTE outline" ) );
+ }
+ else
+ {
+ if( !CompareToken( ".ROUTE_KEEPOUT", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: not a ROUTE KEEPOUT outline" ) );
+ }
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ {
+ if( aIdfVersion > IDF_V2 )
+ ERROR_IDF << "no OWNER; setting to UNOWNED\n";
+
+ owner = UNOWNED;
+ }
+ else
+ {
+ if( !ParseOwner( token, owner ) )
+ {
+ ERROR_IDF << "invalid OWNER (reverting to UNOWNED): " << token << "\n";
+ owner = UNOWNED;
+ }
+ }
+
+ // check RECORD 2
+ // [layers: TOP, BOTTOM, BOTH, INNER, ALL]
+ std::string iline;
+ bool comment = false;
+
+ if( aIdfVersion > IDF_V2 || outlineType == OTLN_ROUTE_KEEPOUT )
+ {
+ while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
+
+ if( !aBoardFile.good() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within a section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no layers specification\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: layers specification must not be in quotes\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !ParseIDFLayer( token, layers ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: invalid layers specification\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( aIdfVersion == IDF_V2 )
+ {
+ if( layers == LYR_INNER || layers == LYR_ALL )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: IDFv2 allows only TOP/BOTTOM/BOTH; layer was '";
+ ostr << token << "'\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+
+ } // RECORD 2, conditional > IDFv2 or ROUTE_KO_OUTLINE
+ else
+ {
+ layers = LYR_ALL;
+ }
+
+ // read RECORD 3 values
+ readOutlines( aBoardFile, aIdfVersion );
+
+ // check RECORD 4
+ while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
+
+ if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( outlineType == OTLN_ROUTE )
+ {
+ if( !CompareToken( ".END_ROUTE_OUTLINE", iline ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no .END_ROUTE_OUTLINE found\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+ else
+ {
+ if( !CompareToken( ".END_ROUTE_KEEPOUT", iline ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no .END_ROUTE_KEEPOUT found\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+
+ return;
+}
+
+
+void ROUTE_OUTLINE::writeData( std::ofstream& aBoardFile )
+{
+ // this section is optional; do not write if not required
+ if( outlines.empty() )
+ return;
+
+ if( layers == LYR_INVALID )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "layer not specified" ) );
+
+ writeComments( aBoardFile );
+
+ // write RECORD 1
+ if( outlineType == OTLN_ROUTE )
+ aBoardFile << ".ROUTE_OUTLINE ";
+ else
+ aBoardFile << ".ROUTE_KEEPOUT ";
+
+ writeOwner( aBoardFile );
+
+ // write RECORD 2
+ WriteLayersText( aBoardFile, layers );
+ aBoardFile << "\n";
+
+ // write RECORD 3
+ writeOutlines( aBoardFile );
+
+ // write RECORD 4
+ if( outlineType == OTLN_ROUTE )
+ aBoardFile << ".END_ROUTE_OUTLINE\n\n";
+ else
+ aBoardFile << ".END_ROUTE_KEEPOUT\n\n";
+
+ return;
+}
+
+
+bool ROUTE_OUTLINE::Clear( void )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ clear();
+ layers = LYR_INVALID;
+
+ return true;
+}
+
+
+/*
+ * CLASS: PLACE_OUTLINE
+ */
+PLACE_OUTLINE::PLACE_OUTLINE( IDF3_BOARD* aParent )
+{
+ setParent( aParent );
+ outlineType = OTLN_PLACE;
+ single = true;
+ thickness = -1.0;
+ side = LYR_INVALID;
+}
+
+
+bool PLACE_OUTLINE::SetSide( IDF3::IDF_LAYER aSide )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ switch( aSide )
+ {
+ case LYR_TOP:
+ case LYR_BOTTOM:
+ case LYR_BOTH:
+ side = aSide;
+ break;
+
+ default:
+ do{
+ side = LYR_INVALID;
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: invalid layer (" << aSide << "): must be one of TOP/BOTTOM/BOTH\n";
+ ostr << "* outline type: " << GetOutlineTypeString( outlineType );
+ errormsg = ostr.str();
+
+ return false;
+ } while( 0 );
+
+ break;
+ }
+
+ return true;
+}
+
+
+IDF3::IDF_LAYER PLACE_OUTLINE::GetSide( void )
+{
+ return side;
+}
+
+
+bool PLACE_OUTLINE::SetMaxHeight( double aHeight )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ if( aHeight < 0.0 )
+ {
+ thickness = 0.0;
+
+ do{
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: invalid height (" << aHeight << "): must be >= 0.0";
+ ostr << "* outline type: " << GetOutlineTypeString( outlineType );
+ errormsg = ostr.str();
+
+ return false;
+ } while( 0 );
+ }
+
+ thickness = aHeight;
+ return true;
+}
+
+double PLACE_OUTLINE::GetMaxHeight( void )
+{
+ return thickness;
+}
+
+void PLACE_OUTLINE::readData( std::ifstream& aBoardFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion )
+{
+ // PLACE_OUTLINE/KEEPOUT
+ // .PLACE_OUTLINE [OWNER]
+ // [board side: Top/Bot/Both] [height]
+ // [outline]
+
+ // check RECORD 1
+ std::string token;
+ bool quoted = false;
+ int idx = 0;
+ std::streampos pos = aBoardFile.tellg();
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: invalid invocation: blank header line\n" ) );
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: section name must not be in quotes\n";
+ ostr << "* line: '" << aHeader << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( outlineType == OTLN_PLACE )
+ {
+ if( !CompareToken( ".PLACE_OUTLINE", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: not a .PLACE_OUTLINE" ) );
+ }
+ else
+ {
+ if( !CompareToken( ".PLACE_KEEPOUT", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: not a .PLACE_KEEPOUT" ) );
+ }
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ {
+ if( aIdfVersion > IDF_V2 )
+ ERROR_IDF << "no OWNER; setting to UNOWNED\n";
+
+ owner = UNOWNED;
+ }
+ else
+ {
+ if( !ParseOwner( token, owner ) )
+ {
+ ERROR_IDF << "invalid OWNER (reverting to UNOWNED): " << token << "\n";
+ owner = UNOWNED;
+ }
+ }
+
+ // check RECORD 2
+ // [board side: Top/Bot/Both] [height]
+ std::string iline;
+ bool comment = false;
+
+ if( aIdfVersion > IDF_V2 || outlineType == OTLN_PLACE_KEEPOUT )
+ {
+ while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
+
+ if( !aBoardFile.good() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within the section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no board side information\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !ParseIDFLayer( token, side ) ||
+ ( side != LYR_TOP && side != LYR_BOTTOM && side != LYR_BOTH ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: invalid board side: must be one of TOP/BOTTOM/BOTH\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( GetIDFString( iline, token, quoted, idx ) )
+ {
+ std::stringstream teststr;
+ teststr << token;
+
+ teststr >> thickness;
+
+ if( teststr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: invalid height\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( thickness < 0.0 )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: thickness < 0\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( unit == UNIT_THOU )
+ {
+ thickness *= IDF_THOU_TO_MM;
+ }
+ else if( ( aIdfVersion == IDF_V2 ) && ( unit == UNIT_TNM ) )
+ {
+ thickness *= IDF_TNM_TO_MM;
+ }
+ else if( unit != UNIT_MM )
+ {
+ ostringstream ostr;
+ ostr << "\n* BUG: invalid UNIT type: " << unit;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( thickness < 0.0 )
+ thickness = 0.0;
+
+ }
+ else
+ {
+ // for OTLN_PLACE, thickness may be omitted, but is required for OTLN_PLACE_KEEPOUT
+ if( outlineType == OTLN_PLACE_KEEPOUT )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: missing thickness\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ thickness = -1.0;
+ }
+ }
+ else
+ {
+ side = LYR_TOP;
+ thickness = 0.0;
+ }
+
+ // read RECORD 3 values
+ readOutlines( aBoardFile, aIdfVersion );
+
+ // check RECORD 4
+ while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
+
+ if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( outlineType == OTLN_PLACE )
+ {
+ if( !GetIDFString( iline, token, quoted, idx )
+ || !CompareToken( ".END_PLACE_OUTLINE", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid .PLACE_OUTLINE section: no .END_PLACE_OUTLINE found" ) );
+ }
+ else
+ {
+ if( !GetIDFString( iline, token, quoted, idx )
+ || !CompareToken( ".END_PLACE_KEEPOUT", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid .PLACE_KEEPOUT section: no .END_PLACE_KEEPOUT found" ) );
+ }
+
+ return;
+}
+
+void PLACE_OUTLINE::writeData( std::ofstream& aBoardFile )
+{
+ // this section is optional; do not write if not required
+ if( outlines.empty() )
+ return;
+
+ writeComments( aBoardFile );
+
+ // write RECORD 1
+ if( outlineType == OTLN_PLACE )
+ aBoardFile << ".PLACE_OUTLINE ";
+ else
+ aBoardFile << ".PLACE_KEEPOUT ";
+
+ writeOwner( aBoardFile );
+
+ // write RECORD 2
+ switch( side )
+ {
+ case LYR_TOP:
+ case LYR_BOTTOM:
+ case LYR_BOTH:
+ WriteLayersText( aBoardFile, side );
+ break;
+
+ default:
+ do
+ {
+ ostringstream ostr;
+ ostr << "\n* invalid PLACE_OUTLINE/KEEPOUT side (";
+ ostr << side << "); must be one of TOP/BOTTOM/BOTH";
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ } while( 0 );
+
+ break;
+ }
+
+ // thickness is optional for OTLN_PLACE, but mandatory for OTLN_PLACE_KEEPOUT
+ if( thickness < 0.0 && outlineType == OTLN_PLACE_KEEPOUT)
+ {
+ aBoardFile << "\n";
+ }
+ else
+ {
+ aBoardFile << " ";
+
+ if( unit != UNIT_THOU )
+ aBoardFile << setiosflags(ios::fixed) << setprecision(5) << thickness << "\n";
+ else
+ aBoardFile << setiosflags(ios::fixed) << setprecision(1) << (thickness / IDF_THOU_TO_MM) << "\n";
+ }
+
+ // write RECORD 3
+ writeOutlines( aBoardFile );
+
+ // write RECORD 4
+ if( outlineType == OTLN_PLACE )
+ aBoardFile << ".END_PLACE_OUTLINE\n\n";
+ else
+ aBoardFile << ".END_PLACE_KEEPOUT\n\n";
+
+ return;
+}
+
+
+bool PLACE_OUTLINE::Clear( void )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ clear();
+ thickness = 0.0;
+ side = LYR_INVALID;
+
+ return true;
+}
+
+
+/*
+ * CLASS: ROUTE_KEEPOUT
+ */
+ROUTE_KO_OUTLINE::ROUTE_KO_OUTLINE( IDF3_BOARD* aParent )
+ : ROUTE_OUTLINE( aParent )
+{
+ outlineType = OTLN_ROUTE_KEEPOUT;
+ return;
+}
+
+
+/*
+ * CLASS: PLACE_KEEPOUT
+ */
+PLACE_KO_OUTLINE::PLACE_KO_OUTLINE( IDF3_BOARD* aParent )
+ : PLACE_OUTLINE( aParent )
+{
+ outlineType = OTLN_PLACE_KEEPOUT;
+ return;
+}
+
+
+/*
+ * CLASS: VIA_KEEPOUT
+ */
+VIA_KO_OUTLINE::VIA_KO_OUTLINE( IDF3_BOARD* aParent )
+ : OTHER_OUTLINE( aParent )
+{
+ single = true;
+ outlineType = OTLN_VIA_KEEPOUT;
+}
+
+
+/*
+ * CLASS: PLACEMENT GROUP (PLACE_REGION)
+ */
+GROUP_OUTLINE::GROUP_OUTLINE( IDF3_BOARD* aParent )
+{
+ setParent( aParent );
+ outlineType = OTLN_GROUP_PLACE;
+ thickness = 0.0;
+ side = LYR_INVALID;
+ single = true;
+ return;
+}
+
+
+bool GROUP_OUTLINE::SetSide( IDF3::IDF_LAYER aSide )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ switch( aSide )
+ {
+ case LYR_TOP:
+ case LYR_BOTTOM:
+ case LYR_BOTH:
+ side = aSide;
+ break;
+
+ default:
+ do{
+ ostringstream ostr;
+ ostr << "invalid side (" << aSide << "); must be one of TOP/BOTTOM/BOTH\n";
+ ostr << "* outline type: " << GetOutlineTypeString( outlineType );
+ errormsg = ostr.str();
+
+ return false;
+ } while( 0 );
+
+ break;
+ }
+
+ return true;
+}
+
+
+IDF3::IDF_LAYER GROUP_OUTLINE::GetSide( void )
+{
+ return side;
+}
+
+
+bool GROUP_OUTLINE::SetGroupName( std::string aGroupName )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ groupName = aGroupName;
+
+ return true;
+}
+
+
+const std::string& GROUP_OUTLINE::GetGroupName( void )
+{
+ return groupName;
+}
+
+
+void GROUP_OUTLINE::readData( std::ifstream& aBoardFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion )
+{
+ // Placement Group
+ // .PLACE_REGION [OWNER]
+ // [side: Top/Bot/Both ] [component group name]
+ // [outline]
+
+ // check RECORD 1
+ std::string token;
+ bool quoted = false;
+ int idx = 0;
+ std::streampos pos = aBoardFile.tellg();
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: invalid invocation: blank header line" ) );
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: section name must not be in quotes\n";
+ ostr << "* line: '" << aHeader << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !CompareToken( ".PLACE_REGION", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: not a .PLACE_REGION" ) );
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ {
+ if( aIdfVersion > IDF_V2 )
+ ERROR_IDF << "no OWNER; setting to UNOWNED\n";
+
+ owner = UNOWNED;
+ }
+ else
+ {
+ if( !ParseOwner( token, owner ) )
+ {
+ ERROR_IDF << "invalid OWNER (reverting to UNOWNED): " << token << "\n";
+ owner = UNOWNED;
+ }
+ }
+
+ std::string iline;
+ bool comment = false;
+
+ // check RECORD 2
+ // [side: Top/Bot/Both ] [component group name]
+ while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
+
+ if( !aBoardFile.good() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no board side specified\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !ParseIDFLayer( token, side ) ||
+ ( side != LYR_TOP && side != LYR_BOTTOM && side != LYR_BOTH ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: invalid board side, must be one of TOP/BOTTOM/BOTH\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no outline identifier\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ groupName = token;
+
+ // read RECORD 3 values
+ readOutlines( aBoardFile, aIdfVersion );
+
+ // check RECORD 4
+ while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
+
+ if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx )
+ || !CompareToken( ".END_PLACE_REGION", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* invalid .PLACE_REGION section: no .END_PLACE_REGION found" ) );
+
+ return;
+}
+
+
+void GROUP_OUTLINE::writeData( std::ofstream& aBoardFile )
+{
+ // this section is optional; do not write if not required
+ if( outlines.empty() )
+ return;
+
+ writeComments( aBoardFile );
+
+ // write RECORD 1
+ aBoardFile << ".PLACE_REGION ";
+
+ writeOwner( aBoardFile );
+
+ // write RECORD 2
+ switch( side )
+ {
+ case LYR_TOP:
+ case LYR_BOTTOM:
+ case LYR_BOTH:
+ WriteLayersText( aBoardFile, side );
+ break;
+
+ default:
+ do{
+ ostringstream ostr;
+ ostr << "\n* invalid PLACE_REGION side (must be TOP/BOTTOM/BOTH): ";
+ ostr << side;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ } while( 0 );
+
+ break;
+ }
+
+ aBoardFile << " \"" << groupName << "\"\n";
+
+ // write RECORD 3
+ writeOutlines( aBoardFile );
+
+ // write RECORD 4
+ aBoardFile << ".END_PLACE_REGION\n\n";
+
+ return;
+}
+
+bool GROUP_OUTLINE::Clear( void )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ clear();
+ thickness = 0.0;
+ side = LYR_INVALID;
+ groupName.clear();
+
+ return true;
+}
+
+/*
+ * CLASS: COMPONENT OUTLINE
+ */
+IDF3_COMP_OUTLINE::IDF3_COMP_OUTLINE( IDF3_BOARD* aParent )
+{
+ setParent( aParent );
+ single = true;
+ outlineType = OTLN_COMPONENT;
+ compType = COMP_INVALID;
+ refNum = 0;
+ return;
+}
+
+void IDF3_COMP_OUTLINE::readProperties( std::ifstream& aLibFile )
+{
+ bool quoted = false;
+ bool comment = false;
+ std::string iline;
+ std::string token;
+ std::streampos pos;
+ std::string pname; // property name
+ std::string pval; // property value
+ int idx = 0;
+
+ while( aLibFile.good() )
+ {
+ if( !FetchIDFLine( aLibFile, iline, comment, pos ) )
+ continue;
+
+ idx = 0;
+
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: bad property section (no PROP)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: PROP or .END must not be quoted\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( token.size() >= 5 && CompareToken( ".END_", token.substr( 0, 5 ) ) )
+ {
+ if(aLibFile.eof())
+ aLibFile.clear();
+
+ aLibFile.seekg( pos );
+ return;
+ }
+
+ if( !CompareToken( "PROP", token ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: expecting PROP or .END_ELECTRICAL\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no PROP name\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ pname = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no PROP value\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ pval = token;
+
+ if( props.insert( pair< string, string >(pname, pval) ).second == false )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: duplicate property name \"" << pname << "\"\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+
+ return;
+}
+
+
+bool IDF3_COMP_OUTLINE::writeProperties( std::ofstream& aLibFile )
+{
+ if( props.empty() )
+ return true;
+ std::map< std::string, std::string >::const_iterator itS = props.begin();
+ std::map< std::string, std::string >::const_iterator itE = props.end();
+
+ while( itS != itE )
+ {
+ aLibFile << "PROP " << "\"" << itS->first << "\" \""
+ << itS->second << "\"\n";
+ ++itS;
+ }
+
+ return !aLibFile.fail();
+}
+
+void IDF3_COMP_OUTLINE::readData( std::ifstream& aLibFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion )
+{
+ // .ELECTRICAL/.MECHANICAL
+ // [GEOM] [PART] [UNIT] [HEIGHT]
+ // [outline]
+ // [PROP] [prop name] [prop value]
+ // check RECORD 1
+ std::string token;
+ bool quoted = false;
+ int idx = 0;
+ std::streampos pos = aLibFile.tellg();
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: invalid invocation: blank header line" ) );
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: section name must not be in quotes\n";
+ ostr << "* line: '" << aHeader << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( CompareToken( ".ELECTRICAL", token ) )
+ {
+ compType = COMP_ELEC;
+ }
+ else if( CompareToken( ".MECHANICAL", token ) )
+ {
+ compType = COMP_MECH;
+ }
+ else
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: expecting .ELECTRICAL or .MECHANICAL header\n";
+ ostr << "* line: '" << aHeader << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ // check RECORD 2
+ // [GEOM] [PART] [UNIT] [HEIGHT]
+ std::string iline;
+ bool comment = false;
+
+ while( aLibFile.good() && !FetchIDFLine( aLibFile, iline, comment, pos ) );
+
+ if( !aLibFile.good() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no GEOMETRY NAME\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ geometry = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no PART NAME\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ part = token;
+
+ if( part.empty() && geometry.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: both GEOMETRY and PART names are empty\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no UNIT type\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( CompareToken( "MM", token ) )
+ {
+ unit = UNIT_MM;
+ }
+ else if( CompareToken( "THOU", token ) )
+ {
+ unit = UNIT_THOU;
+ }
+ else if( aIdfVersion == IDF_V2 && !CompareToken( "TNM", token ) )
+ {
+ unit = UNIT_TNM;
+ }
+ else
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: invalid UNIT '" << token << "': must be one of MM or THOU\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no height specified\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ std::istringstream teststr;
+ teststr.str( token );
+
+ teststr >> thickness;
+ if( teststr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: invalid height '" << token << "'\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( unit == UNIT_THOU )
+ {
+ thickness *= IDF_THOU_TO_MM;
+ }
+ else if( ( aIdfVersion == IDF_V2 ) && ( unit == UNIT_TNM ) )
+ {
+ thickness *= IDF_TNM_TO_MM;
+ }
+ else if( unit != UNIT_MM )
+ {
+ ostringstream ostr;
+ ostr << "\n* BUG: invalid UNIT type: " << unit;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ // read RECORD 3 values
+ readOutlines( aLibFile, aIdfVersion );
+
+ if( compType == COMP_ELEC && aIdfVersion > IDF_V2 )
+ readProperties( aLibFile );
+
+ // check RECORD 4
+ while( aLibFile.good() && !FetchIDFLine( aLibFile, iline, comment, pos ) );
+
+ if( ( !aLibFile.good() && aLibFile.eof() ) || iline.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( compType == COMP_ELEC )
+ {
+ if( !CompareToken( ".END_ELECTRICAL", iline ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no .END_ELECTRICAL found\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+ else
+ {
+ if( !CompareToken( ".END_MECHANICAL", iline ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no .END_MECHANICAL found\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+
+ return;
+}
+
+
+void IDF3_COMP_OUTLINE::writeData( std::ofstream& aLibFile )
+{
+ if( refNum == 0 )
+ return; // nothing to do
+
+ if( compType != COMP_ELEC && compType != COMP_MECH )
+ {
+ ostringstream ostr;
+ ostr << "\n* component type not set or invalid: " << compType;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ writeComments( aLibFile );
+
+ // note: the outline section is required, even if it is empty
+ if( compType == COMP_ELEC )
+ aLibFile << ".ELECTRICAL\n";
+ else
+ aLibFile << ".MECHANICAL\n";
+
+ // RECORD 2
+ // [GEOM] [PART] [UNIT] [HEIGHT]
+ aLibFile << "\"" << geometry << "\" \"" << part << "\" ";
+
+ if( unit != UNIT_THOU )
+ aLibFile << "MM " << setiosflags(ios::fixed) << setprecision(5) << thickness << "\n";
+ else
+ aLibFile << "THOU " << setiosflags(ios::fixed) << setprecision(1) << (thickness / IDF_THOU_TO_MM) << "\n";
+
+ writeOutlines( aLibFile );
+
+ if( compType == COMP_ELEC )
+ {
+ writeProperties( aLibFile );
+ aLibFile << ".END_ELECTRICAL\n\n";
+ }
+ else
+ {
+ aLibFile << ".END_MECHANICAL\n\n";
+ }
+
+ return;
+}
+
+
+bool IDF3_COMP_OUTLINE::Clear( void )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ clear();
+ uid.clear();
+ geometry.clear();
+ part.clear();
+ compType = COMP_INVALID;
+ refNum = 0;
+ props.clear();
+
+ return true;
+}
+
+bool IDF3_COMP_OUTLINE::SetComponentClass( IDF3::COMP_TYPE aCompClass )
+{
+ switch( aCompClass )
+ {
+ case COMP_ELEC:
+ case COMP_MECH:
+ compType = aCompClass;
+ break;
+
+ default:
+ do{
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: invalid component class (must be ELECTRICAL or MECHANICAL): ";
+ ostr << aCompClass << "\n";
+ errormsg = ostr.str();
+
+ return false;
+ } while( 0 );
+
+ break;
+ }
+
+ return true;
+}
+
+
+IDF3::COMP_TYPE IDF3_COMP_OUTLINE::GetComponentClass( void )
+{
+ return compType;
+}
+
+
+void IDF3_COMP_OUTLINE::SetGeomName( const std::string& aGeomName )
+{
+ geometry = aGeomName;
+ uid.clear();
+ return;
+}
+
+const std::string& IDF3_COMP_OUTLINE::GetGeomName( void )
+{
+ return geometry;
+}
+
+void IDF3_COMP_OUTLINE::SetPartName( const std::string& aPartName )
+{
+ part = aPartName;
+ uid.clear();
+ return;
+}
+
+const std::string& IDF3_COMP_OUTLINE::GetPartName( void )
+{
+ return part;
+}
+
+const std::string& IDF3_COMP_OUTLINE::GetUID( void )
+{
+ if( !uid.empty() )
+ return uid;
+
+ if( geometry.empty() && part.empty() )
+ return uid;
+
+ uid = geometry + "_" + part;
+
+ return uid;
+}
+
+
+int IDF3_COMP_OUTLINE::incrementRef( void )
+{
+ return ++refNum;
+}
+
+int IDF3_COMP_OUTLINE::decrementRef( void )
+{
+ if( refNum == 0 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: decrementing refNum beyond 0";
+ errormsg = ostr.str();
+
+ return -1;
+ }
+
+ --refNum;
+ return refNum;
+}
+
+bool IDF3_COMP_OUTLINE::CreateDefaultOutline( const std::string &aGeom, const std::string &aPart )
+{
+ Clear();
+
+ if( aGeom.empty() && aPart.empty() )
+ {
+ geometry = "NOGEOM";
+ part = "NOPART";
+ uid = "NOGEOM_NOPART";
+ }
+ else
+ {
+ geometry = aGeom;
+ part = aPart;
+ uid = aGeom + "_" + aPart;
+ }
+
+ compType = COMP_ELEC;
+ thickness = 5.0;
+ unit = UNIT_MM;
+
+ // Create a star shape 5mm high with points on 5 and 3 mm circles
+ double a, da;
+ da = M_PI / 5.0;
+ a = da / 2.0;
+
+ IDF_POINT p1, p2;
+ IDF_OUTLINE* ol = new IDF_OUTLINE;
+ IDF_SEGMENT* sp;
+
+ p1.x = 1.5 * cos( a );
+ p1.y = 1.5 * sin( a );
+
+ if( ol == NULL )
+ return false;
+
+ for( int i = 0; i < 10; ++i )
+ {
+ if( i & 1 )
+ {
+ p2.x = 2.5 * cos( a );
+ p2.y = 2.5 * sin( a );
+ }
+ else
+ {
+ p2.x = 1.5 * cos( a );
+ p2.y = 1.5 * sin( a );
+ }
+
+ sp = new IDF_SEGMENT( p1, p2 );
+
+ if( sp == NULL )
+ {
+ Clear();
+ return false;
+ }
+
+ ol->push( sp );
+ a += da;
+ p1 = p2;
+ }
+
+ a = da / 2.0;
+ p2.x = 1.5 * cos( a );
+ p2.y = 1.5 * sin( a );
+
+ sp = new IDF_SEGMENT( p1, p2 );
+
+ if( sp == NULL )
+ {
+ Clear();
+ return false;
+ }
+
+ ol->push( sp );
+ outlines.push_back( ol );
+
+ return true;
+}
diff --git a/utils/idftools/idf_outlines.h b/utils/idftools/idf_outlines.h
new file mode 100644
index 0000000..33957e7
--- /dev/null
+++ b/utils/idftools/idf_outlines.h
@@ -0,0 +1,771 @@
+/*
+ * 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
+ */
+
+
+#ifndef IDF_OUTLINES_H
+#define IDF_OUTLINES_H
+
+#include <string>
+#include <list>
+#include <map>
+#include <wx/string.h>
+#include <wx/filename.h>
+
+#include <idf_common.h>
+
+/*
+ * NOTES ON OUTLINE TYPES:
+ *
+ * BOARD_OUTLINE (PANEL_OUTLINE)
+ * .BOARD_OUTLINE [OWNER]
+ * [thickness]
+ * [outlines]
+ *
+ * OTHER_OUTLINE
+ * .OTHER_OUTLINE [OWNER]
+ * [outline identifier] [thickness] [board side: Top/Bot]
+ * [outline]
+ *
+ * ROUTE_OUTLINE
+ * .ROUTE_OUTLINE [OWNER]
+ * [layers]
+ * [outline]
+ *
+ * PLACE_OUTLINE
+ * .PLACE_OUTLINE [OWNER]
+ * [board side: Top/Bot/Both] [height]
+ * [outline]
+ *
+ * ROUTE_KEEPOUT
+ * .ROUTE_KEEPOUT [OWNER]
+ * [layers]
+ * [outline]
+ *
+ * VIA_KEEPOUT
+ * .VIA_KEEPOUT [OWNER]
+ * [outline]
+ *
+ * PLACE_KEEPOUT
+ * .PLACE_KEEPOUT [OWNER]
+ * [board side: Top/Bot/Both] [height]
+ * [outline]
+ *
+ * Placement Group
+ * .PLACE_REGION [OWNER]
+ * [side: Top/Bot/Both ] [component group name]
+ * [outline]
+ *
+ * Component Outline:
+ * .ELECTRICAL/.MECHANICAL
+ * [GEOM] [PART] [UNIT] [HEIGHT]
+ * [outline]
+ * [PROP] [prop name] [prop value]
+ */
+
+class IDF3_BOARD;
+
+
+/**
+ * Class BOARD_OUTLINE
+ * supports the IDFv3 BOARD OUTLINE data and is the basis of other IDFv3 outline classes
+ */
+class BOARD_OUTLINE
+{
+friend class IDF3_BOARD;
+protected:
+ std::string errormsg;
+ std::list< IDF_OUTLINE* > outlines;
+ IDF3::KEY_OWNER owner; // indicates the owner of this outline (MCAD, ECAD, UNOWNED)
+ IDF3::OUTLINE_TYPE outlineType;// type of IDF outline
+ bool single; // true if only a single outline is accepted
+ std::list< std::string > comments; // associated comment list
+ IDF3::IDF_UNIT unit; // outline's native unit (MM or THOU)
+ IDF3_BOARD* parent; // BOARD which contains this outline
+ double thickness; // Board/Extrude Thickness or Height (IDF spec)
+
+ // Read outline data from a BOARD or LIBRARY file's outline section
+ void readOutlines( std::ifstream& aBoardFile, IDF3::IDF_VERSION aIdfVersion );
+ // Write comments to a BOARD or LIBRARY file (must not be within a SECTION as per IDFv3 spec)
+ bool writeComments( std::ofstream& aBoardFile );
+ // Write the outline owner to a BOARD file
+ bool writeOwner( std::ofstream& aBoardFile );
+ // Write the data of a single outline object
+ void writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutline, size_t aIndex );
+ // Iterate through the outlines and write out all data
+ void writeOutlines( std::ofstream& aBoardFile ); // write outline data (no headers)
+ // Clear internal list of outlines
+ void clearOutlines( void );
+ /**
+ * Function SetParent
+ * sets the parent IDF_BOARD object
+ */
+ void setParent( IDF3_BOARD* aParent );
+
+ // Shadow routines used by friends to bypass ownership checks
+ bool addOutline( IDF_OUTLINE* aOutline );
+ virtual bool setThickness( double aThickness );
+ virtual void clear( void );
+
+ /**
+ * Function readData
+ * reads data from a .BOARD_OUTLINE section
+ * In case of an unrecoverable error an exception is thrown. On a successful
+ * return the file pointer will be at the line following .END_BOARD_OUTLINE
+ *
+ * @param aBoardFile is an IDFv3 file opened for reading
+ * @param aHeader is the ".BOARD_OUTLINE" header line as read by FetchIDFLine
+ */
+ virtual void readData( std::ifstream& aBoardFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion );
+
+ /**
+ * Function writeData
+ * writes the comments and .BOARD_OUTLINE section to an IDFv3 file.
+ * Throws exceptions.
+ *
+ * @param aBoardFile is an IDFv3 file opened for writing
+ */
+ virtual void writeData( std::ofstream& aBoardFile );
+
+public:
+ BOARD_OUTLINE();
+ virtual ~BOARD_OUTLINE();
+
+ /**
+ * Function SetUnit
+ * sets the native unit of the outline; except for component outlines this must
+ * be the same as the native unit of the parent IDF_BOARD object
+ *
+ * @param aUnit is the native unit (UNIT_MM or UNIT_THOU)
+ */
+ virtual bool SetUnit( IDF3::IDF_UNIT aUnit );
+
+ /**
+ * Function GetUnit
+ * returns the native unit type of the outline
+ *
+ * @return IDF_UNIT is the native unit (UNIT_MM or UNIT_THOU)
+ */
+ virtual IDF3::IDF_UNIT GetUnit( void );
+
+ /**
+ * Function SetThickness
+ * sets the thickness or height of the outline (mm)
+ *
+ * @param aThickness is the thickness or height of the outline in mm
+ */
+ virtual bool SetThickness( double aThickness );
+
+ /**
+ * Function GetThickness
+ * returns the thickness or height of an outline (mm)
+ */
+ virtual double GetThickness( void );
+
+ /**
+ * Function Clear
+ * frees memory and reinitializes all internal data except for the parent pointer.
+ *
+ * @return bool: true if OK, false on ownership violations
+ */
+ virtual bool Clear( void );
+
+ /**
+ * Function GetOutlineType
+ * returns the type of outline according to the IDFv3 classification
+ */
+ IDF3::OUTLINE_TYPE GetOutlineType( void );
+
+ /**
+ * Function GetParent
+ * returns the parent IDF_BOARD object
+ */
+ IDF3_BOARD* GetParent( void );
+
+
+ /**
+ * Function AddOutline
+ * adds the specified outline to this object.
+ *
+ * @param aOutline is a valid IDF outline
+ *
+ * @return bool: true if the outline was added; false if the outline
+ * already existed or an ownership violation occurs.
+ */
+ bool AddOutline( IDF_OUTLINE* aOutline );
+
+ /**
+ * Function DelOutline( IDF_OUTLINE* aOutline )
+ * removes the given outline, subject to IDF ownership rules,
+ * if it is owned by this object. The outline pointer remains
+ * valid and it is the user's responsibility to delete the object.
+ * The first outline in the list will never be deleted unless it
+ * is the sole remaining outline; this is to ensure that a board
+ * outline is not removed while the cutouts remain.
+ *
+ * @param aOutline is a pointer to the outline to remove from the list
+ *
+ * @return bool: true if the outline was found and removed; false if
+ * the outline was not found or an ownership violation occurs.
+ */
+ bool DelOutline( IDF_OUTLINE* aOutline );
+
+ /**
+ * Function DelOutline( IDF_OUTLINE* aOutline )
+ * deletes the outline specified by the given index, subject to
+ * IDF ownership rules. The outline data is destroyed.
+ * The first outline in the list will never be deleted unless it
+ * is the sole remaining outline; this is to ensure that a board
+ * outline is not removed while the cutouts remain.
+ *
+ * @param aIndex is an index to the outline to delete
+ *
+ * @return bool: true if the outline was found and deleted; false if
+ * the outline was not found or an ownership violation or indexation
+ * error occurs.
+ */
+ bool DelOutline( size_t aIndex );
+
+ /**
+ * Function GetOutlines
+ * returns a pointer to the internal outlines list. It is up to the
+ * user to respect the IDFv3 specification and avoid changes to this
+ * list which are in violation of the specification.
+ */
+ const std::list< IDF_OUTLINE* >*const GetOutlines( void );
+
+ /**
+ * Function OutlinesSize
+ * returns the number of items in the internal outline list
+ */
+ size_t OutlinesSize( void );
+
+ /**
+ * Function GetOutline
+ * returns a pointer to the outline as specified by aIndex.
+ * If the index is out of bounds NULL is returned and the
+ * error message is set. It is the responsibility of the
+ * user to observe IDF ownership rules.
+ */
+ IDF_OUTLINE* GetOutline( size_t aIndex );
+
+ /**
+ * Function GetOwner
+ * returns the ownership status of the outline ( ECAD, MCAD, UNOWNED)
+ */
+ IDF3::KEY_OWNER GetOwner( void );
+
+ /**
+ * Function SetOwner
+ * sets the ownership status of the outline subject to IDF
+ * ownership rules. The return value is true if the ownership
+ * was changed and false if a specification violation occurred.
+ */
+ bool SetOwner( IDF3::KEY_OWNER aOwner );
+
+ /**
+ * Function IsSingle
+ * return true if this type of outline only supports a single
+ * outline. All outlines except for BOARD_OUTLINE are single.
+ */
+ bool IsSingle( void );
+
+ /**
+ * Function ClearOutlines
+ * clears internal data except for the parent pointer
+ */
+ void ClearOutlines( void );
+
+ /**
+ * Function AddComment
+ * adds a comment to the outline data; this function is not
+ * subject to IDF ownership rules.
+ */
+ void AddComment( const std::string& aComment );
+
+ /**
+ * Function CommentSize
+ * returns the number of comments in the internal list
+ */
+ size_t CommentsSize( void );
+
+ /**
+ * Function GetComments
+ * returns a pointer to the internal list of comments
+ */
+ std::list< std::string >* GetComments( void );
+
+ /**
+ * Function GetComment
+ * returns the string representing the indexed comment or
+ * NULL if the index is out of bounds
+ */
+ const std::string* GetComment( size_t aIndex );
+
+ /**
+ * Function DeleteComment
+ * deletes a comment based on the given index.
+ *
+ * @return bool: true if a comment was deleted, false if
+ * the index is out of bounds.
+ */
+ bool DeleteComment( size_t aIndex );
+
+ /**
+ * Function ClearComments
+ * deletes all comments
+ */
+ void ClearComments( void );
+
+ const std::string& GetError( void )
+ {
+ return errormsg;
+ }
+};
+
+
+/**
+ * Class OTHER_OUTLINE
+ * describes miscellaneous extrusions on the board
+ */
+class OTHER_OUTLINE : public BOARD_OUTLINE
+{
+friend class IDF3_BOARD;
+private:
+ std::string uniqueID; // Outline Identifier (IDF spec)
+ IDF3::IDF_LAYER side; // Board Side [TOP/BOTTOM ONLY] (IDF spec)
+
+ /**
+ * Function readData
+ * reads an OTHER_OUTLINE data from an IDFv3 file.
+ * If an unrecoverable error occurs an exception is thrown.
+ *
+ * @param aBoardFile is an IDFv3 file open for reading
+ * @param aHeader is the .OTHER_OUTLINE header as read via FetchIDFLine
+ */
+ virtual void readData( std::ifstream& aBoardFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion );
+
+ /**
+ * Function writeData
+ * writes the OTHER_OUTLINE data to an open IDFv3 file
+ *
+ * @param aBoardFile is an IDFv3 file open for writing
+ *
+ * @return bool: true if the data was successfully written, otherwise false.
+ */
+ virtual void writeData( std::ofstream& aBoardFile );
+
+public:
+ OTHER_OUTLINE( IDF3_BOARD* aParent );
+
+ /**
+ * Function SetOutlineIdentifier
+ * sets the Outline Identifier string of this OTHER_OUTLINE object
+ * as per IDFv3 spec.
+ */
+ virtual bool SetOutlineIdentifier( const std::string aUniqueID );
+
+ /**
+ * Function GetOutlineIdentifier
+ * returns the object's Outline Identifier
+ */
+ virtual const std::string& GetOutlineIdentifier( void );
+
+ /**
+ * Function SetSide
+ * sets the side which this outline is applicable to (TOP, BOTTOM).
+ *
+ * @return bool: true if the side was set, false if the side is invalid
+ * or there is a violation of IDF ownership rules.
+ */
+ virtual bool SetSide( IDF3::IDF_LAYER aSide );
+
+ /**
+ * Function GetSide
+ * returns the side which this outline is applicable to
+ */
+ virtual IDF3::IDF_LAYER GetSide( void );
+
+ /**
+ * Function Clear
+ * deletes internal data except for the parent object
+ */
+ virtual bool Clear( void );
+};
+
+
+/**
+ * Class ROUTE_OUTLINE
+ * describes routing areas on the board
+ */
+class ROUTE_OUTLINE : public BOARD_OUTLINE
+{
+friend class IDF3_BOARD;
+private:
+ /**
+ * Function readData
+ * reads ROUTE_OUTLINE data from an IDFv3 file
+ * If an unrecoverable error occurs an exception is thrown.
+ *
+ * @param aBoardFile is an open IDFv3 board file
+ * @param aHeader is the .ROUTE_OUTLINE header as returned by FetchIDFLine
+ */
+ virtual void readData( std::ifstream& aBoardFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion );
+
+ /**
+ * Function writeData
+ * writes the ROUTE_OUTLINE data to an open IDFv3 file
+ */
+ virtual void writeData( std::ofstream& aBoardFile );
+
+protected:
+ IDF3::IDF_LAYER layers; // Routing layers (IDF spec)
+
+public:
+ ROUTE_OUTLINE( IDF3_BOARD* aParent );
+
+ /**
+ * Function SetLayers
+ * sets the layer or group of layers this outline is applicable to.
+ * This function is subject to IDF ownership rules; true is returned
+ * on success, otherwise false is returned and the error message is set.
+ */
+ virtual bool SetLayers( IDF3::IDF_LAYER aLayer );
+
+ /**
+ * Function GetLayers
+ * returns the layer or group of layers which this outline is applicable to
+ */
+ virtual IDF3::IDF_LAYER GetLayers( void );
+
+ /**
+ * Function Clear
+ * deletes internal data except for the parent object
+ */
+ virtual bool Clear( void );
+};
+
+/**
+ * Class PLACE_OUTLINE
+ * describes areas on the board for placing components
+ */
+class PLACE_OUTLINE : public BOARD_OUTLINE
+{
+friend class IDF3_BOARD;
+private:
+ /**
+ * Function readData
+ * reads PLACE_OUTLINE data from an open IDFv3 file.
+ * If an unrecoverable error occurs an exception is thrown.
+ *
+ * @param aBoardFile is an IDFv3 file opened for reading
+ * @param aHeader is the .PLACE_OUTLINE header as returned by FetchIDFLine
+ */
+ virtual void readData( std::ifstream& aBoardFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion );
+
+ /**
+ * Function writeData
+ * writes the PLACE_OUTLINE data to an open IDFv3 file
+ *
+ * @param aBoardFile is an IDFv3 file opened for writing
+ *
+ * @return bool: true if the data was successfully written, otherwise false
+ */
+ virtual void writeData( std::ofstream& aBoardFile );
+
+protected:
+ IDF3::IDF_LAYER side; // Board Side [TOP/BOTTOM/BOTH ONLY] (IDF spec)
+
+public:
+ PLACE_OUTLINE( IDF3_BOARD* aParent );
+
+ /**
+ * Function SetSide
+ * sets the side (TOP, BOTTOM, BOTH) which this outline applies to.
+ * This function is subject to IDF ownership rules; true is returned
+ * on success, otherwise false is returned and the error message is set.
+ */
+ virtual bool SetSide( IDF3::IDF_LAYER aSide );
+
+ /**
+ * Function GetSide
+ * returns the side which this outline is applicable to
+ */
+ virtual IDF3::IDF_LAYER GetSide( void );
+
+ /**
+ * Function SetMaxHeight
+ * sets the maximum height of a component within this outline.
+ * This function is subject to IDF ownership rules; true is returned
+ * on success, otherwise false is returned and the error message is set.
+ */
+ virtual bool SetMaxHeight( double aHeight );
+
+ /**
+ * Function GetMaxHeight
+ * returns the maximum allowable height for a component in this region
+ */
+ virtual double GetMaxHeight( void );
+
+ /**
+ * Function Clear
+ * deletes all internal data
+ */
+ virtual bool Clear( void );
+};
+
+
+/**
+ * Class ROUTE_KO_OUTLINE
+ * describes regions and layers where no electrical routing is permitted
+ */
+class ROUTE_KO_OUTLINE : public ROUTE_OUTLINE
+{
+public:
+ ROUTE_KO_OUTLINE( IDF3_BOARD* aParent );
+};
+
+/**
+ * Class VIA_KO_OUTLINE
+ * describes regions in which vias are prohibited. Note: IDFv3 only considers
+ * thru-hole vias and makes no statement regarding behavior with blind or buried
+ * vias.
+ */
+class VIA_KO_OUTLINE : public OTHER_OUTLINE
+{
+public:
+ VIA_KO_OUTLINE( IDF3_BOARD* aParent );
+};
+
+
+/**
+ * Class PLACE_KO_OUTLINE
+ * represents regions and layers in which no component may
+ * be placed or on which a maximum component height is in effect.
+ */
+class PLACE_KO_OUTLINE : public PLACE_OUTLINE
+{
+public:
+ PLACE_KO_OUTLINE( IDF3_BOARD* aParent );
+};
+
+/**
+ * Class GROUP_OUTLINE
+ * represents regions and layers in which user-specified features or components
+ * may be placed.
+ */
+class GROUP_OUTLINE : public BOARD_OUTLINE
+{
+friend class IDF3_BOARD;
+private:
+ IDF3::IDF_LAYER side; // Board Side [TOP/BOTTOM/BOTH ONLY] (IDF spec)
+ std::string groupName; // non-unique string
+
+ /**
+ * Function readData
+ * reads GROUP_OUTLINE data from an open IDFv3 file
+ * If an unrecoverable error occurs an exception is thrown.
+ *
+ * @param aBoardFile is an open IDFv3 file
+ * @param aHeader is the .PLACE_REGION header as returned by FetchIDFLine
+ */
+ virtual void readData( std::ifstream& aBoardFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion );
+
+ /**
+ * Function writeData
+ * writes the data to a .PLACE_REGION section of an IDFv3 file
+ *
+ * @param aBoardFile is an IDFv3 file open for writing
+ *
+ * @return bool: true if the data is successfully written, otherwise false
+ */
+ virtual void writeData( std::ofstream& aBoardFile );
+
+public:
+ GROUP_OUTLINE( IDF3_BOARD* aParent );
+
+ /**
+ * Function SetSide
+ * sets the side which this outline applies to (TOP, BOTTOM, BOTH).
+ * This function is subject to IDF ownership rules; true is returned
+ * on success, otherwise false is returned and the error message is set.
+ */
+ virtual bool SetSide( IDF3::IDF_LAYER aSide );
+
+ /**
+ * Function GetSide
+ * returns the side which this outline applies to
+ */
+ virtual IDF3::IDF_LAYER GetSide( void );
+
+ /**
+ * Function SetGroupName
+ * sets the name of the group, subject to IDF ownership rules.
+ * This function is subject to IDF ownership rules; true is returned
+ * on success, otherwise false is returned and the error message is set.
+ */
+ virtual bool SetGroupName( std::string aGroupName );
+
+ /**
+ * Function GetGroupName
+ * returns a reference to the (non-unique) group name
+ */
+ virtual const std::string& GetGroupName( void );
+
+ /**
+ * Function Clear
+ * deletes internal data, subject to IDF ownership rules
+ */
+ virtual bool Clear( void );
+};
+
+
+/**
+ * class IDF3_COMP_OUTLINE
+ * represents a component's outline as stored in an IDF library file
+ */
+class IDF3_COMP_OUTLINE : public BOARD_OUTLINE
+{
+friend class IDF3_BOARD;
+friend class IDF3_COMP_OUTLINE_DATA;
+private:
+ std::string uid; // unique ID
+ std::string geometry; // geometry name (IDF)
+ std::string part; // part name (IDF)
+ IDF3::COMP_TYPE compType; // component type
+ int refNum; // number of components referring to this outline
+
+ std::map< std::string, std::string > props; // properties list
+
+ void readProperties( std::ifstream& aLibFile );
+ bool writeProperties( std::ofstream& aLibFile );
+
+ /**
+ * Function readData
+ * reads a component outline from an open IDFv3 file
+ * If an unrecoverable error occurs, an exception is thrown.
+ *
+ * @param aLibFile is an open IDFv3 Library file
+ * @param aHeader is the .ELECTRICAL or .MECHANICAL header as returned by FetchIDFLine
+ */
+ virtual void readData( std::ifstream& aLibFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion );
+
+ /**
+ * Function writeData
+ * writes comments and component outline data to an IDFv3 Library file
+ *
+ * @param aLibFile is an IDFv3 library file open for writing
+ *
+ * @return bool: true if the data was successfully written, otherwise false
+ */
+ virtual void writeData( std::ofstream& aLibFile );
+
+ /**
+ * Function incrementRef
+ * increments the internal reference counter to keep track of the number of
+ * components referring to this outline.
+ *
+ * @return int: the number of current references to this component outline
+ */
+ int incrementRef( void );
+
+ /**
+ * Function decrementRef
+ * decrements the internal reference counter to keep track of the number of
+ * components referring to this outline.
+ *
+ * @return int: the number of remaining references or -1 if there were no
+ * references when the function was invoked, in which case the error message
+ * is also set.
+ */
+ int decrementRef( void );
+
+public:
+ IDF3_COMP_OUTLINE( IDF3_BOARD* aParent );
+
+ /**
+ * Function Clear
+ * deletes internal outline data
+ */
+ virtual bool Clear( void );
+
+ /**
+ * Function SetComponentClass
+ * sets the type of component outline (.ELECTRICAL or .MECHANICAL).
+ * Returns true on success, otherwise false and the error message is set
+ */
+ bool SetComponentClass( IDF3::COMP_TYPE aCompClass );
+
+ /**
+ * Function GetComponentClass
+ * returns the class of component represented by this outline
+ */
+ IDF3::COMP_TYPE GetComponentClass( void );
+
+ /**
+ * Function SetGeomName
+ * sets the Geometry Name (Package Name, IDFv3 spec) of the component outline
+ */
+ void SetGeomName( const std::string& aGeomName );
+
+ /**
+ * Function GetGeomName
+ * returns the Geometry Name (Package Name) of the component outline
+ */
+ const std::string& GetGeomName( void );
+
+ /**
+ * Function SetPartName
+ * sets the Part Name (Part Number, IDFv3 spec) of the component outline
+ */
+ void SetPartName( const std::string& aPartName );
+
+ /**
+ * Function GetPartName
+ * returns the Part Name (Part Number) of the component outline
+ */
+ const std::string& GetPartName( void );
+
+ /**
+ * Function GetUID
+ * returns the unique identifier for this component outline;
+ * this is equal to GEOM_NAME + "_" + PART_NAME
+ */
+ const std::string& GetUID( void );
+
+ /**
+ * Function CreateDefaultOutline
+ * creates a default outline with the given Geometry and Part names.
+ * This outline is a star with outer radius 5mm and inner radius 2.5mm.
+ */
+ bool CreateDefaultOutline( const std::string &aGeom, const std::string &aPart );
+
+ // XXX: property manipulators
+};
+
+#endif // IDF_OUTLINES_H
diff --git a/utils/idftools/idf_parser.cpp b/utils/idftools/idf_parser.cpp
new file mode 100644
index 0000000..c4d3e4c
--- /dev/null
+++ b/utils/idftools/idf_parser.cpp
@@ -0,0 +1,4288 @@
+/*
+ * 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
+ */
+
+
+#include <iostream>
+#include <iomanip>
+#include <fstream>
+#include <sstream>
+#include <cmath>
+#include <cerrno>
+#include <algorithm>
+
+#include <idf_parser.h>
+#include <idf_helpers.h>
+
+using namespace std;
+using namespace IDF3;
+
+
+static bool MatchCompOutline( IDF3_COMP_OUTLINE* aOutlineA, IDF3_COMP_OUTLINE* aOutlineB )
+{
+ if( aOutlineA->GetComponentClass() != aOutlineB->GetComponentClass() )
+ return false;
+
+ if( aOutlineA->OutlinesSize() != aOutlineB->OutlinesSize() )
+ return false;
+
+ // are both outlines empty?
+ if( aOutlineA->OutlinesSize() == 0 )
+ return true;
+
+ IDF_OUTLINE* opA = aOutlineA->GetOutline( 0 );
+ IDF_OUTLINE* opB = aOutlineB->GetOutline( 0 );
+
+ if( opA->size() != opB->size() )
+ return false;
+
+ if( opA->size() == 0 )
+ return true;
+
+ std::list<IDF_SEGMENT*>::iterator olAs = opA->begin();
+ std::list<IDF_SEGMENT*>::iterator olAe = opA->end();
+ std::list<IDF_SEGMENT*>::iterator olBs = opB->begin();
+
+ while( olAs != olAe )
+ {
+ if( !(*olAs)->MatchesStart( (*olBs)->startPoint ) )
+ return false;
+
+ if( !(*olAs)->MatchesEnd( (*olBs)->endPoint ) )
+ return false;
+
+ ++olAs;
+ ++olBs;
+ }
+
+ return true;
+}
+
+
+/*
+ * CLASS: IDF3_COMP_OUTLINE_DATA
+ * This represents the outline placement
+ * information and other data specific to
+ * each component instance.
+ */
+IDF3_COMP_OUTLINE_DATA::IDF3_COMP_OUTLINE_DATA()
+{
+ parent = NULL;
+ outline = NULL;
+ xoff = 0.0;
+ yoff = 0.0;
+ zoff = 0.0;
+ aoff = 0.0;
+
+ return;
+}
+
+IDF3_COMP_OUTLINE_DATA::IDF3_COMP_OUTLINE_DATA( IDF3_COMPONENT* aParent,
+ IDF3_COMP_OUTLINE* aOutline )
+{
+ parent = aParent;
+ outline = aOutline;
+ xoff = 0.0;
+ yoff = 0.0;
+ zoff = 0.0;
+ aoff = 0.0;
+
+ if( aOutline )
+ aOutline->incrementRef();
+
+ return;
+}
+
+IDF3_COMP_OUTLINE_DATA::IDF3_COMP_OUTLINE_DATA( IDF3_COMPONENT* aParent,
+ IDF3_COMP_OUTLINE* aOutline,
+ double aXoff, double aYoff,
+ double aZoff, double aAngleOff )
+{
+ parent = aParent;
+ outline = aOutline;
+ xoff = aXoff;
+ yoff = aYoff;
+ zoff = aZoff;
+ aoff = aAngleOff;
+ return;
+}
+
+IDF3_COMP_OUTLINE_DATA::~IDF3_COMP_OUTLINE_DATA()
+{
+ if( outline )
+ outline->decrementRef();
+
+ return;
+}
+
+#ifndef DISABLE_IDF_OWNERSHIP
+bool IDF3_COMP_OUTLINE_DATA::checkOwnership( int aSourceLine, const char* aSourceFunc )
+{
+ if( !parent )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
+ ostr << "* BUG: IDF3_COMP_OUTLINE_DATA::parent not set; cannot enforce ownership rules\n";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ IDF3::IDF_PLACEMENT placement = parent->GetPlacement();
+ IDF3::CAD_TYPE parentCAD = parent->GetCadType();
+
+ if( placement == PS_PLACED || placement == PS_UNPLACED )
+ return true;
+
+ if( placement == PS_MCAD && parentCAD == CAD_MECH )
+ return true;
+
+ if( placement == PS_ECAD && parentCAD == CAD_ELEC )
+ return true;
+
+ do
+ {
+ ostringstream ostr;
+ ostr << "* " << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
+ ostr << "* ownership violation; CAD type is ";
+
+ if( parentCAD == CAD_MECH )
+ ostr << "MCAD ";
+ else
+ ostr << "ECAD ";
+
+ ostr << "while outline owner is " << GetPlacementString( placement ) << "\n";
+ errormsg = ostr.str();
+
+ } while( 0 );
+
+ return false;
+}
+#endif
+
+bool IDF3_COMP_OUTLINE_DATA::SetOffsets( double aXoff, double aYoff,
+ double aZoff, double aAngleOff )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ xoff = aXoff;
+ yoff = aYoff;
+ zoff = aZoff;
+ aoff = aAngleOff;
+ return true;
+}
+
+void IDF3_COMP_OUTLINE_DATA::GetOffsets( double& aXoff, double& aYoff,
+ double& aZoff, double& aAngleOff )
+{
+ aXoff = xoff;
+ aYoff = yoff;
+ aZoff = zoff;
+ aAngleOff = aoff;
+ return;
+}
+
+
+void IDF3_COMP_OUTLINE_DATA::SetParent( IDF3_COMPONENT* aParent )
+{
+ parent = aParent;
+}
+
+bool IDF3_COMP_OUTLINE_DATA::SetOutline( IDF3_COMP_OUTLINE* aOutline )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ if( outline )
+ outline->decrementRef();
+
+ outline = aOutline;
+
+ if( outline )
+ outline->incrementRef();
+
+ return true;
+}
+
+
+bool IDF3_COMP_OUTLINE_DATA::readPlaceData( std::ifstream &aBoardFile,
+ IDF3::FILE_STATE& aBoardState,
+ IDF3_BOARD *aBoard,
+ IDF3::IDF_VERSION aIdfVersion,
+ bool aNoSubstituteOutlines )
+{
+ if( !aBoard )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: invoked with no reference to the parent IDF_BOARD" ) );
+
+ // clear out data possibly left over from previous use of the object
+ outline = NULL;
+ parent = NULL;
+
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ int idx = 0;
+ bool quoted = false;
+ std::string token;
+ std::string uid;
+ std::string refdes;
+ IDF3::IDF_PLACEMENT placement = IDF3::PS_UNPLACED;
+ IDF3::IDF_LAYER side = IDF3::LYR_TOP;
+
+ // RECORD 2: 'package name', 'part number', 'Refdes' (any, NOREFDES, BOARD)
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( ( !aBoardFile.good() && !aBoardFile.eof() ) || iline.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: could not read PLACEMENT section\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( isComment )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: comment within PLACEMENT section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ GetIDFString( iline, token, quoted, idx );
+
+ if( !quoted && CompareToken( ".END_PLACEMENT", token ) )
+ {
+ aBoardState = IDF3::FILE_PLACEMENT;
+ return false;
+ }
+
+ std::string ngeom = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: no PART NAME in PLACEMENT RECORD2\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ std::string npart = token;
+ uid = ngeom + "_" + npart;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: no REFDES in PLACEMENT RECORD2\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( CompareToken( "NOREFDES", token ) )
+ {
+ // according to the IDF3.0 specification, this is a
+ // mechanical component. The specification is defective
+ // since it is impossible to associate mechanical
+ // components with their holes unless the mechanical
+ // component is given a unique RefDes. This class of defect
+ // is one reason IDF does not work well in faithfully
+ // conveying information between ECAD and MCAD.
+ refdes = aBoard->GetNewRefDes();
+ }
+ else if( CompareToken( "BOARD", token ) )
+ {
+ ostringstream ostr;
+
+ ostr << "UNSUPPORTED FEATURE\n";
+ ostr << "* RefDes is 'BOARD', indicating this is a PANEL FILE (not supported)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ else if( CompareToken( "PANEL", token ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: RefDes in PLACEMENT RECORD2 is 'PANEL'\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ else if( token.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: empty RefDes string in PLACEMENT RECORD2\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ else
+ {
+ // note: perversely, spaces can be a valid RefDes
+ refdes = token;
+ }
+
+ // V2: RECORD 3: X, Y, ROT, SIDE (top/bot), PLACEMENT (fixed, placed, unplaced)
+ // V3: RECORD 3: X, Y, Z, ROT, SIDE (top/bot), PLACEMENT (placed, unplaced, mcad, ecad)
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( !aBoardFile.good() )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* problems reading PLACEMENT SECTION, RECORD 3\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( isComment )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: comment within PLACEMENT section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ GetIDFString( iline, token, quoted, idx );
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: X value must not be in quotes (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ istringstream istr;
+ istr.str( token );
+
+ istr >> xoff;
+ if( istr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: X value is not numeric (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: no Y value (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ istr.clear();
+ istr.str( token );
+
+ istr >> yoff;
+ if( istr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: Y value is not numeric (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( aIdfVersion > IDF_V2 )
+ {
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDFv3 file\n";
+ ostr << "* violation: no Z value (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ istr.clear();
+ istr.str( token );
+
+ istr >> zoff;
+ if( istr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDFv3 file\n";
+ ostr << "* violation: Z value is not numeric (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: no rotation value (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ istr.clear();
+ istr.str( token );
+
+ istr >> aoff;
+ if( istr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: rotation value is not numeric (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: no SIDE value (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( CompareToken( "TOP", token ) )
+ {
+ side = IDF3::LYR_TOP;
+ }
+ else if( CompareToken( "BOTTOM", token ) )
+ {
+ side = IDF3::LYR_BOTTOM;
+ }
+ else
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: invalid SIDE value in PLACEMENT RECORD 3 ('";
+ ostr << token << "'); must be one of TOP/BOTTOM\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: no PLACEMENT value in PLACEMENT RECORD 3\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( CompareToken( "PLACED", token ) )
+ {
+ placement = IDF3::PS_PLACED;
+ }
+ else if( CompareToken( "UNPLACED", token ) )
+ {
+ placement = IDF3::PS_UNPLACED;
+ }
+ else if( aIdfVersion > IDF_V2 && CompareToken( "MCAD", token ) )
+ {
+ placement = IDF3::PS_MCAD;
+ }
+ else if( aIdfVersion > IDF_V2 && CompareToken( "ECAD", token ) )
+ {
+ placement = IDF3::PS_ECAD;
+ }
+ else if( aIdfVersion < IDF_V3 && CompareToken( "FIXED", token ) )
+ {
+ if( aBoard->GetCadType() == CAD_ELEC )
+ placement = IDF3::PS_MCAD;
+ else
+ placement = IDF3::PS_ECAD;
+ }
+ else
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: invalid PLACEMENT value ('";
+ ostr << token << "') in PLACEMENT RECORD 3\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ outline = aBoard->GetComponentOutline( uid );
+
+ if( outline == NULL && !aNoSubstituteOutlines )
+ {
+ ERROR_IDF << "MISSING OUTLINE\n";
+ cerr << "* GeomName( " << ngeom << " ), PartName( " << npart << " )\n";
+ cerr << "* Substituting default outline.\n";
+ outline = aBoard->GetInvalidOutline( ngeom, npart );
+
+ if( outline == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* missing outline: cannot create default" ) );
+ }
+
+ if( aBoard->GetUnit() == IDF3::UNIT_THOU )
+ {
+ xoff *= IDF_THOU_TO_MM;
+ yoff *= IDF_THOU_TO_MM;
+ zoff *= IDF_THOU_TO_MM;
+ }
+
+ parent = aBoard->FindComponent( refdes );
+
+ if( parent == NULL )
+ {
+ IDF3_COMPONENT* cp = new IDF3_COMPONENT( aBoard );
+
+ if( cp == NULL )
+ {
+ outline = NULL;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "cannot create component object" ) );
+ }
+
+ cp->SetRefDes( refdes );
+ cp->SetPosition( xoff, yoff, aoff, side );
+ cp->SetPlacement( placement );
+
+ xoff = 0;
+ yoff = 0;
+ aoff = 0;
+
+ aBoard->AddComponent( cp );
+
+ parent = cp;
+ }
+ else
+ {
+ double tX, tY, tA;
+ IDF3::IDF_LAYER tL;
+
+ if( parent->GetPosition( tX, tY, tA, tL ) )
+ {
+ if( side != tL )
+ {
+ outline = NULL;
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: inconsistent PLACEMENT data; ";
+ ostr << "* SIDE value has changed from " << GetLayerString( tL );
+ ostr << " to " << GetLayerString( side ) << "\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ xoff -= tX;
+ yoff -= tY;
+ aoff -= tA;
+ }
+ else
+ {
+ parent->SetPosition( xoff, yoff, aoff, side );
+ parent->SetPlacement( placement );
+
+ xoff = 0;
+ yoff = 0;
+ aoff = 0;
+ }
+
+ if( placement != parent->GetPlacement() )
+ {
+ outline = NULL;
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: inconsistent PLACEMENT data; ";
+ ostr << "* PLACEMENT value has changed from ";
+ ostr << GetPlacementString( parent->GetPlacement() );
+ ostr << " to " << GetPlacementString( placement ) << "\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ }
+
+ // copy internal data to a new object and push it into the component's outline list
+ IDF3_COMP_OUTLINE_DATA* cdp = new IDF3_COMP_OUTLINE_DATA;
+ *cdp = *this;
+ if( outline ) outline->incrementRef();
+ outline = NULL;
+
+ if( !parent->AddOutlineData( cdp ) )
+ {
+ delete cdp;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not add outline data object" ) );
+ }
+
+ return true;
+} // IDF3_COMP_OUTLINE_DATA::readPlaceData
+
+
+void IDF3_COMP_OUTLINE_DATA::writePlaceData( std::ofstream& aBoardFile,
+ double aXpos, double aYpos, double aAngle,
+ const std::string aRefDes,
+ IDF3::IDF_PLACEMENT aPlacement,
+ IDF3::IDF_LAYER aSide )
+{
+ if( outline == NULL )
+ return;
+
+ if( outline->GetUID().empty() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "empty GEOM and PART names" ) );
+
+ if( aPlacement == PS_INVALID )
+ {
+ ERROR_IDF << "placement invalid (" << aRefDes << ":";
+ std::cerr << aPlacement << "); defaulting to PLACED\n";
+ aPlacement = PS_PLACED;
+ }
+
+ if( aSide != LYR_TOP && aSide != LYR_BOTTOM )
+ {
+ ostringstream ostr;
+ ostr << "\n* invalid side (" << GetLayerString( aSide ) << "); ";
+ ostr << "must be TOP or BOTTOM\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ // calculate the final position based on layer
+ double xpos, ypos, ang;
+
+ switch( aSide )
+ {
+ case LYR_TOP:
+ xpos = aXpos + xoff;
+ ypos = aYpos + yoff;
+ ang = aAngle + aoff;
+ break;
+
+ default:
+ xpos = aXpos - xoff;
+ ypos = aYpos + yoff;
+ ang = aAngle - aoff;
+ break;
+ }
+
+ std::string arefdes = aRefDes;
+
+ if( arefdes.empty() || !arefdes.compare( "~" )
+ || ( arefdes.size() >= 8 && CompareToken( "NOREFDES", arefdes.substr(0, 8) ) ) )
+ arefdes = "NOREFDES";
+
+ aBoardFile << "\"" << outline->GetGeomName() << "\" \"" << outline->GetPartName() << "\" "
+ << arefdes << "\n";
+
+ IDF3::IDF_UNIT unit = UNIT_MM;
+
+ if( parent )
+ unit = parent->GetUnit();
+
+ if( unit == UNIT_MM )
+ {
+ aBoardFile << setiosflags(ios::fixed) << setprecision(5) << xpos << " "
+ << ypos << " " << setprecision(3) << zoff << " "
+ << ang << " ";
+ }
+ else
+ {
+ aBoardFile << setiosflags(ios::fixed) << setprecision(1) << (xpos / IDF_THOU_TO_MM) << " "
+ << (ypos / IDF_THOU_TO_MM) << " " << (zoff / IDF_THOU_TO_MM) << " "
+ << setprecision(3) << ang << " ";
+ }
+
+ WriteLayersText( aBoardFile, aSide );
+
+ switch( aPlacement )
+ {
+ case PS_PLACED:
+ aBoardFile << " PLACED\n";
+ break;
+
+ case PS_UNPLACED:
+ aBoardFile << " UNPLACED\n";
+ break;
+
+ case PS_MCAD:
+ aBoardFile << " MCAD\n";
+ break;
+
+ default:
+ aBoardFile << " ECAD\n";
+ break;
+ }
+
+ return;
+}
+
+
+/*
+ * CLASS: IDF3_COMPONENT
+ *
+ * This represents a component and its associated
+ * IDF outlines and ancillary data (position, etc)
+ */
+IDF3_COMPONENT::IDF3_COMPONENT( IDF3_BOARD* aParent )
+{
+ xpos = 0.0;
+ ypos = 0.0;
+ angle = 0.0;
+
+ hasPosition = false;
+ placement = PS_INVALID;
+ layer = LYR_INVALID;
+
+ parent = aParent;
+ return;
+}
+
+IDF3_COMPONENT::~IDF3_COMPONENT()
+{
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itcS = components.begin();
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itcE = components.end();
+
+ while( itcS != itcE )
+ {
+ delete *itcS;
+ ++itcS;
+ }
+
+ components.clear();
+
+ std::list< IDF_DRILL_DATA* >::iterator itdS = drills.begin();
+ std::list< IDF_DRILL_DATA* >::iterator itdE = drills.end();
+
+ while( itdS != itdE )
+ {
+ delete *itdS;
+ ++itdS;
+ }
+
+ drills.clear();
+
+ return;
+}
+
+#ifndef DISABLE_IDF_OWNERSHIP
+bool IDF3_COMPONENT::checkOwnership( int aSourceLine, const char* aSourceFunc )
+{
+ if( !parent )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
+ ostr << "\n* BUG: parent not set";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ IDF3::CAD_TYPE pcad = parent->GetCadType();
+
+ switch( placement )
+ {
+ case PS_UNPLACED:
+ case PS_PLACED:
+ case PS_INVALID:
+ break;
+
+ case PS_MCAD:
+
+ if( pcad != CAD_MECH )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "\n* ownership violation; internal CAD type (MCAD) conflicts with PLACEMENT (";
+ ostr << GetPlacementString( placement ) << ")";
+ errormsg = ostr.str();
+
+ return false;
+ }
+ break;
+
+ case PS_ECAD:
+
+ if( pcad != CAD_ELEC )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "\n* ownership violation; internal CAD type (MCAD) conflicts with PLACEMENT (";
+ ostr << GetPlacementString( placement ) << ")";
+ errormsg = ostr.str();
+
+ return false;
+ }
+ break;
+
+ default:
+ do{
+ ostringstream ostr;
+ ostr << "\n* BUG: unhandled internal placement value (" << placement << ")";
+ errormsg = ostr.str();
+
+ return false;
+ } while( 0 );
+
+ break;
+ }
+
+ return true;
+}
+#endif
+
+
+void IDF3_COMPONENT::SetParent( IDF3_BOARD* aParent )
+{
+ parent = aParent;
+ return;
+}
+
+IDF3::CAD_TYPE IDF3_COMPONENT::GetCadType( void )
+{
+ if( parent )
+ return parent->GetCadType();
+
+ return CAD_INVALID;
+}
+
+IDF3::IDF_UNIT IDF3_COMPONENT::GetUnit( void )
+{
+ if( parent )
+ return parent->GetUnit();
+
+ return UNIT_INVALID;
+}
+
+bool IDF3_COMPONENT::SetRefDes( const std::string& aRefDes )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ if( aRefDes.empty() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): invalid RefDes (empty)";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ if( CompareToken( "PANEL", aRefDes ) )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: PANEL is a reserved designator and may not be used by components";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ refdes = aRefDes;
+ return true;
+}
+
+
+const std::string& IDF3_COMPONENT::GetRefDes( void )
+{
+ return refdes;
+}
+
+IDF_DRILL_DATA* IDF3_COMPONENT::AddDrill( double aDia, double aXpos, double aYpos,
+ IDF3::KEY_PLATING aPlating,
+ const std::string aHoleType,
+ IDF3::KEY_OWNER aOwner )
+{
+ IDF_DRILL_DATA* dp = new IDF_DRILL_DATA( aDia, aXpos, aYpos, aPlating,
+ refdes, aHoleType, aOwner );
+
+ if( dp == NULL )
+ return NULL;
+
+ drills.push_back( dp );
+
+ return dp;
+}
+
+
+IDF_DRILL_DATA* IDF3_COMPONENT::AddDrill( IDF_DRILL_DATA* aDrilledHole )
+{
+ if( !aDrilledHole )
+ return NULL;
+
+ if( CompareToken( "PANEL", refdes ) )
+ {
+ ERROR_IDF;
+ cerr << "\n* BUG: PANEL drills not supported at component level\n";
+ return NULL;
+ }
+
+ if( refdes.compare( aDrilledHole->GetDrillRefDes() ) )
+ {
+ ERROR_IDF;
+ cerr << "\n* BUG: pushing an incorrect REFDES ('" << aDrilledHole->GetDrillRefDes();
+ cerr << "') to component ('" << refdes << "')\n";
+ return NULL;
+ }
+
+ drills.push_back( aDrilledHole );
+
+ return aDrilledHole;
+}
+
+
+bool IDF3_COMPONENT::DelDrill( double aDia, double aXpos, double aYpos )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ errormsg.clear();
+
+ if( drills.empty() )
+ return false;
+
+ bool val = false;
+
+ list< IDF_DRILL_DATA* >::iterator itS = drills.begin();
+ list< IDF_DRILL_DATA* >::iterator itE = drills.end();
+
+ while( !drills.empty() && itS != itE )
+ {
+ if( (*itS)->Matches( aDia, aXpos, aYpos ) )
+ {
+ val = true;
+ delete *itS;
+ itS = drills.erase( itS );
+ continue;
+ }
+ ++itS;
+ }
+
+ return val;
+}
+
+
+bool IDF3_COMPONENT::DelDrill( IDF_DRILL_DATA* aDrill )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ errormsg.clear();
+
+ if( drills.empty() )
+ return false;
+
+ list< IDF_DRILL_DATA* >::iterator itS = drills.begin();
+ list< IDF_DRILL_DATA* >::iterator itE = drills.end();
+
+ while( !drills.empty() && itS != itE )
+ {
+ if( *itS == aDrill )
+ {
+ delete *itS;
+ drills.erase( itS );
+ return true;
+ }
+ ++itS;
+ }
+
+ return false;
+}
+
+const std::list< IDF_DRILL_DATA* >*const IDF3_COMPONENT::GetDrills( void )
+{
+ return &drills;
+}
+
+bool IDF3_COMPONENT::AddOutlineData( IDF3_COMP_OUTLINE_DATA* aComponentOutline )
+{
+ if( aComponentOutline == NULL )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): invalid aComponentOutline (NULL)";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+
+ components.push_back( aComponentOutline );
+
+ return true;
+}
+
+bool IDF3_COMPONENT::DeleteOutlineData( IDF3_COMP_OUTLINE_DATA* aComponentOutline )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ if( components.empty() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): component list is empty";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ if( aComponentOutline == NULL )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): invalid aComponentOutline (NULL)";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ errormsg.clear();
+
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itS = components.begin();
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itE = components.end();
+
+ while( itS != itE )
+ {
+ if( *itS == aComponentOutline )
+ {
+ delete *itS;
+ components.erase( itS );
+ return true;
+ }
+
+ ++itS;
+ }
+
+ return false;
+}
+
+bool IDF3_COMPONENT::DeleteOutlineData( size_t aIndex )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ if( aIndex >= components.size() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* aIndex (" << aIndex << ") out of range; list size is " << components.size();
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itS = components.begin();
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itE = components.end();
+ size_t idx = 0;
+
+ while( itS != itE )
+ {
+ if( idx == aIndex )
+ {
+ delete *itS;
+ components.erase( itS );
+ return true;
+ }
+
+ ++idx;
+ ++itS;
+ }
+
+ return false;
+}
+
+size_t IDF3_COMPONENT::GetOutlinesSize( void )
+{
+ return components.size();
+}
+
+const std::list< IDF3_COMP_OUTLINE_DATA* >*const IDF3_COMPONENT::GetOutlinesData( void )
+{
+ return &components;
+}
+
+bool IDF3_COMPONENT::GetPosition( double& aXpos, double& aYpos, double& aAngle,
+ IDF3::IDF_LAYER& aLayer )
+{
+ errormsg.clear();
+
+ if( !hasPosition )
+ {
+ aXpos = 0.0;
+ aYpos = 0.0;
+ aAngle = 0.0;
+ aLayer = IDF3::LYR_INVALID;
+ return false;
+ }
+
+ aXpos = xpos;
+ aYpos = ypos;
+ aAngle = angle;
+ aLayer = layer;
+ return true;
+}
+
+bool IDF3_COMPONENT::SetPosition( double aXpos, double aYpos, double aAngle, IDF3::IDF_LAYER aLayer )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ errormsg.clear();
+
+ switch( aLayer )
+ {
+ case LYR_TOP:
+ case LYR_BOTTOM:
+ break;
+
+ default:
+ do{
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "\n* invalid side (must be TOP or BOTTOM only): " << GetLayerString( aLayer );
+ errormsg = ostr.str();
+
+ return false;
+ } while( 0 );
+ break;
+ }
+
+ if( hasPosition )
+ return false;
+
+ hasPosition = true;
+ xpos = aXpos;
+ ypos = aYpos;
+ angle = aAngle;
+ layer = aLayer;
+ return true;
+}
+
+
+IDF3::IDF_PLACEMENT IDF3_COMPONENT::GetPlacement( void )
+{
+ return placement;
+}
+
+
+bool IDF3_COMPONENT::SetPlacement( IDF3::IDF_PLACEMENT aPlacementValue )
+{
+ if( aPlacementValue < PS_UNPLACED || aPlacementValue >= PS_INVALID )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "\n* invalid PLACEMENT value (" << aPlacementValue << ")";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ placement = aPlacementValue;
+
+ return true;
+}
+
+bool IDF3_COMPONENT::writeDrillData( std::ofstream& aBoardFile )
+{
+ if( drills.empty() )
+ return true;
+
+ std::list< IDF_DRILL_DATA* >::iterator itS = drills.begin();
+ std::list< IDF_DRILL_DATA* >::iterator itE = drills.end();
+
+ while( itS != itE )
+ {
+ (*itS)->write( aBoardFile, GetUnit() );
+ ++itS;
+ }
+
+ return true;
+}
+
+
+bool IDF3_COMPONENT::writePlaceData( std::ofstream& aBoardFile )
+{
+ if( components.empty() )
+ return true;
+
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itS = components.begin();
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itE = components.end();
+
+ while( itS != itE )
+ {
+ (*itS)->writePlaceData( aBoardFile, xpos, ypos, angle, refdes, placement, layer );
+ ++itS;
+ }
+
+ return true;
+}
+
+
+IDF3_BOARD::IDF3_BOARD( IDF3::CAD_TYPE aCadType )
+{
+ idfVer = IDF_V3;
+ state = FILE_START;
+ cadType = aCadType;
+ userPrec = 5;
+ userScale = 1.0;
+ userXoff = 0.0;
+ userYoff = 0.0;
+ brdFileVersion = 0;
+ libFileVersion = 0;
+ iRefDes = 0;
+ unit = UNIT_MM;
+
+ // unlike other outlines which are created as necessary,
+ // the board outline always exists and its parent must
+ // be set here
+ olnBoard.setParent( this );
+ olnBoard.setThickness( 1.6 );
+
+ return;
+}
+
+IDF3_BOARD::~IDF3_BOARD()
+{
+ Clear();
+
+ return;
+}
+
+
+const std::string& IDF3_BOARD::GetNewRefDes( void )
+{
+ ostringstream ostr;
+ ostr << "NOREFDESn" << iRefDes++;
+
+ sRefDes = ostr.str();
+
+ return sRefDes;
+}
+
+
+#ifndef DISABLE_IDF_OWNERSHIP
+bool IDF3_BOARD::checkComponentOwnership( int aSourceLine, const char* aSourceFunc,
+ IDF3_COMPONENT* aComponent )
+{
+ if( !aComponent )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc;
+ ostr << "(): Invalid component pointer (NULL)";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ IDF3::IDF_PLACEMENT place = aComponent->GetPlacement();
+
+ if( place == PS_PLACED || place == PS_UNPLACED )
+ return true;
+
+ if( place == PS_MCAD && cadType == CAD_MECH )
+ return true;
+
+ if( place == PS_ECAD && cadType == CAD_ELEC )
+ return true;
+
+ do
+ {
+ ostringstream ostr;
+ ostr << "* " << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
+ ostr << "* ownership violation; CAD type is ";
+
+ if( cadType == CAD_MECH )
+ ostr << "MCAD ";
+ else
+ ostr << "ECAD ";
+
+ ostr << "while outline owner is " << GetPlacementString( place ) << "\n";
+ errormsg = ostr.str();
+
+ } while( 0 );
+
+ return false;
+}
+#endif
+
+IDF3::CAD_TYPE IDF3_BOARD::GetCadType( void )
+{
+ return cadType;
+}
+
+void IDF3_BOARD::SetBoardName( std::string aBoardName )
+{
+ boardName = aBoardName;
+ return;
+}
+
+const std::string& IDF3_BOARD::GetBoardName( void )
+{
+ return boardName;
+}
+
+bool IDF3_BOARD::setUnit( IDF3::IDF_UNIT aUnit, bool convert )
+{
+ switch( aUnit )
+ {
+ case UNIT_MM:
+ case UNIT_THOU:
+ unit = aUnit;
+ break;
+
+ case UNIT_TNM:
+ ERROR_IDF << "\n* TNM unit is not supported; defaulting to mm\n";
+ unit = UNIT_MM;
+ break;
+
+ default:
+ do
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* invalid board unit (" << aUnit << ")";
+ errormsg = ostr.str();
+
+ return false;
+ } while( 0 );
+
+ break;
+ }
+
+ // iterate through all owned OUTLINE objects (except IDF3_COMP_OUTLINE)
+ // and set to the same unit
+
+ olnBoard.SetUnit( aUnit );
+
+ do
+ {
+ std::map< std::string, OTHER_OUTLINE*>::iterator its = olnOther.begin();
+ std::map< std::string, OTHER_OUTLINE*>::iterator ite = olnOther.end();
+
+ while( its != ite )
+ {
+ its->second->SetUnit( aUnit );
+ ++its;
+ }
+
+ } while( 0 );
+
+ do
+ {
+ std::list<ROUTE_OUTLINE*>::iterator its = olnRoute.begin();
+ std::list<ROUTE_OUTLINE*>::iterator ite = olnRoute.end();
+
+ while( its != ite )
+ {
+ (*its)->SetUnit( aUnit );
+ ++its;
+ }
+
+ } while( 0 );
+
+ do
+ {
+ std::list<PLACE_OUTLINE*>::iterator its = olnPlace.begin();
+ std::list<PLACE_OUTLINE*>::iterator ite = olnPlace.end();
+
+ while( its != ite )
+ {
+ (*its)->SetUnit( aUnit );
+ ++its;
+ }
+
+ } while( 0 );
+
+ do
+ {
+ std::list<ROUTE_KO_OUTLINE*>::iterator its = olnRouteKeepout.begin();
+ std::list<ROUTE_KO_OUTLINE*>::iterator ite = olnRouteKeepout.end();
+
+ while( its != ite )
+ {
+ (*its)->SetUnit( aUnit );
+ ++its;
+ }
+
+ } while( 0 );
+
+ do
+ {
+ std::list<VIA_KO_OUTLINE*>::iterator its = olnViaKeepout.begin();
+ std::list<VIA_KO_OUTLINE*>::iterator ite = olnViaKeepout.end();
+
+ while( its != ite )
+ {
+ (*its)->SetUnit( aUnit );
+ ++its;
+ }
+
+ } while( 0 );
+
+ do
+ {
+ std::list<PLACE_KO_OUTLINE*>::iterator its = olnPlaceKeepout.begin();
+ std::list<PLACE_KO_OUTLINE*>::iterator ite = olnPlaceKeepout.end();
+
+ while( its != ite )
+ {
+ (*its)->SetUnit( aUnit );
+ ++its;
+ }
+
+ } while( 0 );
+
+ do
+ {
+ std::multimap<std::string, GROUP_OUTLINE*>::iterator its = olnGroup.begin();
+ std::multimap<std::string, GROUP_OUTLINE*>::iterator ite = olnGroup.end();
+
+ while( its != ite )
+ {
+ its->second->SetUnit( aUnit );
+ ++its;
+ }
+
+ } while( 0 );
+
+ //iterate through all owned IDF3_COMP_OUTLINE objects and
+ // set to the same unit IF convert = true
+ if( convert )
+ {
+ std::map<std::string, IDF3_COMP_OUTLINE*>::iterator its = compOutlines.begin();
+ std::map<std::string, IDF3_COMP_OUTLINE*>::iterator ite = compOutlines.end();
+
+ while( its != ite )
+ {
+ its->second->SetUnit( aUnit );
+ ++its;
+ }
+
+ }
+
+ return true;
+}
+
+
+IDF3::IDF_UNIT IDF3_BOARD::GetUnit( void )
+{
+ return unit;
+}
+
+
+bool IDF3_BOARD::SetBoardThickness( double aBoardThickness )
+{
+ if( aBoardThickness <= 0.0 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): ";
+ ostr << "board thickness (" << aBoardThickness << ") must be > 0";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ if(! olnBoard.SetThickness( aBoardThickness ) )
+ {
+ errormsg = olnBoard.GetError();
+ return false;
+ }
+
+ return true;
+}
+
+
+double IDF3_BOARD::GetBoardThickness( void )
+{
+ return olnBoard.GetThickness();
+}
+
+
+// read the DRILLED HOLES section
+void IDF3_BOARD::readBrdDrills( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState )
+{
+ IDF_DRILL_DATA drill;
+
+ while( drill.read( aBoardFile, unit, aBoardState, idfVer ) )
+ {
+ IDF_DRILL_DATA *dp = new IDF_DRILL_DATA;
+ *dp = drill;
+
+ if( AddDrill( dp ) == NULL )
+ {
+ delete dp;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: could not add drill data; cannot continue reading the file" ) );
+ }
+ }
+
+ return;
+}
+
+
+// read the NOTES section
+void IDF3_BOARD::readBrdNotes( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState )
+{
+ IDF_NOTE note;
+
+ while( note.readNote( aBoardFile, aBoardState, unit ) )
+ {
+ IDF_NOTE *np = new IDF_NOTE;
+ *np = note;
+ notes.push_back( np );
+ }
+
+ return;
+}
+
+
+// read the component placement section
+void IDF3_BOARD::readBrdPlacement( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState, bool aNoSubstituteOutlines )
+{
+ IDF3_COMP_OUTLINE_DATA oldata;
+
+ while( oldata.readPlaceData( aBoardFile, aBoardState, this, idfVer, aNoSubstituteOutlines ) );
+
+ return;
+}
+
+
+// read the board HEADER
+void IDF3_BOARD::readBrdHeader( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState )
+{
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ int idx = 0;
+ bool quoted = false;
+ std::string token;
+
+ // RECORD 1: ".HEADER" must be the very first line
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( !aBoardFile.good() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading board header" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: first line must be .HEADER\n" ) );
+
+ if( !CompareToken( ".HEADER", iline ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* first line must be .HEADER and have no quotes or trailing text" ) );
+
+ // RECORD 2:
+ // File Type [str]: BOARD_FILE (PANEL_FILE not supported)
+ // IDF Version Number [float]: must be 3.0
+ // Source System [str]: ignored
+ // Date [str]: ignored
+ // Board File Version [int]: ignored
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( !aBoardFile.good() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading board header, RECORD 2" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: comment within .HEADER section" ) );
+
+ idx = 0;
+ GetIDFString( iline, token, quoted, idx );
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* File Type in HEADER section must not be in quotes" ) );
+
+ if( !CompareToken( "BOARD_FILE", token ) )
+ {
+ ERROR_IDF;
+
+ if( CompareToken( "PANEL_FILE", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "not a board file\n"
+ "* PANEL_FILE is not supported (expecting BOARD_FILE)" ) );
+ else
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Expecting string: BOARD_FILE" ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: HEADER section, RECORD 2: no FIELD 2" ) );
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: IDF Version must not be in quotes" ) );
+
+ if( !token.compare( "3.0" ) || !token.compare( "3." ) || !token.compare( "3" ) )
+ idfVer = IDF_V3;
+ else if( !token.compare( "2.0" ) || !token.compare( "2." ) || !token.compare( "2" ) )
+ idfVer = IDF_V2;
+ else
+ {
+ ostringstream ostr;
+
+ ostr << "unsupported IDF version\n";
+ ostr << "* Expecting version to be a variant of '3.0', '2.0' (value: '" << token << "')\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 3: no Source System string" ) );
+
+ brdSource = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 4: no Date string" ) );
+
+ brdDate = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 5: no Board File Version number" ) );
+
+ std::istringstream istr;
+ istr.str( token );
+
+ istr >> brdFileVersion;
+
+ if( istr.fail() )
+ {
+ ERROR_IDF << "invalid Board File Version in header\n";
+ cerr << "* Setting default version of 1\n";
+ brdFileVersion = 1;
+ }
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 5: Board File Version must not be in quotes" ) );
+
+ // RECORD 3:
+ // Board Name [str]: stored
+ // Units [str]: MM or THOU
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( !aBoardFile.good() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* problems reading board header, RECORD 2" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: comment within .HEADER section" ) );
+
+ idx = 0;
+ GetIDFString( iline, token, quoted, idx );
+
+ boardName = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 3, FIELD 1: no Board Name" ) );
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 3, FIELD 2: UNIT may not be in quotes" ) );
+
+ if( CompareToken( "MM", token ) )
+ {
+ unit = IDF3::UNIT_MM;
+ }
+ else if( CompareToken( "THOU", token ) )
+ {
+ unit = IDF3::UNIT_THOU;
+ }
+ else if( ( idfVer == IDF_V2 ) && CompareToken( "TNM", token ) )
+ {
+ unit = IDF3::UNIT_TNM;
+ }
+ else
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* HEADER section, RECORD 3, FIELD 2: expecting MM or THOU (got '" << token << "')\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ olnBoard.SetUnit( unit );
+
+ // RECORD 4:
+ // .END_HEADER
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( ( !aBoardFile.good() && !aBoardFile.eof() ) || iline.empty() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading board header, RECORD 4" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: comment within .HEADER section\n" ) );
+
+ if( !CompareToken( ".END_HEADER", iline ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* Violation of specification: expected .END_HEADER\n";
+ ostr << "* line: '" << iline << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ aBoardState = IDF3::FILE_HEADER;
+ return;
+}
+
+
+// read individual board sections; pay attention to IDFv3 section specifications
+void IDF3_BOARD::readBrdSection( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState,
+ bool aNoSubstituteOutlines )
+{
+ std::list< std::string > comments; // comments associated with a section
+
+ // Reads in .SECTION_ID or #COMMENTS
+ // Expected SECTION IDs:
+ // .BOARD_OUTLINE
+ // .PANEL_OUTLINE (NOT SUPPORTED)
+ // .OTHER_OUTLINE
+ // .ROUTE_OUTLINE
+ // .PLACE_OUTLINE
+ // .ROUTE_KEEPOUT
+ // .VIA_KEEPOUT
+ // .PLACE_KEEPOUT
+ // .PLACE_REGION
+ // .DRILLED_HOLES
+ // .NOTES
+ // .PLACEMENT
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ int idx = 0;
+ bool quoted = false;
+ std::string token;
+
+ while( aBoardFile.good() )
+ {
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( !aBoardFile.good() )
+ {
+ if( aBoardFile.eof() && aBoardState >= IDF3::FILE_HEADER && aBoardState < IDF3::FILE_INVALID )
+ {
+ if( !comments.empty() )
+ ERROR_IDF << "[warning]: trailing comments in IDF file (comments will be lost)\n";
+
+ return;
+ }
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading board section" ) );
+ }
+
+ if( isComment )
+ {
+ comments.push_back( iline );
+ continue;
+ }
+
+ // This must be a header
+ GetIDFString( iline, token, quoted, idx );
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* Violation of specification: quoted string where SECTION HEADER expected\n";
+ ostr << "* line: '" << iline << "'";
+ ostr << "* position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( CompareToken( ".BOARD_OUTLINE", token ) )
+ {
+ if( aBoardState != IDF3::FILE_HEADER )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: no HEADER section" ) );
+ }
+
+ olnBoard.readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ olnBoard.AddComment( *its );
+ ++its;
+ }
+ }
+
+ aBoardState = IDF3::FILE_OUTLINE;
+ return;
+ }
+
+ if( CompareToken( ".PANEL_OUTLINE", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "PANEL_OUTLINE not supported" ) );
+
+ if( CompareToken( ".OTHER_OUTLINE", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .OTHER_OUTLINE" ) );
+
+ OTHER_OUTLINE* op = new OTHER_OUTLINE( this );
+
+ if( op == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not create OTHER_OUTLINE object" ) );
+
+ op->SetUnit( unit );
+ op->readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ op->AddComment( *its );
+ ++its;
+ }
+ }
+
+ if( olnOther.insert( pair<string, OTHER_OUTLINE*>(op->GetOutlineIdentifier(), op) ).second == false )
+ {
+ ostringstream ostr;
+ ostr << "invalid IDF file\n";
+ ostr << "* Violation of specification. Non-unique ID in OTHER_OUTLINE '";
+ ostr << op->GetOutlineIdentifier() << "'\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* pos: " << pos;
+ delete op;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ return;
+ }
+
+ if( CompareToken( ".ROUTE_OUTLINE", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .ROUTE_OUTLINE" ) );
+
+ ROUTE_OUTLINE* op = new ROUTE_OUTLINE( this );
+
+ if( op == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not create ROUTE_OUTLINE object" ) );
+
+ op->SetUnit( unit );
+ op->readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ op->AddComment( *its );
+ ++its;
+ }
+ }
+
+ olnRoute.push_back( op );
+
+ return;
+ }
+
+ if( CompareToken( ".PLACE_OUTLINE", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .PLACE_OUTLINE" ) );
+
+ PLACE_OUTLINE* op = new PLACE_OUTLINE( this );
+
+ if( op == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not create PLACE_OUTLINE object" ) );
+
+ op->SetUnit( unit );
+ op->readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ op->AddComment( *its );
+ ++its;
+ }
+ }
+
+ olnPlace.push_back( op );
+
+ return;
+ }
+
+ if( CompareToken( ".ROUTE_KEEPOUT", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .ROUTE_KEEPOUT" ) );
+
+ ROUTE_KO_OUTLINE* op = new ROUTE_KO_OUTLINE( this );
+
+ if( op == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not create ROUTE_KEEPOUT object" ) );
+
+ op->SetUnit( unit );
+ op->readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ op->AddComment( *its );
+ ++its;
+ }
+ }
+
+ olnRouteKeepout.push_back( op );
+
+ return;
+ }
+
+ if( CompareToken( ".VIA_KEEPOUT", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .VIA_KEEPOUT" ) );
+
+ VIA_KO_OUTLINE* op = new VIA_KO_OUTLINE( this );
+
+ if( op == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not create VIA_KEEPOUT object" ) );
+
+ op->SetUnit( unit );
+ op->readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ op->AddComment( *its );
+ ++its;
+ }
+ }
+
+ olnViaKeepout.push_back( op );
+
+ return;
+ }
+
+ if( CompareToken( ".PLACE_KEEPOUT", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .PLACE_KEEPOUT" ) );
+
+ PLACE_KO_OUTLINE* op = new PLACE_KO_OUTLINE( this );
+
+ if( op == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not create PLACE_KEEPOUT object" ) );
+
+ op->SetUnit( unit );
+ op->readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ op->AddComment( *its );
+ ++its;
+ }
+ }
+
+ olnPlaceKeepout.push_back( op );
+
+ return;
+ }
+
+ if( CompareToken( ".PLACE_REGION", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .PLACE_REGION" ) );
+
+ GROUP_OUTLINE* op = new GROUP_OUTLINE( this );
+
+ if( op == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not create PLACE_REGION object" ) );
+
+ op->SetUnit( unit );
+ op->readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ op->AddComment( *its );
+ ++its;
+ }
+ }
+
+ olnGroup.insert( pair<string, GROUP_OUTLINE*>(op->GetGroupName(), op) );
+
+ return;
+ }
+
+ if( CompareToken( ".DRILLED_HOLES", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .DRILLED_HOLES" ) );
+
+ readBrdDrills( aBoardFile, aBoardState );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ drillComments.push_back( *its );
+ ++its;
+ }
+ }
+
+ return;
+ }
+
+ if( CompareToken( ".NOTES", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .NOTES" ) );
+
+ if( idfVer < IDF_V3 )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv2 file\n"
+ "* Violation of specification: NOTES section not in specification" ) );
+
+ readBrdNotes( aBoardFile, aBoardState );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ noteComments.push_back( *its );
+ ++its;
+ }
+ }
+
+ return;
+ }
+
+ if( CompareToken( ".PLACEMENT", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .PLACEMENT" ) );
+
+ readBrdPlacement( aBoardFile, aBoardState, aNoSubstituteOutlines );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ placeComments.push_back( *its );
+ ++its;
+ }
+ }
+
+ return;
+ }
+ } // while( aBoardFile.good()
+
+ return;
+} // readBrdSection()
+
+
+// read the board file data
+void IDF3_BOARD::readBoardFile( const std::string& aFileName, bool aNoSubstituteOutlines )
+{
+ std::ifstream brd;
+
+ brd.exceptions ( std::ifstream::badbit );
+
+ try
+ {
+ brd.open( aFileName.c_str(), std::ios_base::in | std::ios_base::binary );
+
+ if( !brd.is_open() )
+ {
+ ostringstream ostr;
+ ostr << "\n* could not open file: '" << aFileName << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ IDF3::FILE_STATE state = IDF3::FILE_START;
+
+ // note: as per IDFv3 specification:
+ // "The Header section must be the first section in the file, the second
+ // section must be the Outline section, and the last section must be the
+ // Placement section. All other sections may be in any order."
+
+ // further notes: Except for the HEADER section, sections may be preceeded by
+ // comment lines which will be copied back out on write(). No comments may
+ // be associated with the board file itself since the only logical location
+ // for unambiguous association is at the end of the file, which is inconvenient
+ // for large files.
+
+ readBrdHeader( brd, state );
+
+ // read the various sections
+ while( state != IDF3::FILE_PLACEMENT && brd.good() )
+ readBrdSection( brd, state, aNoSubstituteOutlines );
+
+ if( !brd.good() )
+ {
+ // check if we have valid data
+ if( brd.eof() && state >= IDF3::FILE_OUTLINE && state < IDF3::FILE_INVALID )
+ {
+ brd.close();
+ return;
+ }
+
+ brd.close();
+
+ ostringstream ostr;
+ ostr << "\n* empty IDF file: '" << aFileName << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( brd.good() && state == IDF3::FILE_PLACEMENT )
+ {
+ // read in any trailing lines and report on ignored comments (minor fault)
+ // and any non-comment item (non-compliance with IDFv3)
+ while( brd.good() )
+ {
+ while( !FetchIDFLine( brd, iline, isComment, pos ) && brd.good() );
+
+ // normally this is a fault but we have all the data in accordance with specs
+ if( ( !brd.good() && !brd.eof() ) || iline.empty() )
+ break;
+
+ if( isComment )
+ {
+ ERROR_IDF << "[warning]: trailing comments after PLACEMENT\n";
+ }
+ else
+ {
+ ostringstream ostr;
+ ostr << "\n* problems reading file: '" << aFileName << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: non-comment lines after PLACEMENT section" ) );
+ }
+ }
+ }
+ }
+ catch( const std::exception& e )
+ {
+ brd.exceptions ( std::ios_base::goodbit );
+
+ if( brd.is_open() )
+ brd.close();
+
+ throw;
+ }
+
+ brd.close();
+ return;
+} // readBoardFile()
+
+
+// read the library sections (outlines)
+void IDF3_BOARD::readLibSection( std::ifstream& aLibFile, IDF3::FILE_STATE& aLibState, IDF3_BOARD* aBoard )
+{
+ if( aBoard == NULL )
+ {
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: invoked with NULL reference aBoard" ) );
+ }
+
+ std::list< std::string > comments; // comments associated with a section
+
+ // Reads in .ELECTRICAL, .MECHANICAL or #COMMENTS
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ int idx = 0;
+ bool quoted = false;
+ std::string token;
+ IDF3_COMP_OUTLINE *pout = new IDF3_COMP_OUTLINE( this );
+
+ if( !pout )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* memory allocation failure" ) );
+
+ while( aLibFile.good() )
+ {
+ while( !FetchIDFLine( aLibFile, iline, isComment, pos ) && aLibFile.good() );
+
+ if( !aLibFile.good() && !aLibFile.eof() )
+ {
+ delete pout;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading library section" ) );
+ }
+
+ // no data was read; this only happens at eof()
+ if( iline.empty() )
+ {
+ delete pout;
+ return;
+ }
+
+ if( isComment )
+ {
+ comments.push_back( iline );
+ continue;
+ }
+
+ // This must be a header
+ GetIDFString( iline, token, quoted, idx );
+
+ if( quoted )
+ {
+ ostringstream ostr;
+ ostr << "invalid IDF library\n";
+ ostr << "* Violation of specification: quoted string where .ELECTRICAL or .MECHANICAL expected\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* pos: " << pos;
+ delete pout;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( CompareToken( ".ELECTRICAL", token ) || CompareToken( ".MECHANICAL", token ) )
+ {
+ pout->readData( aLibFile, token, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ pout->AddComment( *its );
+ ++its;
+ }
+ }
+
+ IDF3_COMP_OUTLINE* cop = aBoard->GetComponentOutline( pout->GetUID() );
+
+ if( cop == NULL )
+ {
+ compOutlines.insert( pair<const std::string, IDF3_COMP_OUTLINE*>( pout->GetUID(), pout ) );
+ }
+ else
+ {
+ if( MatchCompOutline( pout, cop ) )
+ {
+ delete pout;
+ // everything is fine; the outlines are genuine duplicates
+ return;
+ }
+
+ ostringstream ostr;
+ ostr << "invalid IDF library\n";
+ ostr << "duplicate Component Outline: '" << pout->GetUID() << "'\n";
+ ostr << "* Violation of specification: multiple outlines have the same GEOM and PART name\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* pos: " << pos;
+ delete pout;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ return;
+ }
+ else
+ {
+ ostringstream ostr;
+ ostr << "invalid IDF library\n";
+ ostr << "* Expecting .ELECTRICAL or .MECHANICAL, got '" << token << "'\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* pos: " << pos;
+ delete pout;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+
+ delete pout;
+
+ if( !aLibFile.eof() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading IDF library file" ) );
+
+ return;
+}
+
+
+// read the library HEADER
+void IDF3_BOARD::readLibHeader( std::ifstream& aLibFile, IDF3::FILE_STATE& aLibState )
+{
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ int idx = 0;
+ bool quoted = false;
+ std::string token;
+
+ // RECORD 1: ".HEADER" must be the very first line
+ while( !FetchIDFLine( aLibFile, iline, isComment, pos ) && aLibFile.good() );
+
+ if( !aLibFile.good() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* premature end of file (no HEADER)" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification: first line must be .HEADER" ) );
+
+ if( !CompareToken( ".HEADER", iline ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification:\n"
+ "* first line must be .HEADER and have no quotes or trailing text" ) );
+
+ // RECORD 2:
+ // File Type [str]: LIBRARY_FILE
+ // IDF Version Number [float]: must be 3.0
+ // Source System [str]: ignored
+ // Date [str]: ignored
+ // Library File Version [int]: ignored
+ while( !FetchIDFLine( aLibFile, iline, isComment, pos ) && aLibFile.good() );
+
+ if( !aLibFile.good() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* premature end of HEADER" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification: comment within .HEADER section" ) );
+
+ idx = 0;
+ GetIDFString( iline, token, quoted, idx );
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification:\n"
+ "* file Type in HEADER section must not be in quotes" ) );
+
+ if( !CompareToken( "LIBRARY_FILE", token ) )
+ {
+ ostringstream ostr;
+ ostr << "invalid IDF library\n";
+ ostr << "* Expecting string: LIBRARY_FILE (got '" << token << "')\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification: HEADER section, RECORD 2: no FIELD 2" ) );
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification: IDF Version must not be in quotes" ) );
+
+ if( !token.compare( "3.0" ) || !token.compare( "3." ) || !token.compare( "3" ) )
+ idfVer = IDF_V3;
+ else if( !token.compare( "2.0" ) || !token.compare( "2." ) || !token.compare( "2" ) )
+ idfVer = IDF_V2;
+ else
+ {
+ ostringstream ostr;
+
+ ostr << "unsupported IDF version\n";
+ ostr << "* Expecting version to be a variant of '3.0', '2.0' (value: '" << token << "')\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 3: no Source System string" ) );
+
+ libSource = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 4: no Date string" ) );
+
+ libDate = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 5: no Board File Version number" ) );
+
+ std::istringstream istr;
+ istr.str( token );
+
+ istr >> libFileVersion;
+
+ if( istr.fail() )
+ {
+ ERROR_IDF << "invalid Library File Version in header\n";
+ cerr << "* Setting default version of 1\n";
+ libFileVersion = 1;
+ }
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 5: Library File Version must not be in quotes" ) );
+
+ // RECORD 3:
+ // .END_HEADER
+ while( !FetchIDFLine( aLibFile, iline, isComment, pos ) && aLibFile.good() );
+
+ if( ( !aLibFile.good() && !aLibFile.eof() ) || iline.empty() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading library header, RECORD 3" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification: comment within .HEADER section" ) );
+
+ if( !CompareToken( ".END_HEADER", iline ) )
+ {
+ ostringstream ostr;
+ ostr << "invalid IDF header\n";
+ ostr << "* Violation of specification: expected .END_HEADER (got '" << iline << "')\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ aLibState = IDF3::FILE_HEADER;
+ return;
+}
+
+
+// read the library file data
+void IDF3_BOARD::readLibFile( const std::string& aFileName )
+{
+ std::ifstream lib;
+
+ lib.exceptions ( std::ifstream::badbit );
+
+ try
+ {
+ lib.open( aFileName.c_str(), std::ios_base::in | std::ios_base::binary );
+
+ IDF3::FILE_STATE state = IDF3::FILE_START;
+
+ readLibHeader( lib, state );
+
+ while( lib.good() ) readLibSection( lib, state, this );
+ }
+ catch( const std::exception& e )
+ {
+ lib.exceptions ( std::ios_base::goodbit );
+
+ if( lib.is_open() )
+ lib.close();
+
+ throw;
+ }
+
+ lib.close();
+ return;
+}
+
+
+bool IDF3_BOARD::ReadFile( const wxString& aFullFileName, bool aNoSubstituteOutlines )
+{
+ // 1. Check that the file extension is 'emn'
+ // 2. Check if a file with extension 'emp' exists and read it
+ // 3. Open the specified filename and read it
+
+ std::string fname = TO_UTF8( aFullFileName );
+
+ wxFileName brdname( aFullFileName );
+ wxFileName libname( aFullFileName );
+
+ brdname.SetExt( wxT( "emn" ) );
+ libname.SetExt( wxT( "emp" ) );
+
+ std::string bfname = TO_UTF8( aFullFileName );
+
+ try
+ {
+ if( !brdname.IsOk() )
+ {
+ ostringstream ostr;
+ ostr << "\n* invalid file name: '" << bfname << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !brdname.FileExists() )
+ {
+ ostringstream ostr;
+ ostr << "\n* no such file: '" << bfname << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !brdname.IsFileReadable() )
+ {
+ ostringstream ostr;
+ ostr << "\n* cannot read file: '" << bfname << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ bfname = TO_UTF8( brdname.GetFullPath() );
+ std::string lfname = TO_UTF8( libname.GetFullPath() );
+
+ if( !libname.FileExists() )
+ {
+ // NOTE: Since this is a common case we simply proceed
+ // with the assumption that there is no library file;
+ // however we print a message to inform the user.
+ ERROR_IDF;
+ cerr << "no associated library file (*.emp)\n";
+ }
+ else if( !libname.IsFileReadable() )
+ {
+ ostringstream ostr;
+ ostr << "\n* cannot read library file: '" << lfname << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ else
+ {
+ // read the library file before proceeding
+ readLibFile( lfname );
+ }
+
+ // read the board file
+ readBoardFile( bfname, aNoSubstituteOutlines );
+ }
+ catch( const std::exception& e )
+ {
+ Clear();
+ errormsg = e.what();
+
+ return false;
+ }
+
+ return true;
+}
+
+
+// write the library file data
+bool IDF3_BOARD::writeLibFile( const std::string& aFileName )
+{
+ std::ofstream lib;
+ lib.exceptions( std::ofstream::failbit );
+
+ try
+ {
+ lib.open( aFileName.c_str(), std::ios_base::out );
+
+ wxDateTime tdate( time( NULL ) );
+
+ if( idfSource.empty() )
+ idfSource = "KiCad-IDF Framework";
+
+ ostringstream fileDate;
+ fileDate << setfill( '0' ) << setw(4) << tdate.GetYear();
+ fileDate << "/" << setw(2) << tdate.GetMonth() << "/" << tdate.GetDay();
+ fileDate << "." << tdate.GetHour() << ":" << tdate.GetMinute() << ":" << tdate.GetSecond();
+ libDate = fileDate.str();
+
+ lib << ".HEADER\n";
+ lib << "LIBRARY_FILE 3.0 \"Created by " << idfSource;
+ lib << "\" " << libDate << " " << (++libFileVersion) << "\n";
+ lib << ".END_HEADER\n\n";
+
+ std::map< std::string, IDF3_COMP_OUTLINE*>::iterator its = compOutlines.begin();
+ std::map< std::string, IDF3_COMP_OUTLINE*>::iterator ite = compOutlines.end();
+
+ while( its != ite )
+ {
+ its->second->writeData( lib );
+ ++its;
+ }
+
+ }
+ catch( const std::exception& e )
+ {
+ lib.exceptions( std::ios_base::goodbit );
+
+ if( lib.is_open() )
+ lib.close();
+
+ throw;
+ }
+
+ lib.close();
+
+ return true;
+}
+
+// write the board file data
+void IDF3_BOARD::writeBoardFile( const std::string& aFileName )
+{
+ std::ofstream brd;
+ brd.exceptions( std::ofstream::failbit );
+
+ try
+ {
+ brd.open( aFileName.c_str(), std::ios_base::out );
+
+ wxDateTime tdate( time( NULL ) );
+
+ if( idfSource.empty() )
+ idfSource = "KiCad-IDF Framework";
+
+ ostringstream fileDate;
+ fileDate << setfill( '0' ) << setw(4) << tdate.GetYear();
+ fileDate << "/" << setw(2) << tdate.GetMonth() << "/" << tdate.GetDay();
+ fileDate << "." << tdate.GetHour() << ":" << tdate.GetMinute() << ":" << tdate.GetSecond();
+ brdDate = fileDate.str();
+
+ brd << ".HEADER\n";
+ brd << "BOARD_FILE 3.0 \"Created by " << idfSource;
+ brd << "\" " << brdDate << " " << (++brdFileVersion) << "\n";
+
+ if( boardName.empty() )
+ brd << "\"BOARD WITH NO NAME\" ";
+ else
+ brd << "\"" << boardName << "\" ";
+
+ brd << setw(1) << setfill( ' ' );
+
+ if( unit == IDF3::UNIT_MM )
+ brd << "MM\n";
+ else
+ brd << "THOU\n";
+
+ brd << ".END_HEADER\n\n";
+
+ // write the BOARD_OUTLINE
+ olnBoard.writeData( brd );
+
+ // OTHER outlines
+ do
+ {
+ std::map<std::string, OTHER_OUTLINE*>::iterator its = olnOther.begin();
+ std::map<std::string, OTHER_OUTLINE*>::iterator ite = olnOther.end();
+
+ while(its != ite )
+ {
+ its->second->writeData( brd );
+ ++its;
+ }
+
+ } while( 0 );
+
+ // ROUTE outlines
+ do
+ {
+ std::list<ROUTE_OUTLINE*>::iterator its = olnRoute.begin();
+ std::list<ROUTE_OUTLINE*>::iterator ite = olnRoute.end();
+
+ while( its != ite )
+ {
+ (*its)->writeData( brd );
+ ++its;
+ }
+
+ } while( 0 );
+
+ // PLACEMENT outlines
+ do
+ {
+ std::list<PLACE_OUTLINE*>::iterator its = olnPlace.begin();
+ std::list<PLACE_OUTLINE*>::iterator ite = olnPlace.end();
+
+ while( its != ite )
+ {
+ (*its)->writeData( brd );
+ ++its;
+ }
+
+ } while( 0 );
+
+ // ROUTE KEEPOUT outlines
+ do
+ {
+ std::list<ROUTE_KO_OUTLINE*>::iterator its = olnRouteKeepout.begin();
+ std::list<ROUTE_KO_OUTLINE*>::iterator ite = olnRouteKeepout.end();
+
+ while( its != ite )
+ {
+ (*its)->writeData( brd );
+ ++its;
+ }
+
+ } while( 0 );
+
+ // VIA KEEPOUT outlines
+ do
+ {
+ std::list<VIA_KO_OUTLINE*>::iterator its = olnViaKeepout.begin();
+ std::list<VIA_KO_OUTLINE*>::iterator ite = olnViaKeepout.end();
+
+ while( its != ite )
+ {
+ (*its)->writeData( brd );
+ ++its;
+ }
+
+ } while( 0 );
+
+ // PLACE KEEPOUT outlines
+ do
+ {
+ std::list<PLACE_KO_OUTLINE*>::iterator its = olnPlaceKeepout.begin();
+ std::list<PLACE_KO_OUTLINE*>::iterator ite = olnPlaceKeepout.end();
+
+ while( its != ite )
+ {
+ (*its)->writeData( brd );
+ ++its;
+ }
+
+ } while( 0 );
+
+ // PLACEMENT GROUP outlines
+ do
+ {
+ std::multimap<std::string, GROUP_OUTLINE*>::iterator its = olnGroup.begin();
+ std::multimap<std::string, GROUP_OUTLINE*>::iterator ite = olnGroup.end();
+
+ while( its != ite )
+ {
+ its->second->writeData( brd );
+ ++its;
+ }
+
+ } while( 0 );
+
+ // Drilled holes
+ do
+ {
+ std::list<std::string>::iterator itds = drillComments.begin();
+ std::list<std::string>::iterator itde = drillComments.end();
+
+ while( itds != itde )
+ {
+ brd << "# " << *itds << "\n";
+ ++itds;
+ }
+
+ brd << ".DRILLED_HOLES\n";
+
+ std::list<IDF_DRILL_DATA*>::iterator itbs = board_drills.begin();
+ std::list<IDF_DRILL_DATA*>::iterator itbe = board_drills.end();
+
+ while( itbs != itbe )
+ {
+ (*itbs)->write( brd, unit );
+ ++itbs;
+ }
+
+ std::map< std::string, IDF3_COMPONENT*>::iterator itcs = components.begin();
+ std::map< std::string, IDF3_COMPONENT*>::iterator itce = components.end();
+
+ while( itcs != itce )
+ {
+ itcs->second->writeDrillData( brd );
+ ++itcs;
+ }
+
+ brd << ".END_DRILLED_HOLES\n\n";
+ } while( 0 );
+
+ // Notes
+ if( !notes.empty() )
+ {
+ std::list<std::string>::iterator itncs = noteComments.begin();
+ std::list<std::string>::iterator itnce = noteComments.end();
+
+ while( itncs != itnce )
+ {
+ brd << "# " << *itncs << "\n";
+ ++itncs;
+ }
+
+ brd << ".NOTES\n";
+
+ std::list<IDF_NOTE*>::iterator itns = notes.begin();
+ std::list<IDF_NOTE*>::iterator itne = notes.end();
+
+ while( itns != itne )
+ {
+ (*itns)->writeNote( brd, unit );
+ ++itns;
+ }
+
+ brd << ".END_NOTES\n\n";
+
+ }
+
+ // Placement
+ if( !components.empty() )
+ {
+ std::list<std::string>::iterator itpcs = placeComments.begin();
+ std::list<std::string>::iterator itpce = placeComments.end();
+
+ while( itpcs != itpce )
+ {
+ brd << "# " << *itpcs << "\n";
+ ++itpcs;
+ }
+
+ std::map< std::string, IDF3_COMPONENT*>::iterator itcs = components.begin();
+ std::map< std::string, IDF3_COMPONENT*>::iterator itce = components.end();
+
+ // determine if there are any component outlines at all and avoid
+ // writing an empty PLACEMENT section if there are no outlines.
+ // this will cost a little time but prevents software such as
+ // CircuitWorks from segfaulting on an empty section.
+
+ bool hasOutlines = false;
+
+ while( itcs != itce )
+ {
+ if( itcs->second->GetOutlinesSize() > 0 )
+ {
+ itcs = components.begin();
+ hasOutlines = true;
+ break;
+ }
+
+ ++itcs;
+ }
+
+ if( hasOutlines )
+ {
+ brd << ".PLACEMENT\n";
+
+ while( itcs != itce )
+ {
+ itcs->second->writePlaceData( brd );
+ ++itcs;
+ }
+
+ brd << ".END_PLACEMENT\n";
+ }
+
+ }
+
+ }
+ catch( const std::exception& e )
+ {
+ brd.exceptions( std::ios_base::goodbit );
+
+ if( brd.is_open() )
+ brd.close();
+
+ throw;
+ }
+
+ brd.close();
+
+ return;
+}
+
+
+bool IDF3_BOARD::WriteFile( const wxString& aFullFileName, bool aUnitMM, bool aForceUnitFlag )
+{
+ if( aUnitMM != IDF3::UNIT_THOU )
+ setUnit( IDF3::UNIT_MM, aForceUnitFlag );
+ else
+ setUnit( IDF3::UNIT_THOU, aForceUnitFlag );
+
+ // 1. Check that the file extension is 'emn'
+ // 2. Write the *.emn file according to the IDFv3 spec
+ // 3. Write the *.emp file according to the IDFv3 spec
+
+ std::string fname = TO_UTF8( aFullFileName );
+
+ wxFileName brdname( aFullFileName );
+ wxFileName libname( aFullFileName );
+
+ brdname.SetExt( wxT( "emn" ) );
+ libname.SetExt( wxT( "emp" ) );
+
+ std::string bfname = TO_UTF8( aFullFileName );
+
+ try
+ {
+ if( !brdname.IsOk() )
+ {
+ ostringstream ostr;
+ ostr << "\n* invalid file name: '" << bfname << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( brdname.FileExists() && !brdname.IsFileWritable() )
+ {
+ ostringstream ostr;
+ ostr << "cannot overwrite existing board file\n";
+ ostr << "* filename: '" << bfname << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ bfname = TO_UTF8( brdname.GetFullPath() );
+ std::string lfname = TO_UTF8( libname.GetFullPath() );
+
+ if( libname.FileExists() && !libname.IsFileWritable() )
+ {
+ ostringstream ostr;
+ ostr << "cannot overwrite existing library file\n";
+ ostr << "* filename: '" << lfname << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ writeLibFile( lfname );
+ writeBoardFile( bfname );
+
+ }
+ catch( const std::exception& e )
+ {
+ errormsg = e.what();
+
+ return false;
+ }
+
+ return true;
+}
+
+
+const std::string& IDF3_BOARD::GetIDFSource( void )
+{
+ return idfSource;
+}
+
+
+void IDF3_BOARD::SetIDFSource( const std::string& aIDFSource )
+{
+ idfSource = aIDFSource;
+ return;
+}
+
+const std::string& IDF3_BOARD::GetBoardSource( void )
+{
+ return brdSource;
+}
+
+const std::string& IDF3_BOARD::GetLibrarySource( void )
+{
+ return libSource;
+}
+
+const std::string& IDF3_BOARD::GetBoardDate( void )
+{
+ return brdDate;
+}
+
+const std::string& IDF3_BOARD::GetLibraryDate( void )
+{
+ return libDate;
+}
+
+int IDF3_BOARD::GetBoardVersion( void )
+{
+ return brdFileVersion;
+}
+
+bool IDF3_BOARD::SetBoardVersion( int aVersion )
+{
+ if( aVersion < 0 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* board version (" << aVersion << ") must be >= 0";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ brdFileVersion = aVersion;
+
+ return true;
+}
+
+
+int IDF3_BOARD::GetLibraryVersion( void )
+{
+ return libFileVersion;
+}
+
+
+bool IDF3_BOARD::SetLibraryVersion( int aVersion )
+{
+ if( aVersion < 0 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* library version (" << aVersion << ") must be >= 0";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ libFileVersion = aVersion;
+
+ return true;
+}
+
+
+double IDF3_BOARD::GetUserScale( void )
+{
+ return userScale;
+}
+
+
+bool IDF3_BOARD::SetUserScale( double aScaleFactor )
+{
+ if( aScaleFactor == 0.0 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: user scale factor must not be 0";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ userScale = aScaleFactor;
+ return true;
+}
+
+int IDF3_BOARD::GetUserPrecision( void )
+{
+ return userPrec;
+}
+
+bool IDF3_BOARD::SetUserPrecision( int aPrecision )
+{
+ if( aPrecision < 1 || aPrecision > 8 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* precision value (" << aPrecision << ") must be 1..8";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ userPrec = aPrecision;
+ return true;
+}
+
+
+void IDF3_BOARD::GetUserOffset( double& aXoff, double& aYoff )
+{
+ aXoff = userXoff;
+ aYoff = userYoff;
+ return;
+}
+
+
+void IDF3_BOARD::SetUserOffset( double aXoff, double aYoff )
+{
+ userXoff = aXoff;
+ userYoff = aYoff;
+ return;
+}
+
+
+bool IDF3_BOARD::AddBoardOutline( IDF_OUTLINE* aOutline )
+{
+ if( !olnBoard.AddOutline( aOutline ) )
+ {
+ errormsg = olnBoard.GetError();
+
+ return false;
+ }
+
+ return true;
+}
+
+
+bool IDF3_BOARD::DelBoardOutline( IDF_OUTLINE* aOutline )
+{
+ if( !olnBoard.DelOutline( aOutline ) )
+ {
+ errormsg = olnBoard.GetError();
+ return false;
+ }
+
+ return true;
+}
+
+
+bool IDF3_BOARD::DelBoardOutline( size_t aIndex )
+{
+ if( !olnBoard.DelOutline( aIndex ) )
+ {
+ errormsg = olnBoard.GetError();
+ return false;
+ }
+
+ return true;
+}
+
+
+size_t IDF3_BOARD::GetBoardOutlinesSize( void )
+{
+ return olnBoard.OutlinesSize();
+}
+
+
+BOARD_OUTLINE* IDF3_BOARD::GetBoardOutline( void )
+{
+ return &olnBoard;
+}
+
+
+const std::list< IDF_OUTLINE* >*const IDF3_BOARD::GetBoardOutlines( void )
+{
+ return olnBoard.GetOutlines();
+}
+
+
+IDF_DRILL_DATA* IDF3_BOARD::AddBoardDrill( double aDia, double aXpos, double aYpos,
+ IDF3::KEY_PLATING aPlating,
+ const std::string aHoleType,
+ IDF3::KEY_OWNER aOwner )
+{
+ IDF_DRILL_DATA* drill = new IDF_DRILL_DATA( aDia, aXpos, aYpos, aPlating,
+ "BOARD", aHoleType, aOwner );
+
+ if( drill != NULL )
+ board_drills.push_back( drill );
+
+ return drill;
+}
+
+IDF_DRILL_DATA* IDF3_BOARD::AddDrill( IDF_DRILL_DATA* aDrilledHole )
+{
+ if( !aDrilledHole )
+ return NULL;
+
+ // note: PANEL drills are essentially BOARD drills which
+ // the panel requires to be present
+ if( CompareToken( "BOARD", aDrilledHole->GetDrillRefDes() )
+ || CompareToken( "PANEL", aDrilledHole->GetDrillRefDes() ) )
+ {
+ board_drills.push_back( aDrilledHole );
+ return aDrilledHole;
+ }
+
+ return addCompDrill( aDrilledHole );
+}
+
+
+bool IDF3_BOARD::DelBoardDrill( double aDia, double aXpos, double aYpos )
+{
+ errormsg.clear();
+
+ std::list<IDF_DRILL_DATA*>::iterator sp = board_drills.begin();
+ std::list<IDF_DRILL_DATA*>::iterator ep = board_drills.end();
+ bool rval = false;
+
+ while( sp != ep )
+ {
+ if( (*sp)->Matches( aDia, aXpos, aYpos ) )
+ {
+#ifndef DISABLE_IDF_OWNERSHIP
+ IDF3::KEY_OWNER keyo = (*sp)->GetDrillOwner();
+
+ if( keyo == UNOWNED || ( keyo == MCAD && cadType == CAD_MECH )
+ || ( keyo == ECAD && cadType == CAD_ELEC ) )
+ {
+ rval = true;
+ delete *sp;
+ sp = board_drills.erase( sp );
+ continue;
+ }
+ else
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* ownership violation; drill owner (";
+
+ switch( keyo )
+ {
+ case ECAD:
+ ostr << "ECAD";
+ break;
+
+ case MCAD:
+ ostr << "MCAD";
+ break;
+
+ default:
+ ostr << "invalid: " << keyo;
+ break;
+ }
+
+ ostr << ") may not be modified by ";
+
+ if( cadType == CAD_MECH )
+ ostr << "MCAD";
+ else
+ ostr << "ECAD";
+
+ errormsg = ostr.str();
+
+ ++sp;
+ continue;
+ }
+#else
+ rval = true;
+ delete *sp;
+ sp = board_drills.erase( sp );
+ continue;
+#endif
+ }
+
+ ++sp;
+ }
+
+ return rval;
+}
+
+
+// a slot is a deficient representation of a kicad slotted hole;
+// it is usually associated with a component but IDFv3 does not
+// provide for such an association. Note: this mechanism must bypass
+// the BOARD_OUTLINE ownership rules
+bool IDF3_BOARD::AddSlot( double aWidth, double aLength, double aOrientation, double aX, double aY )
+{
+ if( aWidth < IDF_MIN_DIA_MM )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* slot width (" << aWidth << ") must be >= " << IDF_MIN_DIA_MM;
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ if( aLength < IDF_MIN_DIA_MM )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* slot length (" << aLength << ") must be >= " << IDF_MIN_DIA_MM;
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ IDF_POINT c[2]; // centers
+ IDF_POINT pt[4];
+
+ double a1 = aOrientation / 180.0 * M_PI;
+ double a2 = a1 + M_PI_2;
+ double d1 = aLength / 2.0;
+ double d2 = aWidth / 2.0;
+ double sa1 = sin( a1 );
+ double ca1 = cos( a1 );
+ double dsa2 = d2 * sin( a2 );
+ double dca2 = d2 * cos( a2 );
+
+ c[0].x = aX + d1 * ca1;
+ c[0].y = aY + d1 * sa1;
+
+ c[1].x = aX - d1 * ca1;
+ c[1].y = aY - d1 * sa1;
+
+ pt[0].x = c[0].x - dca2;
+ pt[0].y = c[0].y - dsa2;
+
+ pt[1].x = c[1].x - dca2;
+ pt[1].y = c[1].y - dsa2;
+
+ pt[2].x = c[1].x + dca2;
+ pt[2].y = c[1].y + dsa2;
+
+ pt[3].x = c[0].x + dca2;
+ pt[3].y = c[0].y + dsa2;
+
+ IDF_OUTLINE* outline = new IDF_OUTLINE;
+
+ if( outline == NULL )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* could not create an outline object";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ // first straight run
+ IDF_SEGMENT* seg = new IDF_SEGMENT( pt[0], pt[1] );
+ outline->push( seg );
+ // first 180 degree cap
+ seg = new IDF_SEGMENT( c[1], pt[1], -180.0, true );
+ outline->push( seg );
+ // final straight run
+ seg = new IDF_SEGMENT( pt[2], pt[3] );
+ outline->push( seg );
+ // final 180 degree cap
+ seg = new IDF_SEGMENT( c[0], pt[3], -180.0, true );
+ outline->push( seg );
+
+ if( !olnBoard.addOutline( outline ) )
+ {
+ errormsg = olnBoard.GetError();
+ return false;
+ }
+
+ return true;
+}
+
+
+IDF_DRILL_DATA* IDF3_BOARD::addCompDrill( double aDia, double aXpos, double aYpos,
+ IDF3::KEY_PLATING aPlating,
+ const std::string aHoleType,
+ IDF3::KEY_OWNER aOwner,
+ const std::string& aRefDes )
+{
+ // first find the matching component; if it doesn't exist we must create it somehow -
+ // question is, do we need a component outline at this stage or can those be added later?
+ //
+ // Presumably we can create a component with no outline and add the outlines later.
+ // If a component is created and an outline specified but the outline is not loaded,
+ // we're screwed if (a) we have already read the library file (*.emp) or (b) we don't
+ // know the filename
+
+ std::string refdes = aRefDes;
+
+ // note: for components 'NOREFDES' would be assigned a Unique ID, but for holes
+ // there is no way of associating the hole with the correct entity (if any)
+ // so a hole added with "NOREFDES" goes to a generic component "NOREFDES"
+ if( refdes.empty() )
+ refdes = "NOREFDES";
+
+ // check if the target is BOARD or PANEL
+ if( CompareToken( "BOARD", refdes ) )
+ return AddBoardDrill( aDia, aXpos, aYpos, aPlating, aHoleType, aOwner );
+
+ if( CompareToken( "PANEL", refdes ) )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* PANEL data not supported";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ std::map<std::string, IDF3_COMPONENT*>::iterator ref = components.find( refdes );
+
+ if( ref == components.end() )
+ {
+ // create the item
+ IDF3_COMPONENT* comp = new IDF3_COMPONENT( this );
+
+ if( comp == NULL )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* could not create new component object";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ comp->SetParent( this );
+ comp->SetRefDes( refdes );
+ ref = components.insert( std::pair< std::string, IDF3_COMPONENT*> ( comp->GetRefDes(), comp ) ).first;
+ }
+
+ // add the drill
+ IDF_DRILL_DATA* dp = ref->second->AddDrill( aDia, aXpos, aYpos, aPlating, aHoleType, aOwner );
+
+ if( !dp )
+ {
+ errormsg = ref->second->GetError();
+ return NULL;
+ }
+
+ return dp;
+}
+
+
+IDF_DRILL_DATA* IDF3_BOARD::addCompDrill( IDF_DRILL_DATA* aDrilledHole )
+{
+ if( !aDrilledHole )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): NULL pointer";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ if( CompareToken( "PANEL", aDrilledHole->GetDrillRefDes() ) )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* PANEL data not supported";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ std::map<std::string, IDF3_COMPONENT*>::iterator ref = components.find( aDrilledHole->GetDrillRefDes() );
+
+ if( ref == components.end() )
+ {
+ // create the item
+ IDF3_COMPONENT* comp = new IDF3_COMPONENT( this );
+
+ if( comp == NULL )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* could not create new component object";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ comp->SetParent( this );
+ comp->SetRefDes( aDrilledHole->GetDrillRefDes() );
+ ref = components.insert( std::pair< std::string, IDF3_COMPONENT*> ( comp->GetRefDes(), comp ) ).first;
+ }
+
+ IDF_DRILL_DATA* dp = ref->second->AddDrill( aDrilledHole );
+
+ if( !dp )
+ {
+ errormsg = ref->second->GetError();
+ return NULL;
+ }
+
+ return dp;
+}
+
+
+bool IDF3_BOARD::delCompDrill( double aDia, double aXpos, double aYpos, std::string aRefDes )
+{
+ errormsg.clear();
+
+ std::map<std::string, IDF3_COMPONENT*>::iterator ref = components.find( aRefDes );
+
+ if( ref == components.end() )
+ return false;
+
+ if( !ref->second->DelDrill( aDia, aXpos, aYpos ) )
+ {
+ errormsg = ref->second->GetError();
+ return false;
+ }
+
+ return true;
+}
+
+
+bool IDF3_BOARD::AddComponent( IDF3_COMPONENT* aComponent )
+{
+ if( !aComponent )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__;
+ ostr << "(): Invalid component pointer (NULL)";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ if( components.insert( std::pair<std::string, IDF3_COMPONENT*>
+ ( aComponent->GetRefDes(), aComponent ) ).second == false )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
+ ostr << "* duplicate RefDes ('" << aComponent->GetRefDes() << "')";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ return true;
+}
+
+
+bool IDF3_BOARD::DelComponent( IDF3_COMPONENT* aComponent )
+{
+ errormsg.clear();
+
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkComponentOwnership( __LINE__, __FUNCTION__, aComponent ) )
+ return false;
+#endif
+
+ std::map<std::string, IDF3_COMPONENT*>::iterator it =
+ components.find( aComponent->GetRefDes() );
+
+ if( it == components.end() )
+ return false;
+
+ delete it->second;
+ components.erase( it );
+
+ return true;
+}
+
+
+bool IDF3_BOARD::DelComponent( size_t aIndex )
+{
+ if( aIndex >= components.size() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
+ ostr << "* aIndex (" << aIndex << ") out of range (" << components.size() << ")";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ std::map<std::string, IDF3_COMPONENT*>::iterator it = components.begin();
+
+ while( aIndex-- > 0 ) ++it;
+
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkComponentOwnership( __LINE__, __FUNCTION__, it->second ) )
+ return false;
+#endif
+
+ delete it->second;
+ components.erase( it );
+
+ return true;
+}
+
+
+size_t IDF3_BOARD::GetComponentsSize( void )
+{
+ return components.size();
+}
+
+
+std::map< std::string, IDF3_COMPONENT* >*const IDF3_BOARD::GetComponents( void )
+{
+ return &components;
+}
+
+
+IDF3_COMPONENT* IDF3_BOARD::FindComponent( std::string aRefDes )
+{
+ std::map<std::string, IDF3_COMPONENT*>::iterator it = components.find( aRefDes );
+
+ if( it == components.end() )
+ return NULL;
+
+ return it->second;
+}
+
+
+// returns a pointer to a component outline object or NULL
+// if the object doesn't exist
+IDF3_COMP_OUTLINE* IDF3_BOARD::GetComponentOutline( wxString aFullFileName )
+{
+ std::string fname = TO_UTF8( aFullFileName );
+ wxFileName idflib( aFullFileName );
+
+ if( !idflib.IsOk() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
+ cerr << "* invalid file name: '" << fname << "'";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ if( !idflib.FileExists() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
+ cerr << "* no such file: '" << fname << "'";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ if( !idflib.IsFileReadable() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
+ cerr << "* cannot read file: '" << fname << "'";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ std::map< std::string, std::string >::iterator itm = uidFileList.find( fname );
+
+ if( itm != uidFileList.end() )
+ return GetComponentOutline( itm->second );
+
+ IDF3_COMP_OUTLINE* cp = new IDF3_COMP_OUTLINE( this );
+
+ if( cp == NULL )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
+ cerr << "* failed to create outline\n";
+ cerr << "* filename: '" << fname << "'";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ std::ifstream model;
+ model.exceptions ( std::ifstream::badbit );
+
+ try
+ {
+ model.open( fname.c_str(), std::ios_base::in | std::ios_base::binary );
+
+
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+
+
+ while( true )
+ {
+ while( !FetchIDFLine( model, iline, isComment, pos ) && model.good() );
+
+ if( !model.good() )
+ {
+ ostringstream ostr;
+ ostr << "\n* problems reading file: '" << fname << "'";
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ // accept comment lines, .ELECTRICAL, or .MECHANICAL only
+ if( isComment )
+ {
+ cp->AddComment( iline );
+ continue;
+ }
+
+ if( CompareToken( ".ELECTRICAL", iline ) || CompareToken( ".MECHANICAL", iline ) )
+ {
+ cp->readData( model, iline, idfVer );
+ break;
+ }
+ else
+ {
+ ostringstream ostr;
+ ostr << "faulty IDF component definition\n";
+ ostr << "* Expecting .ELECTRICAL or .MECHANICAL, got '" << iline << "'\n";
+ cerr << "* File: '" << fname << "'\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ } // while( true )
+ }
+ catch( const std::exception& e )
+ {
+ delete cp;
+
+ model.exceptions ( std::ios_base::goodbit );
+
+ if( model.is_open() )
+ model.close();
+
+ errormsg = e.what();
+
+ return NULL;
+ }
+
+ model.close();
+
+ // check the unique ID against the list from library components
+ std::list< std::string >::iterator lsts = uidLibList.begin();
+ std::list< std::string >::iterator lste = uidLibList.end();
+ std::string uid = cp->GetUID();
+ IDF3_COMP_OUTLINE* oldp = NULL;
+
+ while( lsts != lste )
+ {
+ if( ! lsts->compare( uid ) )
+ {
+ oldp = GetComponentOutline( uid );
+
+ if( MatchCompOutline( cp, oldp ) )
+ {
+ // everything is fine; the outlines are genuine duplicates; delete the copy
+ delete cp;
+ // make sure we can find the item via its filename
+ uidFileList.insert( std::pair< std::string, std::string>( fname, uid ) );
+ // return the pointer to the original
+ return oldp;
+ }
+ else
+ {
+ delete cp;
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* duplicate UID for different Component Outlines: '" << uid << "'\n";
+ ostr << "* original loaded from library, duplicate in current file\n";
+ ostr << "* file: '" << fname << "'";
+
+ errormsg = ostr.str();
+ return NULL;
+ }
+ }
+
+ ++lsts;
+ }
+
+ // if we got this far then any duplicates are from files previously read
+ oldp = GetComponentOutline( uid );
+
+ if( oldp == NULL )
+ {
+ // everything is fine, there are no existing entries
+ uidFileList.insert( std::pair< std::string, std::string>( fname, uid ) );
+ compOutlines.insert( pair<const std::string, IDF3_COMP_OUTLINE*>( uid, cp ) );
+
+ return cp;
+ }
+
+ if( MatchCompOutline( cp, oldp ) )
+ {
+ // everything is fine; the outlines are genuine duplicates; delete the copy
+ delete cp;
+ // make sure we can find the item via its other filename
+ uidFileList.insert( std::pair< std::string, std::string>( fname, uid ) );
+ // return the pointer to the original
+ return oldp;
+ }
+
+ delete cp;
+
+ // determine the file name of the first instance
+ std::map< std::string, std::string >::iterator ufls = uidFileList.begin();
+ std::map< std::string, std::string >::iterator ufle = uidFileList.end();
+ std::string oldfname;
+
+ while( ufls != ufle )
+ {
+ if( ! ufls->second.compare( uid ) )
+ {
+ oldfname = ufls->first;
+ break;
+ }
+
+ ++ufls;
+ }
+
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* duplicate UID for different Component Outlines: '" << uid << "'\n";
+ ostr << "* original file: '" << oldfname << "'\n";
+ ostr << "* this file: '" << fname << "'";
+
+ errormsg = ostr.str();
+ return NULL;
+}
+
+
+// returns a pointer to the component outline object with the
+// unique ID aComponentID
+IDF3_COMP_OUTLINE* IDF3_BOARD::GetComponentOutline( std::string aComponentID )
+{
+ std::map< std::string, IDF3_COMP_OUTLINE*>::iterator its = compOutlines.find( aComponentID );
+
+ if( its != compOutlines.end() )
+ return its->second;
+
+ return NULL;
+}
+
+
+// returns a pointer to the outline which is substituted
+// whenever a true outline cannot be found or is defective
+IDF3_COMP_OUTLINE* IDF3_BOARD::GetInvalidOutline( const std::string& aGeomName, const std::string& aPartName )
+{
+ std::string uid;
+ bool empty = false;
+
+ if( aGeomName.empty() && aPartName.empty() )
+ {
+ uid = "NOGEOM_NOPART";
+ empty = true;
+ }
+ else
+ {
+ uid = aGeomName + "_" + aPartName;
+ }
+
+ IDF3_COMP_OUTLINE* cp = GetComponentOutline( uid );
+
+ if( cp != NULL )
+ return cp;
+
+ cp = new IDF3_COMP_OUTLINE( this );
+
+ if( cp == NULL )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): ";
+ cerr << "could not create new outline";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ if( empty )
+ cp->CreateDefaultOutline( "", "" );
+ else
+ cp->CreateDefaultOutline( aGeomName, aPartName );
+
+ compOutlines.insert( pair<const std::string, IDF3_COMP_OUTLINE*>(cp->GetUID(), cp) );
+
+ return cp;
+}
+
+
+// clears all data
+void IDF3_BOARD::Clear( void )
+{
+ // preserve the board thickness
+ double thickness = olnBoard.GetThickness();
+
+ idfSource.clear();
+ brdSource.clear();
+ libSource.clear();
+ brdDate.clear();
+ libDate.clear();
+ uidFileList.clear();
+ uidLibList.clear();
+ brdFileVersion = 0;
+ libFileVersion = 0;
+ iRefDes = 0;
+ sRefDes.clear();
+
+ // delete comment lists
+ noteComments.clear();
+ drillComments.clear();
+ placeComments.clear();
+
+ // delete notes
+ while( !notes.empty() )
+ {
+ delete notes.front();
+ notes.pop_front();
+ }
+
+ // delete drill list
+ do
+ {
+ std::list<IDF_DRILL_DATA*>::iterator ds = board_drills.begin();
+ std::list<IDF_DRILL_DATA*>::iterator de = board_drills.end();
+
+ while( ds != de )
+ {
+ delete *ds;
+ ++ds;
+ }
+
+ board_drills.clear();
+ } while(0);
+
+
+ // delete components
+ do
+ {
+ std::map<std::string, IDF3_COMPONENT*>::iterator cs = components.begin();
+ std::map<std::string, IDF3_COMPONENT*>::iterator ce = components.end();
+
+ while( cs != ce )
+ {
+ delete cs->second;
+ ++cs;
+ }
+
+ components.clear();
+ } while(0);
+
+
+ // delete component outlines
+ do
+ {
+ std::map<std::string, IDF3_COMP_OUTLINE*>::iterator cs = compOutlines.begin();
+ std::map<std::string, IDF3_COMP_OUTLINE*>::iterator ce = compOutlines.end();
+
+ while( cs != ce )
+ {
+ delete cs->second;
+ ++cs;
+ }
+
+ compOutlines.clear();
+ } while(0);
+
+
+ // delete OTHER outlines
+ do
+ {
+ std::map<std::string, OTHER_OUTLINE*>::iterator os = olnOther.begin();
+ std::map<std::string, OTHER_OUTLINE*>::iterator oe = olnOther.end();
+
+ while( os != oe )
+ {
+ delete os->second;
+ ++os;
+ }
+
+ olnOther.clear();
+ } while(0);
+
+
+ // delete ROUTE outlines
+ do
+ {
+ std::list<ROUTE_OUTLINE*>::iterator os = olnRoute.begin();
+ std::list<ROUTE_OUTLINE*>::iterator oe = olnRoute.end();
+
+ while( os != oe )
+ {
+ delete *os;
+ ++os;
+ }
+
+ olnRoute.clear();
+ } while(0);
+
+
+ // delete PLACE outlines
+ do
+ {
+ std::list<PLACE_OUTLINE*>::iterator os = olnPlace.begin();
+ std::list<PLACE_OUTLINE*>::iterator oe = olnPlace.end();
+
+ while( os != oe )
+ {
+ delete *os;
+ ++os;
+ }
+
+ olnPlace.clear();
+ } while(0);
+
+
+ // delete ROUTE KEEPOUT outlines
+ do
+ {
+ std::list<ROUTE_KO_OUTLINE*>::iterator os = olnRouteKeepout.begin();
+ std::list<ROUTE_KO_OUTLINE*>::iterator oe = olnRouteKeepout.end();
+
+ while( os != oe )
+ {
+ delete *os;
+ ++os;
+ }
+
+ olnRouteKeepout.clear();
+ } while(0);
+
+
+ // delete VIA KEEPOUT outlines
+ do
+ {
+ std::list<VIA_KO_OUTLINE*>::iterator os = olnViaKeepout.begin();
+ std::list<VIA_KO_OUTLINE*>::iterator oe = olnViaKeepout.end();
+
+ while( os != oe )
+ {
+ delete *os;
+ ++os;
+ }
+
+ olnViaKeepout.clear();
+ } while(0);
+
+
+ // delete PLACEMENT KEEPOUT outlines
+ do
+ {
+ std::list<PLACE_KO_OUTLINE*>::iterator os = olnPlaceKeepout.begin();
+ std::list<PLACE_KO_OUTLINE*>::iterator oe = olnPlaceKeepout.end();
+
+ while( os != oe )
+ {
+ delete *os;
+ ++os;
+ }
+
+ olnPlaceKeepout.clear();
+ } while(0);
+
+
+ // delete PLACEMENT GROUP outlines
+ do
+ {
+ std::multimap<std::string, GROUP_OUTLINE*>::iterator os = olnGroup.begin();
+ std::multimap<std::string, GROUP_OUTLINE*>::iterator oe = olnGroup.end();
+
+ while( os != oe )
+ {
+ delete os->second;
+ ++os;
+ }
+
+ olnGroup.clear();
+ } while(0);
+
+ boardName.clear();
+ olnBoard.setThickness( thickness );
+
+ state = FILE_START;
+ unit = UNIT_MM;
+ userScale = 1.0;
+ userXoff = 0.0;
+ userYoff = 0.0;
+
+ return;
+}
+
+
+const std::map<std::string, OTHER_OUTLINE*>*const
+IDF3_BOARD::GetOtherOutlines( void )
+{
+ return &olnOther;
+}
diff --git a/utils/idftools/idf_parser.h b/utils/idftools/idf_parser.h
new file mode 100644
index 0000000..e666e17
--- /dev/null
+++ b/utils/idftools/idf_parser.h
@@ -0,0 +1,721 @@
+/*
+ * 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
+ */
+
+/*
+ * NOTE:
+ *
+ * Rules to ensure friendly use within a DLL:
+ *
+ * 1. all functions which throw exceptions must not be publicly available;
+ * they must become FRIEND functions instead.
+ *
+ * 2. All objects with PRIVATE functions which throw exceptions when
+ * invoked by a PUBLIC function must indicate success or failure
+ * and make the exception information available via a GetError()
+ * routine.
+ *
+ * General notes:
+ *
+ * 1. Due to the complexity of objects and the risk of accumulated
+ * position errors, CAD packages should only delete or add complete
+ * components. If a component being added already exists, it is
+ * replaced by the new component IF and only if the CAD type is
+ * permitted to make such changes.
+ *
+ * 2. Internally all units shall be in mm and by default we shall
+ * write files with mm units. The internal flags mm/thou shall only
+ * be used to translate data being read from or written to files.
+ * This avoids the painful management of a mixture of mm and thou.
+ * The API shall require all dimensions in mm; for people using any
+ * other unit, it is their responsibility to perform the conversion
+ * to mm. Conversion back to thou may incur small rounding errors.
+ */
+
+
+#ifndef IDF_PARSER_H
+#define IDF_PARSER_H
+
+#include <idf_outlines.h>
+
+class IDF3_COMPONENT;
+
+class IDF3_COMP_OUTLINE_DATA
+{
+friend class IDF3_BOARD;
+friend class IDF3_COMPONENT;
+private:
+ double xoff; // X offset from KiCad or X placement from IDF file
+ double yoff; // Y offset from KiCad or Y placement from IDF file
+ double zoff; // height offset (specified in IDFv3 spec, corresponds to KiCad Z offset)
+ double aoff; // angular offset from KiCad or Rotation Angle from IDF file
+ std::string errormsg;
+
+ IDF3_COMP_OUTLINE* outline; // component outline to use
+ IDF3_COMPONENT* parent; // associated component
+
+#ifndef DISABLE_IDF_OWNERSHIP
+ bool checkOwnership( int aSourceLine, const char* aSourceFunc );
+#endif
+
+ /**
+ * Function readPlaceData
+ * reads placement data from an open IDFv3 file
+ *
+ * @param aBoardFile is the open IDFv3 file
+ * @param aBoardState is the internal status flag of the IDF parser
+ * @param aIdfVersion is the version of the file currently being parsed
+ * @param aBoard is the IDF3_BOARD object which will store the data
+ *
+ * @return bool: true if placement data was successfully read. false if
+ * no placement data was read; this may happen if the end of the placement
+ * data was encountered or an error occurred. if an error occurred then
+ * an exception is thrown.
+ */
+ bool readPlaceData( std::ifstream &aBoardFile, IDF3::FILE_STATE& aBoardState,
+ IDF3_BOARD *aBoard, IDF3::IDF_VERSION aIdfVersion,
+ bool aNoSubstituteOutlines );
+
+ /**
+ * Function writePlaceData
+ * writes RECORD 2 and RECORD 3 of a PLACEMENT section as per IDFv3 specification
+ *
+ * @param aBoardFile is the open IDFv3 file
+ * @param aXpos is the X location of the parent component
+ * @param aYpos is the Y location of the parent component
+ * @param aAngle is the rotation of the parent component
+ * @param aRefDes is the reference designator of the parent component
+ * @param aPlacement is the IDF Placement Status of the parent component
+ * @param aSide is the IDF Layer Designator (TOP or BOTTOM)
+ *
+ * @return bool: true if data was successfully written, otherwise false
+ */
+ void writePlaceData( std::ofstream& aBoardFile, double aXpos, double aYpos, double aAngle,
+ const std::string aRefDes, IDF3::IDF_PLACEMENT aPlacement,
+ IDF3::IDF_LAYER aSide );
+
+public:
+ /**
+ * Constructor
+ * creates an object with default settings and no parent or associated outline
+ */
+ IDF3_COMP_OUTLINE_DATA();
+
+ /**
+ * Constructor
+ * creates an object with default settings and the specified parent and associated outline
+ *
+ * @param aParent is the owning IDF3_COMPONENT object
+ * @param aOutline is the outline for this placed component
+ */
+ IDF3_COMP_OUTLINE_DATA( IDF3_COMPONENT* aParent, IDF3_COMP_OUTLINE* aOutline );
+
+ /**
+ * Constructor
+ * creates an object the specified parent and associated outline and the specified
+ * data.
+ *
+ * @param aParent is the owning IDF3_COMPONENT object
+ * @param aOutline is the outline for this placed component
+ * @param aXoff is the X offset of this outline in relation to its parent
+ * @param aYoff is the Y offset of this outline in relation to its parent
+ * @param aZoff is the board offset of this outline as per IDFv3 specification
+ * @param aAoff is the rotational offset of this outline in relation to its parent
+ */
+ IDF3_COMP_OUTLINE_DATA( IDF3_COMPONENT* aParent, IDF3_COMP_OUTLINE* aOutline,
+ double aXoff, double aYoff, double aZoff, double aAngleOff );
+
+ ~IDF3_COMP_OUTLINE_DATA();
+
+ /**
+ * Function SetOffsets
+ * sets the position and orientation of this outline item in relation to its parent
+ *
+ * @param aXoff is the X offset of this outline in relation to its parent
+ * @param aYoff is the Y offset of this outline in relation to its parent
+ * @param aZoff is the board offset of this outline as per IDFv3 specification
+ * @param aAoff is the rotational offset of this outline in relation to its parent
+ *
+ * @return bool: true if the operation succeeded, false if an ownership
+ * violation occurred
+ */
+ bool SetOffsets( double aXoff, double aYoff, double aZoff, double aAngleOff );
+
+ /**
+ * Function GetOffsets
+ * retrieves the position and orientation of this outline item in relation to its parent
+ *
+ * @param aXoff is the X offset of this outline in relation to its parent
+ * @param aYoff is the Y offset of this outline in relation to its parent
+ * @param aZoff is the board offset of this outline as per IDFv3 specification
+ * @param aAoff is the rotational offset of this outline in relation to its parent
+ */
+ void GetOffsets( double& aXoff, double& aYoff, double& aZoff, double& aAngleOff );
+
+ /**
+ * Function SetParent
+ * sets the parent object
+ *
+ * @param aParent is the owning IDF3_COMPONENT object
+ */
+ void SetParent( IDF3_COMPONENT* aParent );
+
+ /**
+ * Function SetOutline
+ * sets the outline whose position is managed by this object
+ *
+ * @param aOutline is the outline for this component
+ *
+ * @return bool: true if the operation succeeded, false if an ownership
+ * violation occurred
+ */
+ bool SetOutline( IDF3_COMP_OUTLINE* aOutline );
+
+ /**
+ * Function GetOutline
+ * retrieves the outline whose position is managed by this object
+ *
+ * @return IDF3_COMP_OUTLINE*: the outline for this component
+ */
+ IDF3_COMP_OUTLINE* GetOutline( void )
+ {
+ return outline;
+ }
+
+ const std::string& GetError( void )
+ {
+ return errormsg;
+ }
+};
+
+
+class IDF3_COMPONENT
+{
+friend class IDF3_BOARD;
+private:
+ std::list< IDF3_COMP_OUTLINE_DATA* > components;
+ std::list< IDF_DRILL_DATA* > drills;
+
+ double xpos;
+ double ypos;
+ double angle;
+ IDF3::IDF_PLACEMENT placement;
+ IDF3::IDF_LAYER layer; // [TOP/BOTTOM ONLY as per IDF spec]
+ bool hasPosition; ///< True after SetPosition is called once
+ std::string refdes; ///< Reference Description (MUST BE UNIQUE)
+ IDF3_BOARD* parent;
+ std::string errormsg;
+
+ /**
+ * Function WriteDrillData
+ * writes the internal drill data to an IDFv3 .DRILLED_HOLES section
+ *
+ * @param aBoardFile is an IDFv3 file opened for writing
+ *
+ * @return bool: true if the operation succeeded, otherwise false
+ */
+ bool writeDrillData( std::ofstream& aBoardFile );
+
+ /**
+ * Function WritePlaceData
+ * writes the component placement data to an IDFv3 .PLACEMENT section
+ *
+ * @param aBoardFile is an IDFv3 file opened for writing
+ *
+ * @return bool: true if the operation succeeded, otherwise false
+ */
+ bool writePlaceData( std::ofstream& aBoardFile );
+
+#ifndef DISABLE_IDF_OWNERSHIP
+ bool checkOwnership( int aSourceLine, const char* aSourceFunc );
+#endif
+
+public:
+ /**
+ * Constructor
+ * sets the parent object and initializes other internal parameters to default values
+ *
+ * @param aParent is the owning IDF3_BOARD object
+ */
+ IDF3_COMPONENT( IDF3_BOARD* aParent );
+
+ ~IDF3_COMPONENT();
+
+ /**
+ * Function SetParent
+ * sets the parent object
+ *
+ * @param aParent is the owning IDF3_BOARD object
+ */
+ void SetParent( IDF3_BOARD* aParent );
+
+ /**
+ * Function GetCadType
+ * returns the type of CAD (IDF3::CAD_ELEC, IDF3::CAD_MECH) which instantiated this object
+ *
+ * @return IDF3::CAD_TYPE
+ */
+ IDF3::CAD_TYPE GetCadType( void );
+
+ /**
+ * Function GetCadType
+ * returns the IDF UNIT type of the parent object or IDF3::UNIT_INVALID if
+ * the parent was not set
+ *
+ * @return IDF3::IDF_UNIT
+ */
+ IDF3::IDF_UNIT GetUnit( void );
+
+ /**
+ * Function SetRefDes
+ * sets the Reference Designator (RefDes) of this component; the RefDes is shared
+ * by all outlines associated with this component.
+ *
+ * @return bool: true if the RefDes was accepted, otherwise false. Prohibited
+ * values include empty strings and the word PANEL.
+ */
+ bool SetRefDes( const std::string& aRefDes );
+
+ /**
+ * Function GetRefDes
+ * Retrieves the Reference Designator (RefDes) of this component
+ *
+ * @return string: the Reference Designator
+ */
+ const std::string& GetRefDes( void );
+
+ /**
+ * Function AddDrill
+ * adds a drill entry to the component and returns its pointer
+ *
+ * @param aDia diameter of the drill (mm)
+ * @param aXpos X position of the drill (mm)
+ * @param aYpos Y position of the drill (mm)
+ * @param aPlating plating type (PTH, NPTH)
+ * @param aHoleType hole class (PIN, VIA, MTG, TOOL, etc)
+ * @param aOwner owning CAD system (ECAD, MCAD, UNOWNED)
+ *
+ * @return pointer: a pointer to the newly created drill entry or NULL
+ */
+ IDF_DRILL_DATA* AddDrill( double aDia, double aXpos, double aYpos,
+ IDF3::KEY_PLATING aPlating,
+ const std::string aHoleType,
+ IDF3::KEY_OWNER aOwner );
+
+ /**
+ * Function AddDrill
+ * adds the given drill entry to the component and returns the pointer
+ * to indicate success. A return value of NULL indicates that the item
+ * was not added and it is the user's responsibility to delete the
+ * object if necessary.
+ *
+ * @param aDrilledHole pointer to a drill entry
+ *
+ * @return pointer: aDrilledHole if the function succeeds, otherwise NULL
+ */
+ IDF_DRILL_DATA* AddDrill( IDF_DRILL_DATA* aDrilledHole );
+
+ /**
+ * Function DelDrill( double aDia, double aXpos, double aYpos )
+ * deletes a drill entry based on its size and location. This operation is
+ * subject to IDF ownership rules.
+ *
+ * @param aDia diameter (mm) of the drilled hole to be deleted
+ * @param aXpos X position (mm) of the hole to be deleted
+ * @param aYpos X position (mm) of the hole to be deleted
+ *
+ * @return bool: true if a drill was found and deleted, otherwise false.
+ * If an ownership violation occurs an exception is thrown.
+ */
+ bool DelDrill( double aDia, double aXpos, double aYpos );
+
+ /**
+ * Function DelDrill( IDF_DRILL_DATA* aDrill )
+ * deletes a drill entry based on pointer. This operation is
+ * subject to IDF ownership rules.
+ *
+ * @param aDrill the pointer associated with the drill entry to be deleted
+ *
+ * @return bool: true if a drill was found and deleted, otherwise false.
+ * If an ownership violation occurs an exception is thrown.
+ */
+ bool DelDrill( IDF_DRILL_DATA* aDrill );
+
+ /**
+ * Function GetDrills
+ * returns a pointer to the internal list of drills. To avoid IDF
+ * violations, the user should not alter these entries.
+ */
+ const std::list< IDF_DRILL_DATA* >*const GetDrills( void );
+
+ /**
+ * Function AddOutlineData
+ * adds the given component outline data to this component
+ *
+ * @param aComponentOutline is a pointer to the outline data to be added
+ *
+ * @return true if the operation succeedes, otherwise false
+ */
+ bool AddOutlineData( IDF3_COMP_OUTLINE_DATA* aComponentOutline );
+
+ /**
+ * Function DeleteOutlineData( IDF3_COMP_OUTLINE_DATA* aComponentOutline )
+ * removes outline data based on the pointer provided.
+ *
+ * @param aComponentOutline is a pointer to be deleted from the internal list
+ *
+ * @return bool: true if the data was found and deleted, otherwise false
+ */
+ bool DeleteOutlineData( IDF3_COMP_OUTLINE_DATA* aComponentOutline );
+
+ /**
+ * Function DeleteOutlineData( size_t aIndex )
+ * removes outline data based on the provided index.
+ *
+ * @param aIndex is an index to the internal outline list
+ *
+ * @return bool: true if the data was deleted, false if the
+ * index was out of bounds.
+ */
+ bool DeleteOutlineData( size_t aIndex );
+
+ /**
+ * Function GetOutlineSize
+ * returns the number of outlines in the internal list
+ */
+ size_t GetOutlinesSize( void );
+
+
+ /**
+ * Function GetOutlinesData
+ * returns a pointer to the internal list of outline data
+ */
+ const std::list< IDF3_COMP_OUTLINE_DATA* >*const GetOutlinesData( void );
+
+ /**
+ * Function GetPosition
+ * retrieves the internal position parameters and returns true if the
+ * position was previously set, otherwise false.
+ */
+ bool GetPosition( double& aXpos, double& aYpos, double& aAngle, IDF3::IDF_LAYER& aLayer );
+
+ // NOTE: it may be possible to extend this so that internal drills and outlines
+ // are moved when the component is moved. However there is always a danger of
+ // position creep due to the relative position updates.
+ /**
+ * Function SetPosition
+ * sets the internal position parameters and returns true if the
+ * position was set, false if the position was previously set. This object
+ * does not allow modification of the position once it is set since this may
+ * adversely affect the relationship with its internal objects.
+ *
+ * @param aXpos is the X position (mm) of the component
+ * @param aYpos is the Y position (mm) of the component
+ * @param aAngle is the rotation of the component (degrees)
+ * @param aLayer is the layer on which the component is places (TOP, BOTTOM)
+ *
+ * @return bool: true if the position was set, otherwise false
+ */
+ bool SetPosition( double aXpos, double aYpos, double aAngle, IDF3::IDF_LAYER aLayer );
+
+ /**
+ * Function GetPlacement
+ * returns the IDF placement value of this component (UNPLACED, PLACED, ECAD, MCAD)
+ */
+ IDF3::IDF_PLACEMENT GetPlacement( void );
+
+ /**
+ * Function SetPlacement
+ * sets the placement value of the component subject to ownership rules.
+ * An exception is thrown if aPlacementValue is invalid or an ownership
+ * violation occurs.
+ *
+ * @return bool: true if the operation succeeded, otherwise false and the
+ * error message is set.
+ */
+ bool SetPlacement( IDF3::IDF_PLACEMENT aPlacementValue );
+
+ const std::string& GetError( void )
+ {
+ return errormsg;
+ }
+};
+
+class IDF3_BOARD
+{
+private:
+ std::map< std::string, std::string > uidFileList; // map of files opened and UIDs
+ std::list< std::string > uidLibList; // list of UIDs read from a library file
+ std::string errormsg; // string for passing error messages to user
+ std::list< IDF_NOTE* > notes; // IDF notes
+ std::list< std::string > noteComments; // comment list for NOTES section
+ std::list< std::string > drillComments; // comment list for DRILL section
+ std::list< std::string > placeComments; // comment list for PLACEMENT section
+ std::list<IDF_DRILL_DATA*> board_drills;
+ std::map< std::string, IDF3_COMPONENT*> components; // drill and placement data for components
+ std::map< std::string, IDF3_COMP_OUTLINE*> compOutlines; // component outlines (data for library file)
+ std::string boardName;
+ IDF3::FILE_STATE state;
+ IDF3::CAD_TYPE cadType;
+ IDF3::IDF_UNIT unit;
+ IDF3::IDF_VERSION idfVer; // IDF version of Board or Library
+ int iRefDes; // counter for automatically numbered NOREFDES items
+ std::string sRefDes;
+
+ std::string idfSource; // SOURCE string to use when writing BOARD and LIBRARY headers
+ std::string brdSource; // SOURCE string as retrieved from a BOARD file
+ std::string libSource; // SOURCE string as retrieved from a LIBRARY file
+ std::string brdDate; // DATE string from BOARD file
+ std::string libDate; // DATE string from LIBRARY file
+ int brdFileVersion; // File Version from BOARD file
+ int libFileVersion; // File Version from LIBRARY file
+
+ int userPrec; // user may store any integer here
+ double userScale; // user may store a scale for translating to arbitrary units
+ double userXoff; // user may specify an arbitrary X/Y offset
+ double userYoff;
+
+ // main board outline and cutouts
+ BOARD_OUTLINE olnBoard;
+ // OTHER outlines
+ std::map<std::string, OTHER_OUTLINE*> olnOther;
+ // ROUTE outlines
+ std::list<ROUTE_OUTLINE*> olnRoute;
+ // PLACEMENT outlines
+ std::list<PLACE_OUTLINE*> olnPlace;
+ // ROUTE KEEPOUT outlines
+ std::list<ROUTE_KO_OUTLINE*> olnRouteKeepout;
+ // VIA KEEPOUT outlines
+ std::list<VIA_KO_OUTLINE*> olnViaKeepout;
+ // PLACE KEEPOUT outlines
+ std::list<PLACE_KO_OUTLINE*> olnPlaceKeepout;
+ // PLACEMENT GROUP outlines
+ std::multimap<std::string, GROUP_OUTLINE*> olnGroup;
+
+ // Set the unit; this can only be done internally upon
+ // reading a file or saving
+ bool setUnit( IDF3::IDF_UNIT aUnit, bool convert = false );
+
+ IDF_DRILL_DATA* addCompDrill( double aDia, double aXpos, double aYpos,
+ IDF3::KEY_PLATING aPlating,
+ const std::string aHoleType,
+ IDF3::KEY_OWNER aOwner,
+ const std::string& aRefDes );
+
+ IDF_DRILL_DATA* addCompDrill( IDF_DRILL_DATA* aDrilledHole );
+
+ bool delCompDrill( double aDia, double aXpos, double aYpos, std::string aRefDes );
+
+ // read the DRILLED HOLES section
+ void readBrdDrills( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState );
+ // read the NOTES section
+ void readBrdNotes( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState );
+ // read the component placement section
+ void readBrdPlacement( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState,
+ bool aNoSubstituteOutlines );
+ // read the board HEADER
+ void readBrdHeader( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState );
+ // read individual board sections; pay attention to IDFv3 section specifications
+ // exception thrown on unrecoverable errors. state flag set to FILE_PLACEMENT
+ // upon reading the PLACEMENT file; according to IDFv3 this is the final section
+ void readBrdSection( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState,
+ bool aNoSubstituteOutlines );
+ // read the board file data
+ void readBoardFile( const std::string& aFileName, bool aNoSubstituteOutlines );
+
+ // write the board file data
+ void writeBoardFile( const std::string& aFileName );
+
+ // read the library sections (outlines)
+ void readLibSection( std::ifstream& aLibFile, IDF3::FILE_STATE& aLibState, IDF3_BOARD* aBoard );
+ // read the library HEADER
+ void readLibHeader( std::ifstream& aLibFile, IDF3::FILE_STATE& aLibState );
+ // read the library file data
+ void readLibFile( const std::string& aFileName );
+
+ // write the library file data
+ bool writeLibFile( const std::string& aFileName );
+
+#ifndef DISABLE_IDF_OWNERSHIP
+ bool checkComponentOwnership( int aSourceLine, const char* aSourceFunc,
+ IDF3_COMPONENT* aComponent );
+#endif
+
+public:
+ IDF3_BOARD( IDF3::CAD_TYPE aCadType );
+ virtual ~IDF3_BOARD();
+
+ IDF3::CAD_TYPE GetCadType( void );
+
+ // retrieve the nominal unit used in reading/writing
+ // data. This is primarily for use by owned objects
+ // and is only of informational use for the end user.
+ // Internally all data is represented in mm and the
+ // end user must use only mm in the API.
+ IDF3::IDF_UNIT GetUnit( void );
+
+ const std::string& GetNewRefDes( void );
+
+ void SetBoardName( std::string aBoardName );
+ const std::string& GetBoardName( void );
+
+ bool SetBoardThickness( double aBoardThickness );
+ double GetBoardThickness( void );
+
+ bool ReadFile( const wxString& aFullFileName, bool aNoSubstituteOutlines = false );
+ bool WriteFile( const wxString& aFullFileName, bool aUnitMM = true, bool aForceUnitFlag = false );
+
+ const std::string& GetIDFSource( void );
+ void SetIDFSource( const std::string& aIDFSource);
+ const std::string& GetBoardSource( void );
+ const std::string& GetLibrarySource( void );
+ const std::string& GetBoardDate( void );
+ const std::string& GetLibraryDate( void );
+ int GetBoardVersion( void );
+ bool SetBoardVersion( int aVersion );
+ int GetLibraryVersion( void );
+ bool SetLibraryVersion( int aVersion );
+
+ double GetUserScale( void );
+ bool SetUserScale( double aScaleFactor );
+
+ int GetUserPrecision( void );
+ bool SetUserPrecision( int aPrecision );
+
+ void GetUserOffset( double& aXoff, double& aYoff );
+ void SetUserOffset( double aXoff, double aYoff );
+
+ bool AddBoardOutline( IDF_OUTLINE* aOutline );
+ bool DelBoardOutline( IDF_OUTLINE* aOutline );
+ bool DelBoardOutline( size_t aIndex );
+ size_t GetBoardOutlinesSize( void );
+ BOARD_OUTLINE* GetBoardOutline( void );
+ const std::list< IDF_OUTLINE* >*const GetBoardOutlines( void );
+
+ // Operations for OTHER OUTLINES
+ const std::map<std::string, OTHER_OUTLINE*>*const GetOtherOutlines( void );
+
+ /// XXX - TO BE IMPLEMENTED
+ //
+ // SetBoardOutlineOwner()
+ //
+ // AddDrillComment
+ // AddPlacementComment
+ // GetDrillComments()
+ // GetPlacementComments()
+ // ClearDrillComments()
+ // ClearPlacementComments()
+ // AddNoteComment
+ // GetNoteComments
+ // AddNote
+ //
+ // [IMPLEMENTED] const std::map<std::string, OTHER_OUTLINE*>*const GetOtherOutlines( void )
+ // size_t GetOtherOutlinesSize()
+ // OTHER_OUTLINE* AddOtherOutline( OTHER_OUTLINE* aOtherOutline )
+ // bool DelOtherOutline( int aIndex )
+ // bool DelOtherOutline( OTHER_OUTLINE* aOtherOutline )
+ //
+ // const std::list<ROUTE_OUTLINE*>*const GetRouteOutlines()
+ // size_t GetRouteOutlinesSize()
+ // ROUTE_OUTLINE* AddRouteOutline( ROUTE_OUTLINE* aRouteOutline )
+ // bool DelRouteOutline( int aIndex )
+ // bool DelRouteOutline( ROUTE_OUTLINE* aRouteOutline )
+ //
+ // const std::list<PLACE_OUTLINE*>*const GetPlacementOutlines()
+ // size_t GetPlacementOutlinesSize()
+ // PLACE_OUTLINE* AddPlacementOutline( PLACE_OUTLINE* aPlaceOutline )
+ // bool DelPlacementOutline( int aIndex )
+ // bool DelPlacementOutline( PLACE_OUTLINE* aPlaceOutline )
+ //
+ // const std::list<ROUTE_KO_OUTLINE*>*const GetRouteKeepOutOutlines()
+ // size_t GetRouteKeepOutOutlinesSize()
+ // ROUTE_KO_OUTLINE* AddRouteKeepoutOutline( ROUTE_KO_OUTLINE* aRouteKeepOut )
+ // bool DelRouteKeepOutOutline( int aIndex )
+ // bool DelRouteKeepOutOutline( ROUTE_KO_OUTLINE* aRouteKeepOut )
+ //
+ // const std::list<VIA_KO_OUTLINE*>*const GetViaKeepOutOutlines()
+ // size_t GetViaKeepOutOutlinesSize()
+ // VIA_KO_OUTLINE* AddViaKeepoutOutline( VIA_KO_OUTLINE* aViaKeepOut )
+ // bool DelViaKeepOutOutline( int aIndex )
+ // bool DelViaKeepOutOutline( VIA_KO_OUTLINE* aViaKeepOut )
+ //
+ // const std::list<PLACE_KO_OUTLINE*>*const GetPlacementKeepOutOutlines()
+ // size_t GetPlacementKeepOutOutlinesSize()
+ // PLACE_KO_OUTLINE* AddPlacementKeepoutOutline( PLACE_KO_OUTLINE* aPlaceKeepOut )
+ // bool DelPlacementKeepOutOutline( int aIndex )
+ // bool DelPlacementKeepOutOutline( PLACE_KO_OUTLINE* aPlaceKeepOut )
+ //
+ // const std::multimap<std::string, GROUP_OUTLINE*>*const GetGroupOutlines()
+ // size_t GetGroupOutlinesSize()
+ // GROUP_OUTLINE* AddGroupOutline( GROUP_OUTLINE* aGroupOutline )
+ // bool DelGroupOutline( int aIndex )
+ // bool DelGroupOutline( GROUP_OUTLINE* aGroupOutline )
+
+ std::list<IDF_DRILL_DATA*>& GetBoardDrills( void )
+ {
+ return board_drills;
+ }
+
+ IDF_DRILL_DATA* AddBoardDrill( double aDia, double aXpos, double aYpos,
+ IDF3::KEY_PLATING aPlating,
+ const std::string aHoleType,
+ IDF3::KEY_OWNER aOwner );
+
+ IDF_DRILL_DATA* AddDrill( IDF_DRILL_DATA* aDrilledHole );
+
+ bool DelBoardDrill( double aDia, double aXpos, double aYpos );
+
+ // a slot is a deficient representation of a kicad slotted hole;
+ // it is usually associated with a component but IDFv3 does not
+ // provide for such an association.
+ bool AddSlot( double aWidth, double aLength, double aOrientation, double aX, double aY );
+
+ bool AddComponent( IDF3_COMPONENT* aComponent );
+ bool DelComponent( IDF3_COMPONENT* aComponent );
+ bool DelComponent( size_t aIndex );
+ size_t GetComponentsSize( void );
+ std::map< std::string, IDF3_COMPONENT* >*const GetComponents( void );
+ IDF3_COMPONENT* FindComponent( std::string aRefDes );
+
+ // returns a pointer to a component outline object or NULL
+ // if the object doesn't exist
+ IDF3_COMP_OUTLINE* GetComponentOutline( wxString aFullFileName );
+
+ // returns a pointer to the component outline object with the
+ // unique ID aComponentID
+ IDF3_COMP_OUTLINE* GetComponentOutline( std::string aComponentID );
+
+ // returns a pointer to the outline "NOGEOM NOPART" which is substituted
+ // whenever a true outline cannot be found or is defective
+ IDF3_COMP_OUTLINE* GetInvalidOutline( const std::string& aGeomName, const std::string& aPartName );
+
+ // clears all data
+ void Clear( void );
+
+ // return error string
+ const std::string& GetError( void )
+ {
+ return errormsg;
+ }
+};
+
+#endif // IDF_PARSER_H
diff --git a/utils/idftools/idf_rect.cpp b/utils/idftools/idf_rect.cpp
new file mode 100644
index 0000000..17b145b
--- /dev/null
+++ b/utils/idftools/idf_rect.cpp
@@ -0,0 +1,433 @@
+/*
+ * 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
+ */
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <sstream>
+#include <cmath>
+#include <cstdio>
+#include <list>
+#include <utility>
+#include <clocale>
+
+using namespace std;
+
+void writeLeaded( FILE* fp, double width, double length, double height,
+ double wireDia, double pitch, bool inch );
+
+void writeLeadless( FILE* fp, double width, double length,
+ double height, double chamfer, bool inch );
+
+int main( int argc, char **argv )
+{
+ // IDF implicitly requires the C locale
+ setlocale( LC_ALL, "C" );
+
+ if( argc == 1 )
+ {
+ cout << "idfrect: This program generates an outline for a rectangular component.\n";
+ cout << " The component may have a single lead (axial) or a chamfer on the\n";
+ cout << " upper left corner.\n";
+ cout << "Input:\n";
+ cout << " Unit: mm, in (millimeters or inches)\n";
+ cout << " Width:\n";
+ cout << " Length:\n";
+ cout << " Height:\n";
+ cout << " Chamfer: length of the 45 deg. chamfer\n";
+ cout << " * Leaded: Y,N (lead is always to the right)\n";
+ cout << " ** Wire diameter\n";
+ cout << " ** Pitch\n";
+ cout << " File name (must end in *.idf)\n\n";
+ cout << " NOTES:\n";
+ cout << " * only required if chamfer = 0\n\n";
+ cout << " ** only required for leaded components\n\n";
+ }
+
+ bool inch = false; // default mm
+ double width = 0.0;
+ double length = 0.0;
+ double height = 0.0;
+ double wireDia = 0.0;
+ double pitch = 0.0;
+ double chamfer = 0.0;
+ bool leaded = false;
+ bool ok = false;
+
+ stringstream tstr;
+ string line;
+
+ line.clear();
+ while( line.compare( "mm" ) && line.compare( "in" ) )
+ {
+ cout << "* Units (mm,in): ";
+ line.clear();
+ std::getline( cin, line );
+ }
+
+ if( line.compare( "mm" ) )
+ inch = true;
+
+ ok = false;
+ while( !ok )
+ {
+ cout << "* Width: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> width;
+ if( !tstr.fail() && width >= 0.001 )
+ ok = true;
+ }
+
+ ok = false;
+ while( !ok )
+ {
+ cout << "* Length: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> length;
+ if( !tstr.fail() && length > 0.0 )
+ ok = true;
+ }
+
+ ok = false;
+ while( !ok )
+ {
+ cout << "* Height: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> height;
+ if( !tstr.fail() && height >= 0.001 )
+ ok = true;
+ }
+
+ ok = false;
+ while( !ok )
+ {
+ cout << "* Chamfer (0 for none): ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> chamfer;
+ if( !tstr.fail() && chamfer >= 0.0 )
+ {
+ if( chamfer > width / 3.0 || chamfer > length / 3.0 )
+ cout << "* WARNING: chamfer must be <= MIN( width, length )/3\n";
+ else
+ ok = true;
+ }
+ }
+
+ if( chamfer < 1e-6 )
+ {
+ ok = false;
+ while( !ok )
+ {
+ cout << "* Leaded: Y, N: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ if( !line.compare( "Y" ) || !line.compare( "y" ) )
+ {
+ leaded = true;
+ ok = true;
+ }
+ else if( !line.compare( "N" ) || !line.compare( "n" ) )
+ {
+ leaded = false;
+ ok = true;
+ }
+ }
+ }
+
+ ok = false;
+ while( leaded && !ok )
+ {
+ cout << "* Wire dia.: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> wireDia;
+ if( !tstr.fail() && wireDia >= 0.001 )
+ {
+ if( wireDia >= length )
+ cout << "* WARNING: wire diameter must be < length\n";
+ else
+ ok = true;
+ }
+ }
+
+ ok = false;
+ while( leaded && !ok )
+ {
+ cout << "* Pitch: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> pitch;
+ if( !tstr.fail() && pitch >= 0.001 )
+ {
+ if( pitch <= ( length + wireDia ) / 2.0 )
+ cout << "* WARNING: pitch must be > (length + wireDia)/2\n";
+ else
+ ok = true;
+ }
+ }
+
+ line.clear();
+ while( line.empty() || line.find( ".idf" ) == string::npos )
+ {
+ cout << "* File name (*.idf): ";
+
+ line.clear();
+ std::getline( cin, line );
+ }
+
+ FILE* fp = fopen( line.c_str(), "w" );
+
+ if( !fp )
+ {
+ cerr << "Could not open output file: " << line << "\n";
+ }
+ else
+ {
+ fprintf( fp, "# rectangular outline%s\n", leaded ? ", leaded" : "" );
+ fprintf( fp, "# file: \"%s\"\n", line.c_str() );
+
+ if( inch )
+ {
+ width *= 1000.0;
+ length *= 1000.0;
+ height *= 1000.0;
+ wireDia *= 1000.0;
+ pitch *= 1000.0;
+ chamfer *= 1000.0;
+
+ fprintf( fp, "# width: %d THOU\n", (int) width );
+ fprintf( fp, "# length: %d THOU\n", (int) length );
+ fprintf( fp, "# height: %d THOU\n", (int) height );
+
+ if( leaded )
+ {
+ fprintf( fp, "# wire dia: %d THOU\n", (int) wireDia );
+ fprintf( fp, "# pitch: %d THOU\n", (int) pitch );
+ }
+ else
+ {
+ fprintf( fp, "# chamfer: %d THOU\n", (int) chamfer );
+ }
+
+ fprintf( fp, ".ELECTRICAL\n" );
+ fprintf( fp, "\"RECT%sIN\" \"W%d_L%d_H%d", leaded ? "L" : "",
+ (int) width, (int) length, (int) height );
+
+ if( leaded )
+ fprintf( fp, "_D%d_P%d\" ", (int) wireDia, (int) pitch );
+ else
+ fprintf( fp, "_C%d\" ", (int) chamfer );
+
+ fprintf( fp, "THOU %d\n", (int) height );
+ }
+ else
+ {
+ fprintf( fp, "# width: %.3f mm\n", width );
+ fprintf( fp, "# length: %.3f mm\n", length );
+ fprintf( fp, "# height: %.3f mm\n", height );
+
+ if( leaded )
+ {
+ fprintf( fp, "# wire dia: %.3f mm\n", wireDia );
+ fprintf( fp, "# pitch: %.3f mm\n", pitch );
+ }
+ else
+ {
+ fprintf( fp, "# chamfer: %.3f mm\n", chamfer );
+ }
+
+ fprintf( fp, ".ELECTRICAL\n" );
+ fprintf( fp, "\"RECT%sMM\" \"W%.3f_L%.3f_H%.3f_", leaded ? "L" : "",
+ width, length, height );
+
+ if( leaded )
+ fprintf( fp, "D%.3f_P%.3f\" ", wireDia, pitch );
+ else
+ fprintf( fp, "C%.3f\" ", chamfer );
+
+ fprintf( fp, "MM %.3f\n", height );
+ }
+
+ if( leaded )
+ writeLeaded( fp, width, length, height, wireDia, pitch, inch );
+ else
+ writeLeadless( fp, width, length, height, chamfer, inch );
+
+ fprintf( fp, ".END_ELECTRICAL\n" );
+ fclose( fp );
+ }
+
+ setlocale( LC_ALL, "" );
+ return 0;
+}
+
+
+void writeLeaded( FILE* fp, double width, double length,
+ double height, double wireDia, double pitch, bool inch )
+{
+ if( inch )
+ {
+ int x1, x2, x3;
+ int y1, y2;
+
+ x1 = pitch / 2.0;
+ x2 = width / 2.0 - x1;
+ x3 = x2 - width;
+
+ y1 = wireDia / 2.0;
+ y2 = length / 2.0;
+
+ fprintf( fp, "0 %d %d 0\n", x1, y1 );
+ fprintf( fp, "0 %d %d 0\n", x2, y1 );
+ fprintf( fp, "0 %d %d 0\n", x2, y2 );
+ fprintf( fp, "0 %d %d 0\n", x3, y2 );
+ fprintf( fp, "0 %d %d 0\n", x3, -y2 );
+ fprintf( fp, "0 %d %d 0\n", x2, -y2 );
+ fprintf( fp, "0 %d %d 0\n", x2, -y1 );
+ fprintf( fp, "0 %d %d 0\n", x1, -y1 );
+ fprintf( fp, "0 %d %d 180\n", x1, y1 );
+ }
+ else
+ {
+ double x1, x2, x3;
+ double y1, y2;
+
+ x1 = pitch / 2.0;
+ x2 = width / 2.0 - x1;
+ x3 = x2 - width;
+
+ y1 = wireDia / 2.0;
+ y2 = length / 2.0;
+
+ fprintf( fp, "0 %.3f %.3f 0\n", x1, y1 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x2, y1 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x2, y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x3, y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x3, -y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x2, -y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x2, -y1 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x1, -y1 );
+ fprintf( fp, "0 %.3f %.3f 180\n", x1, y1 );
+ }
+
+ return;
+}
+
+void writeLeadless( FILE* fp, double width, double length,
+ double height, double chamfer, bool inch )
+{
+ if( chamfer < 0.001 )
+ {
+ if( inch )
+ {
+ int x = width / 2.0;
+ int y = length / 2.0;
+
+ fprintf( fp, "0 %d %d 0\n", x, y );
+ fprintf( fp, "0 %d %d 0\n", -x, y );
+ fprintf( fp, "0 %d %d 0\n", -x, -y );
+ fprintf( fp, "0 %d %d 0\n", x, -y );
+ fprintf( fp, "0 %d %d 0\n", x, y );
+ }
+ else
+ {
+ double x = width / 2.0;
+ double y = length / 2.0;
+
+ fprintf( fp, "0 %.3f %.3f 0\n", x, y );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x, y );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x, -y );
+ fprintf( fp, "0 %.3f %.3f 0\n", x, -y );
+ fprintf( fp, "0 %.3f %.3f 0\n", x, y );
+ }
+
+ return;
+ }
+
+ if( inch )
+ {
+ int x = width / 2.0;
+ int y = length / 2.0;
+ int x1 = x - chamfer;
+ int y1 = y - chamfer;
+
+ fprintf( fp, "0 %d %d 0\n", x, y );
+ fprintf( fp, "0 %d %d 0\n", -x1, y );
+ fprintf( fp, "0 %d %d 0\n", -x, y1 );
+ fprintf( fp, "0 %d %d 0\n", -x, -y );
+ fprintf( fp, "0 %d %d 0\n", x, -y );
+ fprintf( fp, "0 %d %d 0\n", x, y );
+ }
+ else
+ {
+ double x = width / 2.0;
+ double y = length / 2.0;
+ double x1 = x - chamfer;
+ double y1 = y - chamfer;
+
+ fprintf( fp, "0 %.3f %.3f 0\n", x, y );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x1, y );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x, y1 );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x, -y );
+ fprintf( fp, "0 %.3f %.3f 0\n", x, -y );
+ fprintf( fp, "0 %.3f %.3f 0\n", x, y );
+ }
+
+ return;
+}
diff --git a/utils/idftools/vrml_layer.cpp b/utils/idftools/vrml_layer.cpp
new file mode 100644
index 0000000..5974371
--- /dev/null
+++ b/utils/idftools/vrml_layer.cpp
@@ -0,0 +1,1788 @@
+/*
+ * file: vrml_layer.cpp
+ *
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 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
+ */
+
+// Wishlist:
+// 1. crop anything outside the board outline on PTH, silk, and copper layers
+// 2. on the PTH layer, handle cropped holes differently from others;
+// these are assumed to be castellated edges and the profile is not
+// a closed loop as assumed for all other outlines.
+// 3. a scheme is needed to tell a castellated edge from a plain board edge
+
+
+#include <sstream>
+#include <string>
+#include <iomanip>
+#include <cmath>
+#include <vrml_layer.h>
+
+#ifndef CALLBACK
+#define CALLBACK
+#endif
+
+#define GLCALLBACK(x) (( void (CALLBACK*)() )&(x))
+
+// minimum sides to a circle
+#define MIN_NSIDES 6
+
+static void FormatDoublet( double x, double y, int precision, std::string& strx, std::string& stry )
+{
+ std::ostringstream ostr;
+
+ ostr << std::fixed << std::setprecision( precision );
+
+ ostr << x;
+ strx = ostr.str();
+
+ ostr.str( "" );
+ ostr << y;
+ stry = ostr.str();
+
+ while( *strx.rbegin() == '0' )
+ strx.erase( strx.size() - 1 );
+
+ while( *stry.rbegin() == '0' )
+ stry.erase( stry.size() - 1 );
+}
+
+
+static void FormatSinglet( double x, int precision, std::string& strx )
+{
+ std::ostringstream ostr;
+
+ ostr << std::fixed << std::setprecision( precision );
+
+ ostr << x;
+ strx = ostr.str();
+
+ while( *strx.rbegin() == '0' )
+ strx.erase( strx.size() - 1 );
+}
+
+
+int VRML_LAYER::calcNSides( double aRadius, double aAngle )
+{
+ // check #segments on ends of arc
+ int maxSeg = maxArcSeg * aAngle / M_PI;
+
+ if( maxSeg < 3 )
+ maxSeg = 3;
+
+ int csides = aRadius * M_PI / minSegLength;
+
+ if( csides < 0 )
+ csides = -csides;
+
+ if( csides > maxSeg )
+ {
+ if( csides < 2 * maxSeg )
+ csides /= 2;
+ else
+ csides = (((double) csides) * minSegLength / maxSegLength );
+ }
+
+ if( csides < 3 )
+ csides = 3;
+
+ if( (csides & 1) == 0 )
+ csides += 1;
+
+ return csides;
+}
+
+
+static void CALLBACK vrml_tess_begin( GLenum cmd, void* user_data )
+{
+ VRML_LAYER* lp = (VRML_LAYER*) user_data;
+
+ lp->glStart( cmd );
+}
+
+
+static void CALLBACK vrml_tess_end( void* user_data )
+{
+ VRML_LAYER* lp = (VRML_LAYER*) user_data;
+
+ lp->glEnd();
+}
+
+
+static void CALLBACK vrml_tess_vertex( void* vertex_data, void* user_data )
+{
+ VRML_LAYER* lp = (VRML_LAYER*) user_data;
+
+ lp->glPushVertex( (VERTEX_3D*) vertex_data );
+}
+
+
+static void CALLBACK vrml_tess_err( GLenum errorID, void* user_data )
+{
+ VRML_LAYER* lp = (VRML_LAYER*) user_data;
+
+ lp->Fault = true;
+ lp->SetGLError( errorID );
+}
+
+
+static void CALLBACK vrml_tess_combine( GLdouble coords[3], VERTEX_3D* vertex_data[4],
+ GLfloat weight[4], void** outData, void* user_data )
+{
+ VRML_LAYER* lp = (VRML_LAYER*) user_data;
+
+ // the plating is set to true only if all are plated
+ bool plated = vertex_data[0]->pth;
+
+ if( !vertex_data[1]->pth )
+ plated = false;
+
+ if( vertex_data[2] && !vertex_data[2]->pth )
+ plated = false;
+
+ if( vertex_data[3] && !vertex_data[3]->pth )
+ plated = false;
+
+ *outData = lp->AddExtraVertex( coords[0], coords[1], plated );
+}
+
+
+VRML_LAYER::VRML_LAYER()
+{
+ // arc parameters suitable to mm measurements
+ maxArcSeg = 48;
+ minSegLength = 0.1;
+ maxSegLength = 0.5;
+ offsetX = 0.0;
+ offsetY = 0.0;
+
+ fix = false;
+ Fault = false;
+ idx = 0;
+ hidx = 0;
+ eidx = 0;
+ ord = 0;
+ glcmd = 0;
+ pholes = NULL;
+
+ tess = gluNewTess();
+
+ if( !tess )
+ return;
+
+ // set up the tesselator callbacks
+ gluTessCallback( tess, GLU_TESS_BEGIN_DATA, GLCALLBACK( vrml_tess_begin ) );
+
+ gluTessCallback( tess, GLU_TESS_VERTEX_DATA, GLCALLBACK( vrml_tess_vertex ) );
+
+ gluTessCallback( tess, GLU_TESS_END_DATA, GLCALLBACK( vrml_tess_end ) );
+
+ gluTessCallback( tess, GLU_TESS_ERROR_DATA, GLCALLBACK( vrml_tess_err ) );
+
+ gluTessCallback( tess, GLU_TESS_COMBINE_DATA, GLCALLBACK( vrml_tess_combine ) );
+
+ gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
+
+ gluTessNormal( tess, 0, 0, 1 );
+}
+
+
+VRML_LAYER::~VRML_LAYER()
+{
+ Clear();
+
+ if( tess )
+ {
+ gluDeleteTess( tess );
+ tess = NULL;
+ }
+}
+
+
+void VRML_LAYER::GetArcParams( int& aMaxSeg, double& aMinLength, double& aMaxLength )
+{
+ aMaxSeg = maxArcSeg;
+ aMinLength = minSegLength;
+ aMaxLength = maxSegLength;
+}
+
+bool VRML_LAYER::SetArcParams( int aMaxSeg, double aMinLength, double aMaxLength )
+{
+ if( aMaxSeg < 8 )
+ aMaxSeg = 8;
+
+ if( aMinLength <= 0 || aMaxLength <= aMinLength )
+ return false;
+
+ maxArcSeg = aMaxSeg;
+ minSegLength = aMinLength;
+ maxSegLength = aMaxLength;
+ return true;
+}
+
+
+// clear all data
+void VRML_LAYER::Clear( void )
+{
+ int i;
+
+ fix = false;
+ idx = 0;
+
+ for( i = contours.size(); i > 0; --i )
+ {
+ delete contours.back();
+ contours.pop_back();
+ }
+
+ pth.clear();
+
+ areas.clear();
+
+ for( i = vertices.size(); i > 0; --i )
+ {
+ delete vertices.back();
+ vertices.pop_back();
+ }
+
+ clearTmp();
+}
+
+
+// clear ephemeral data in between invocations of the tesselation routine
+void VRML_LAYER::clearTmp( void )
+{
+ unsigned int i;
+
+ Fault = false;
+ hidx = 0;
+ eidx = 0;
+ ord = 0;
+ glcmd = 0;
+
+ triplets.clear();
+ solid.clear();
+
+ for( i = outline.size(); i > 0; --i )
+ {
+ delete outline.back();
+ outline.pop_back();
+ }
+
+ ordmap.clear();
+
+ for( i = extra_verts.size(); i > 0; --i )
+ {
+ delete extra_verts.back();
+ extra_verts.pop_back();
+ }
+
+ // note: unlike outline and extra_verts,
+ // vlist is not responsible for memory management
+ vlist.clear();
+
+ // go through the vertex list and reset ephemeral parameters
+ for( i = 0; i < vertices.size(); ++i )
+ {
+ vertices[i]->o = -1;
+ }
+}
+
+
+// create a new contour to be populated; returns an index
+// into the contour list or -1 if there are problems
+int VRML_LAYER::NewContour( bool aPlatedHole )
+{
+ if( fix )
+ return -1;
+
+ std::list<int>* contour = new std::list<int>;
+
+ if( !contour )
+ return -1;
+
+ contours.push_back( contour );
+ areas.push_back( 0.0 );
+
+ pth.push_back( aPlatedHole );
+
+ return contours.size() - 1;
+}
+
+
+// adds a vertex to the existing list and places its index in
+// an existing contour; returns true if OK,
+// false otherwise (indexed contour does not exist)
+bool VRML_LAYER::AddVertex( int aContourID, double aXpos, double aYpos )
+{
+ if( fix )
+ {
+ error = "AddVertex(): no more vertices may be added (Tesselate was previously executed)";
+ return false;
+ }
+
+ if( aContourID < 0 || (unsigned int) aContourID >= contours.size() )
+ {
+ error = "AddVertex(): aContour is not within a valid range";
+ return false;
+ }
+
+ VERTEX_3D* vertex = new VERTEX_3D;
+
+ if( !vertex )
+ {
+ error = "AddVertex(): a new vertex could not be allocated";
+ return false;
+ }
+
+ vertex->x = aXpos;
+ vertex->y = aYpos;
+ vertex->i = idx++;
+ vertex->o = -1;
+ vertex->pth = pth[ aContourID ];
+
+ VERTEX_3D* v2 = NULL;
+
+ if( contours[aContourID]->size() > 0 )
+ v2 = vertices[ contours[aContourID]->back() ];
+
+ vertices.push_back( vertex );
+ contours[aContourID]->push_back( vertex->i );
+
+ if( v2 )
+ areas[aContourID] += ( aXpos - v2->x ) * ( aYpos + v2->y );
+
+ return true;
+}
+
+
+// ensure the winding of a contour with respect to the normal (0, 0, 1);
+// set 'hole' to true to ensure a hole (clockwise winding)
+bool VRML_LAYER::EnsureWinding( int aContourID, bool aHoleFlag )
+{
+ if( aContourID < 0 || (unsigned int) aContourID >= contours.size() )
+ {
+ error = "EnsureWinding(): aContour is outside the valid range";
+ return false;
+ }
+
+ std::list<int>* cp = contours[aContourID];
+
+ if( cp->size() < 3 )
+ {
+ error = "EnsureWinding(): there are fewer than 3 vertices";
+ return false;
+ }
+
+ double dir = areas[aContourID];
+
+ VERTEX_3D* vp0 = vertices[ cp->back() ];
+ VERTEX_3D* vp1 = vertices[ cp->front() ];
+
+ dir += ( vp1->x - vp0->x ) * ( vp1->y + vp0->y );
+
+ // if dir is positive, winding is CW
+ if( ( aHoleFlag && dir < 0 ) || ( !aHoleFlag && dir > 0 ) )
+ {
+ cp->reverse();
+ areas[aContourID] = -areas[aContourID];
+ }
+
+ return true;
+}
+
+
+bool VRML_LAYER::AppendCircle( double aXpos, double aYpos,
+ double aRadius, int aContourID,
+ bool aHoleFlag )
+{
+ if( aContourID < 0 || (unsigned int) aContourID >= contours.size() )
+ {
+ error = "AppendCircle(): invalid contour (out of range)";
+ return false;
+ }
+
+ int nsides = M_PI * 2.0 * aRadius / minSegLength;
+
+ if( nsides > maxArcSeg )
+ {
+ if( nsides > 2 * maxArcSeg )
+ {
+ // use segments approx. maxAr
+ nsides = M_PI * 2.0 * aRadius / maxSegLength;
+ }
+ else
+ {
+ nsides /= 2;
+ }
+ }
+
+ if( nsides < MIN_NSIDES )
+ nsides = MIN_NSIDES;
+
+ // even numbers give prettier results for circles
+ if( nsides & 1 )
+ nsides += 1;
+
+ double da = M_PI * 2.0 / nsides;
+
+ bool fail = false;
+
+ if( aHoleFlag )
+ {
+ fail |= !AddVertex( aContourID, aXpos + aRadius, aYpos );
+
+ for( double angle = da; angle < M_PI * 2; angle += da )
+ fail |= !AddVertex( aContourID, aXpos + aRadius * cos( angle ),
+ aYpos - aRadius * sin( angle ) );
+ }
+ else
+ {
+ fail |= !AddVertex( aContourID, aXpos + aRadius, aYpos );
+
+ for( double angle = da; angle < M_PI * 2; angle += da )
+ fail |= !AddVertex( aContourID, aXpos + aRadius * cos( angle ),
+ aYpos + aRadius * sin( angle ) );
+ }
+
+ return !fail;
+}
+
+
+// adds a circle the existing list; if 'hole' is true the contour is
+// a hole. Returns true if OK.
+bool VRML_LAYER::AddCircle( double aXpos, double aYpos, double aRadius,
+ bool aHoleFlag, bool aPlatedHole )
+{
+ int pad;
+
+ if( aHoleFlag && aPlatedHole )
+ pad = NewContour( true );
+ else
+ pad = NewContour( false );
+
+ if( pad < 0 )
+ {
+ error = "AddCircle(): failed to add a contour";
+ return false;
+ }
+
+ return AppendCircle( aXpos, aYpos, aRadius, pad, aHoleFlag );
+}
+
+
+// adds a slotted pad with orientation given by angle; if 'hole' is true the
+// contour is a hole. Returns true if OK.
+bool VRML_LAYER::AddSlot( double aCenterX, double aCenterY,
+ double aSlotLength, double aSlotWidth,
+ double aAngle, bool aHoleFlag, bool aPlatedHole )
+{
+ aAngle *= M_PI / 180.0;
+
+ if( aSlotWidth > aSlotLength )
+ {
+ aAngle += M_PI2;
+ std::swap( aSlotLength, aSlotWidth );
+ }
+
+ aSlotWidth /= 2.0;
+ aSlotLength = aSlotLength / 2.0 - aSlotWidth;
+
+ int csides = calcNSides( aSlotWidth, M_PI );
+
+ double capx, capy;
+
+ capx = aCenterX + cos( aAngle ) * aSlotLength;
+ capy = aCenterY + sin( aAngle ) * aSlotLength;
+
+ double ang, da;
+ int i;
+ int pad;
+
+ if( aHoleFlag && aPlatedHole )
+ pad = NewContour( true );
+ else
+ pad = NewContour( false );
+
+ if( pad < 0 )
+ {
+ error = "AddCircle(): failed to add a contour";
+ return false;
+ }
+
+ da = M_PI / csides;
+ bool fail = false;
+
+ if( aHoleFlag )
+ {
+ for( ang = aAngle + M_PI2, i = 0; i < csides; ang -= da, ++i )
+ fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
+ capy + aSlotWidth * sin( ang ) );
+
+ ang = aAngle - M_PI2;
+ fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
+ capy + aSlotWidth * sin( ang ) );
+
+ capx = aCenterX - cos( aAngle ) * aSlotLength;
+ capy = aCenterY - sin( aAngle ) * aSlotLength;
+
+ for( ang = aAngle - M_PI2, i = 0; i < csides; ang -= da, ++i )
+ fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
+ capy + aSlotWidth * sin( ang ) );
+
+ ang = aAngle + M_PI2;
+ fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
+ capy + aSlotWidth * sin( ang ) );
+ }
+ else
+ {
+ for( ang = aAngle - M_PI2, i = 0; i < csides; ang += da, ++i )
+ fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
+ capy + aSlotWidth * sin( ang ) );
+
+ ang = aAngle + M_PI2;
+ fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
+ capy + aSlotWidth * sin( ang ) );
+
+ capx = aCenterX - cos( aAngle ) * aSlotLength;
+ capy = aCenterY - sin( aAngle ) * aSlotLength;
+
+ for( ang = aAngle + M_PI2, i = 0; i < csides; ang += da, ++i )
+ fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
+ capy + aSlotWidth * sin( ang ) );
+
+ ang = aAngle - M_PI2;
+ fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
+ capy + aSlotWidth * sin( ang ) );
+ }
+
+ return !fail;
+}
+
+
+// adds an arc to the given center, start point, pen width, and angle (degrees).
+bool VRML_LAYER::AppendArc( double aCenterX, double aCenterY, double aRadius,
+ double aStartAngle, double aAngle, int aContourID )
+{
+ if( aContourID < 0 || (unsigned int) aContourID >= contours.size() )
+ {
+ error = "AppendArc(): invalid contour (out of range)";
+ return false;
+ }
+
+ aAngle = aAngle / 180.0 * M_PI;
+ aStartAngle = aStartAngle / 180.0 * M_PI;
+
+ int nsides = calcNSides( aRadius, aAngle );
+
+ double da = aAngle / nsides;
+
+ bool fail = false;
+
+ if( aAngle > 0 )
+ {
+ aAngle += aStartAngle;
+ for( double ang = aStartAngle; ang < aAngle; ang += da )
+ fail |= !AddVertex( aContourID, aCenterX + aRadius * cos( ang ),
+ aCenterY + aRadius * sin( ang ) );
+ }
+ else
+ {
+ aAngle += aStartAngle;
+ for( double ang = aStartAngle; ang > aAngle; ang += da )
+ fail |= !AddVertex( aContourID, aCenterX + aRadius * cos( ang ),
+ aCenterY + aRadius * sin( ang ) );
+ }
+
+ return !fail;
+}
+
+
+// adds an arc with the given center, start point, pen width, and angle (degrees).
+bool VRML_LAYER::AddArc( double aCenterX, double aCenterY, double aStartX, double aStartY,
+ double aArcWidth, double aAngle, bool aHoleFlag, bool aPlatedHole )
+{
+ aAngle *= M_PI / 180.0;
+
+ // we don't accept small angles; in fact, 1 degree ( 0.01745 ) is already
+ // way too small but we must set a limit somewhere
+ if( aAngle < 0.01745 && aAngle > -0.01745 )
+ {
+ error = "AddArc(): angle is too small: abs( angle ) < 1 degree";
+ return false;
+ }
+
+ double rad = sqrt( (aStartX - aCenterX) * (aStartX - aCenterX)
+ + (aStartY - aCenterY) * (aStartY - aCenterY) );
+
+ aArcWidth /= 2.0; // this is the radius of the caps
+
+ // we will not accept an arc with an inner radius close to zero so we
+ // set a limit here. the end result will vary somewhat depending on
+ // the output units
+ if( aArcWidth >= ( rad * 1.01 ) )
+ {
+ error = "AddArc(): width/2 exceeds radius*1.01";
+ return false;
+ }
+
+ // calculate the radii of the outer and inner arcs
+ double orad = rad + aArcWidth;
+ double irad = rad - aArcWidth;
+
+ int osides = calcNSides( orad, aAngle );
+ int isides = calcNSides( irad, aAngle );
+ int csides = calcNSides( aArcWidth, M_PI );
+
+ double stAngle = atan2( aStartY - aCenterY, aStartX - aCenterX );
+ double endAngle = stAngle + aAngle;
+
+ // calculate ends of inner and outer arc
+ double oendx = aCenterX + orad* cos( endAngle );
+ double oendy = aCenterY + orad* sin( endAngle );
+ double ostx = aCenterX + orad* cos( stAngle );
+ double osty = aCenterY + orad* sin( stAngle );
+
+ double iendx = aCenterX + irad* cos( endAngle );
+ double iendy = aCenterY + irad* sin( endAngle );
+ double istx = aCenterX + irad* cos( stAngle );
+ double isty = aCenterY + irad* sin( stAngle );
+
+ if( ( aAngle < 0 && !aHoleFlag ) || ( aAngle > 0 && aHoleFlag ) )
+ {
+ aAngle = -aAngle;
+ std::swap( stAngle, endAngle );
+ std::swap( oendx, ostx );
+ std::swap( oendy, osty );
+ std::swap( iendx, istx );
+ std::swap( iendy, isty );
+ }
+
+ int arc;
+
+ if( aHoleFlag && aPlatedHole )
+ arc = NewContour( true );
+ else
+ arc = NewContour( false );
+
+ if( arc < 0 )
+ {
+ error = "AddArc(): could not create a contour";
+ return false;
+ }
+
+ // trace the outer arc:
+ int i;
+ double ang;
+ double da = aAngle / osides;
+
+ for( ang = stAngle, i = 0; i < osides; ang += da, ++i )
+ AddVertex( arc, aCenterX + orad * cos( ang ), aCenterY + orad * sin( ang ) );
+
+ // trace the first cap
+ double capx = ( iendx + oendx ) / 2.0;
+ double capy = ( iendy + oendy ) / 2.0;
+
+ if( aHoleFlag )
+ da = -M_PI / csides;
+ else
+ da = M_PI / csides;
+
+ for( ang = endAngle, i = 0; i < csides; ang += da, ++i )
+ AddVertex( arc, capx + aArcWidth * cos( ang ), capy + aArcWidth * sin( ang ) );
+
+ // trace the inner arc:
+ da = -aAngle / isides;
+
+ for( ang = endAngle, i = 0; i < isides; ang += da, ++i )
+ AddVertex( arc, aCenterX + irad * cos( ang ), aCenterY + irad * sin( ang ) );
+
+ // trace the final cap
+ capx = ( istx + ostx ) / 2.0;
+ capy = ( isty + osty ) / 2.0;
+
+ if( aHoleFlag )
+ da = -M_PI / csides;
+ else
+ da = M_PI / csides;
+
+ for( ang = stAngle + M_PI, i = 0; i < csides; ang += da, ++i )
+ AddVertex( arc, capx + aArcWidth * cos( ang ), capy + aArcWidth * sin( ang ) );
+
+ return true;
+}
+
+
+// tesselates the contours in preparation for a 3D output;
+// returns true if all was fine, false otherwise
+bool VRML_LAYER::Tesselate( VRML_LAYER* holes, bool aHolesOnly )
+{
+ if( !tess )
+ {
+ error = "Tesselate(): GLU tesselator was not initialized";
+ return false;
+ }
+
+ pholes = holes;
+ Fault = false;
+
+ if( aHolesOnly )
+ gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NEGATIVE );
+ else
+ gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
+
+
+ if( contours.size() < 1 || vertices.size() < 3 )
+ {
+ error = "Tesselate(): not enough vertices";
+ return false;
+ }
+
+ // finish the winding calculation on all vertices prior to setting 'fix'
+ if( !fix )
+ {
+ for( unsigned int i = 0; i < contours.size(); ++i )
+ {
+ if( contours[i]->size() < 3 )
+ continue;
+
+ VERTEX_3D* vp0 = vertices[ contours[i]->back() ];
+ VERTEX_3D* vp1 = vertices[ contours[i]->front() ];
+ areas[i] += ( vp1->x - vp0->x ) * ( vp1->y + vp0->y );
+ }
+ }
+
+ // prevent the addition of any further contours and contour vertices
+ fix = true;
+
+ // clear temporary internals which may have been used in a previous run
+ clearTmp();
+
+ // request an outline
+ gluTessProperty( tess, GLU_TESS_BOUNDARY_ONLY, GL_TRUE );
+
+ // adjust internal indices for extra points and holes
+ if( holes )
+ hidx = holes->GetSize();
+ else
+ hidx = 0;
+
+ eidx = idx + hidx;
+
+ if( aHolesOnly && ( checkNContours( true ) == 0 ) )
+ {
+ error = "tesselate(): no hole contours";
+ return false;
+ }
+ else if( !aHolesOnly && ( checkNContours( false ) == 0 ) )
+ {
+ error = "tesselate(): no solid contours";
+ return false;
+ }
+
+ // open the polygon
+ gluTessBeginPolygon( tess, this );
+
+ if( aHolesOnly )
+ {
+ pholes = NULL; // do not accept foreign holes
+ hidx = 0;
+ eidx = idx;
+
+ // add holes
+ pushVertices( true );
+
+ gluTessEndPolygon( tess );
+
+ if( Fault )
+ return false;
+
+ return true;
+ }
+
+ // add solid outlines
+ pushVertices( false );
+
+ // close the polygon
+ gluTessEndPolygon( tess );
+
+ if( Fault )
+ return false;
+
+ // if there are no outlines we cannot proceed
+ if( outline.empty() )
+ {
+ error = "tesselate(): no points in result";
+ return false;
+ }
+
+ // at this point we have a solid outline; add it to the tesselator
+ gluTessBeginPolygon( tess, this );
+
+ if( !pushOutline( NULL ) )
+ return false;
+
+ // add the holes contained by this object
+ pushVertices( true );
+
+ // import external holes (if any)
+ if( hidx && ( holes->Import( idx, tess ) < 0 ) )
+ {
+ std::ostringstream ostr;
+ ostr << "Tesselate():FAILED: " << holes->GetError();
+ error = ostr.str();
+ return false;
+ }
+
+ if( Fault )
+ return false;
+
+ // erase the previous outline data and vertex order
+ // but preserve the extra vertices
+ while( !outline.empty() )
+ {
+ delete outline.back();
+ outline.pop_back();
+ }
+
+ ordmap.clear();
+ ord = 0;
+
+ // go through the vertex lists and reset ephemeral parameters
+ for( unsigned int i = 0; i < vertices.size(); ++i )
+ {
+ vertices[i]->o = -1;
+ }
+
+ for( unsigned int i = 0; i < extra_verts.size(); ++i )
+ {
+ extra_verts[i]->o = -1;
+ }
+
+ // close the polygon; this creates the outline points
+ // and the point ordering list 'ordmap'
+ solid.clear();
+ gluTessEndPolygon( tess );
+
+ // repeat the last operation but request a tesselated surface
+ // rather than an outline; this creates the triangles list.
+ gluTessProperty( tess, GLU_TESS_BOUNDARY_ONLY, GL_FALSE );
+
+ gluTessBeginPolygon( tess, this );
+
+ if( !pushOutline( holes ) )
+ return false;
+
+ gluTessEndPolygon( tess );
+
+ if( Fault )
+ return false;
+
+ return true;
+}
+
+
+bool VRML_LAYER::pushOutline( VRML_LAYER* holes )
+{
+ // traverse the outline list to push all used vertices
+ if( outline.size() < 1 )
+ {
+ error = "pushOutline() failed: no vertices to push";
+ return false;
+ }
+
+ std::list<std::list<int>*>::const_iterator obeg = outline.begin();
+ std::list<std::list<int>*>::const_iterator oend = outline.end();
+
+ int nc = 0; // number of contours pushed
+
+ int pi;
+ std::list<int>::const_iterator begin;
+ std::list<int>::const_iterator end;
+ GLdouble pt[3];
+ VERTEX_3D* vp;
+
+ while( obeg != oend )
+ {
+ if( (*obeg)->size() < 3 )
+ {
+ ++obeg;
+ continue;
+ }
+
+ gluTessBeginContour( tess );
+
+ begin = (*obeg)->begin();
+ end = (*obeg)->end();
+
+ while( begin != end )
+ {
+ pi = *begin;
+
+ if( pi < 0 || (unsigned int) pi > ordmap.size() )
+ {
+ gluTessEndContour( tess );
+ error = "pushOutline():BUG: *outline.begin() is not a valid index to ordmap";
+ return false;
+ }
+
+ // retrieve the actual index
+ pi = ordmap[pi];
+
+ vp = getVertexByIndex( pi, holes );
+
+ if( !vp )
+ {
+ gluTessEndContour( tess );
+ error = "pushOutline():: BUG: ordmap[n] is not a valid index to vertices[]";
+ return false;
+ }
+
+ pt[0] = vp->x;
+ pt[1] = vp->y;
+ pt[2] = 0.0;
+ gluTessVertex( tess, pt, vp );
+ ++begin;
+ }
+
+ gluTessEndContour( tess );
+ ++obeg;
+ ++nc;
+ }
+
+ if( !nc )
+ {
+ error = "pushOutline():: no valid contours available";
+ return false;
+ }
+
+ return true;
+}
+
+
+// writes out the vertex list for a planar feature
+bool VRML_LAYER::WriteVertices( double aZcoord, std::ofstream& aOutFile, int aPrecision )
+{
+ if( ordmap.size() < 3 )
+ {
+ error = "WriteVertices(): not enough vertices";
+ return false;
+ }
+
+ if( aPrecision < 4 )
+ aPrecision = 4;
+
+ int i, j;
+
+ VERTEX_3D* vp = getVertexByIndex( ordmap[0], pholes );
+
+ if( !vp )
+ return false;
+
+ std::string strx, stry, strz;
+ FormatDoublet( vp->x + offsetX, vp->y + offsetY, aPrecision, strx, stry );
+ FormatSinglet( aZcoord, aPrecision, strz );
+
+ aOutFile << strx << " " << stry << " " << strz;
+
+ for( i = 1, j = ordmap.size(); i < j; ++i )
+ {
+ vp = getVertexByIndex( ordmap[i], pholes );
+
+ if( !vp )
+ return false;
+
+ FormatDoublet( vp->x + offsetX, vp->y + offsetY, aPrecision, strx, stry );
+
+ if( i & 1 )
+ aOutFile << ", " << strx << " " << stry << " " << strz;
+ else
+ aOutFile << ",\n" << strx << " " << stry << " " << strz;
+ }
+
+ return !aOutFile.fail();
+}
+
+
+// writes out the vertex list for a 3D feature; top and bottom are the
+// Z values for the top and bottom; top must be > bottom
+bool VRML_LAYER::Write3DVertices( double aTopZ, double aBottomZ,
+ std::ofstream& aOutFile, int aPrecision )
+{
+ if( ordmap.size() < 3 )
+ {
+ error = "Write3DVertices(): insufficient vertices";
+ return false;
+ }
+
+ if( aPrecision < 4 )
+ aPrecision = 4;
+
+ if( aTopZ <= aBottomZ )
+ {
+ error = "Write3DVertices(): top <= bottom";
+ return false;
+ }
+
+ int i, j;
+
+ VERTEX_3D* vp = getVertexByIndex( ordmap[0], pholes );
+
+ if( !vp )
+ return false;
+
+ std::string strx, stry, strz;
+ FormatDoublet( vp->x + offsetX, vp->y + offsetY, aPrecision, strx, stry );
+ FormatSinglet( aTopZ, aPrecision, strz );
+
+ aOutFile << strx << " " << stry << " " << strz;
+
+ for( i = 1, j = ordmap.size(); i < j; ++i )
+ {
+ vp = getVertexByIndex( ordmap[i], pholes );
+
+ if( !vp )
+ return false;
+
+ FormatDoublet( vp->x + offsetX, vp->y + offsetY, aPrecision, strx, stry );
+
+ if( i & 1 )
+ aOutFile << ", " << strx << " " << stry << " " << strz;
+ else
+ aOutFile << ",\n" << strx << " " << stry << " " << strz;
+ }
+
+ // repeat for the bottom layer
+ vp = getVertexByIndex( ordmap[0], pholes );
+ FormatDoublet( vp->x + offsetX, vp->y + offsetY, aPrecision, strx, stry );
+ FormatSinglet( aBottomZ, aPrecision, strz );
+
+ bool endl;
+
+ if( i & 1 )
+ {
+ aOutFile << ", " << strx << " " << stry << " " << strz;
+ endl = false;
+ }
+ else
+ {
+ aOutFile << ",\n" << strx << " " << stry << " " << strz;
+ endl = true;
+ }
+
+ for( i = 1, j = ordmap.size(); i < j; ++i )
+ {
+ vp = getVertexByIndex( ordmap[i], pholes );
+ FormatDoublet( vp->x + offsetX, vp->y + offsetY, aPrecision, strx, stry );
+
+ if( endl )
+ {
+ aOutFile << ", " << strx << " " << stry << " " << strz;
+ endl = false;
+ }
+ else
+ {
+ aOutFile << ",\n" << strx << " " << stry << " " << strz;
+ endl = true;
+ }
+ }
+
+ return !aOutFile.fail();
+}
+
+
+// writes out the index list;
+// 'top' indicates the vertex ordering and should be
+// true for a polygon visible from above the PCB
+bool VRML_LAYER::WriteIndices( bool aTopFlag, std::ofstream& aOutFile )
+{
+ if( triplets.empty() )
+ {
+ error = "WriteIndices(): no triplets (triangular facets) to write";
+ return false;
+ }
+
+ // go through the triplet list and write out the indices based on order
+ std::list<TRIPLET_3D>::const_iterator tbeg = triplets.begin();
+ std::list<TRIPLET_3D>::const_iterator tend = triplets.end();
+
+ int i = 1;
+
+ if( aTopFlag )
+ aOutFile << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3 << ", -1";
+ else
+ aOutFile << tbeg->i2 << ", " << tbeg->i1 << ", " << tbeg->i3 << ", -1";
+
+ ++tbeg;
+
+ while( tbeg != tend )
+ {
+ if( (i++ & 7) == 4 )
+ {
+ i = 1;
+
+ if( aTopFlag )
+ aOutFile << ",\n" << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3 << ", -1";
+ else
+ aOutFile << ",\n" << tbeg->i2 << ", " << tbeg->i1 << ", " << tbeg->i3 << ", -1";
+ }
+ else
+ {
+ if( aTopFlag )
+ aOutFile << ", " << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3 << ", -1";
+ else
+ aOutFile << ", " << tbeg->i2 << ", " << tbeg->i1 << ", " << tbeg->i3 << ", -1";
+ }
+
+ ++tbeg;
+ }
+
+ return !aOutFile.fail();
+}
+
+
+// writes out the index list for a 3D feature
+bool VRML_LAYER::Write3DIndices( std::ofstream& aOutFile, bool aIncludePlatedHoles )
+{
+ if( outline.empty() )
+ {
+ error = "WriteIndices(): no outline available";
+ return false;
+ }
+
+ char mark;
+ bool holes_only = triplets.empty();
+
+ int i = 1;
+ int idx2 = ordmap.size(); // index to the bottom vertices
+
+ if( !holes_only )
+ {
+ mark = ',';
+
+ // go through the triplet list and write out the indices based on order
+ std::list<TRIPLET_3D>::const_iterator tbeg = triplets.begin();
+ std::list<TRIPLET_3D>::const_iterator tend = triplets.end();
+
+ // print out the top vertices
+ aOutFile << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3 << ", -1";
+ ++tbeg;
+
+ while( tbeg != tend )
+ {
+ if( (i++ & 7) == 4 )
+ {
+ i = 1;
+ aOutFile << ",\n" << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3 << ", -1";
+ }
+ else
+ {
+ aOutFile << ", " << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3 << ", -1";
+ }
+
+ ++tbeg;
+ }
+
+ // print out the bottom vertices
+ tbeg = triplets.begin();
+
+ while( tbeg != tend )
+ {
+ if( (i++ & 7) == 4 )
+ {
+ i = 1;
+ aOutFile << ",\n" << (tbeg->i2 + idx2) << ", " << (tbeg->i1 + idx2) << ", " << (tbeg->i3 + idx2) << ", -1";
+ }
+ else
+ {
+ aOutFile << ", " << (tbeg->i2 + idx2) << ", " << (tbeg->i1 + idx2) << ", " << (tbeg->i3 + idx2) << ", -1";
+ }
+
+ ++tbeg;
+ }
+ }
+ else
+ mark = ' ';
+
+
+ // print out indices for the walls joining top to bottom
+ int lastPoint;
+ int curPoint;
+ int curContour = 0;
+
+ std::list<std::list<int>*>::const_iterator obeg = outline.begin();
+ std::list<std::list<int>*>::const_iterator oend = outline.end();
+ std::list<int>* cp;
+ std::list<int>::const_iterator cbeg;
+ std::list<int>::const_iterator cend;
+
+ i = 2;
+ while( obeg != oend )
+ {
+ cp = *obeg;
+
+ if( cp->size() < 3 )
+ {
+ ++obeg;
+ ++curContour;
+ continue;
+ }
+
+ cbeg = cp->begin();
+ cend = cp->end();
+ lastPoint = *(cbeg++);
+
+ // skip all PTH vertices which are not in a solid outline
+ if( !aIncludePlatedHoles && !solid[curContour]
+ && getVertexByIndex( ordmap[lastPoint], pholes )->pth )
+ {
+ ++obeg;
+ ++curContour;
+ continue;
+ }
+
+ while( cbeg != cend )
+ {
+ curPoint = *(cbeg++);
+
+ if( !holes_only )
+ {
+ if( (i++ & 3) == 2 )
+ {
+ i = 1;
+ aOutFile << mark << "\n" << curPoint << ", " << lastPoint << ", " << curPoint + idx2;
+ aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint << ", " << lastPoint + idx2 << ", -1";
+ }
+ else
+ {
+ aOutFile << mark << " " << curPoint << ", " << lastPoint << ", " << curPoint + idx2;
+ aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint << ", " << lastPoint + idx2 << ", -1";
+ }
+ }
+ else
+ {
+ if( (i++ & 3) == 2 )
+ {
+ i = 1;
+ aOutFile << mark << "\n" << curPoint << ", " << curPoint + idx2 << ", " << lastPoint;
+ aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint + idx2 << ", " << lastPoint << ", -1";
+ }
+ else
+ {
+ aOutFile << mark << " " << curPoint << ", " << curPoint + idx2 << ", " << lastPoint;
+ aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint + idx2 << ", " << lastPoint << ", -1";
+ }
+ }
+
+ mark = ',';
+ lastPoint = curPoint;
+ }
+
+ // check if the loop needs to be closed
+ cbeg = cp->begin();
+ cend = --cp->end();
+
+ curPoint = *(cbeg);
+ lastPoint = *(cend);
+
+ if( !holes_only )
+ {
+ if( (i++ & 3) == 2 )
+ {
+ aOutFile << ",\n" << curPoint << ", " << lastPoint << ", " << curPoint + idx2;
+ aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint << ", " << lastPoint + idx2 << ", -1";
+ }
+ else
+ {
+ aOutFile << ", " << curPoint << ", " << lastPoint << ", " << curPoint + idx2;
+ aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint << ", " << lastPoint + idx2 << ", -1";
+ }
+ }
+ else
+ {
+ if( (i++ & 3) == 2 )
+ {
+ aOutFile << ",\n" << curPoint << ", " << curPoint + idx2 << ", " << lastPoint;
+ aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint + idx2 << ", " << lastPoint << ", -1";
+ }
+ else
+ {
+ aOutFile << ", " << curPoint << ", " << curPoint + idx2 << ", " << lastPoint;
+ aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint + idx2 << ", " << lastPoint << ", -1";
+ }
+ }
+
+ ++obeg;
+ ++curContour;
+ }
+
+ return !aOutFile.fail();
+}
+
+
+// add a triangular facet (triplet) to the ouptut index list
+bool VRML_LAYER::addTriplet( VERTEX_3D* p0, VERTEX_3D* p1, VERTEX_3D* p2 )
+{
+ double dx0 = p1->x - p0->x;
+ double dx1 = p2->x - p0->x;
+
+ double dy0 = p1->y - p0->y;
+ double dy1 = p2->y - p0->y;
+
+ // this number is chosen because we shall only write 9 decimal places
+ // at most on the VRML output
+ double err = 0.000000001;
+
+ // test if the triangles are degenerate (parallel sides)
+
+ if( dx0 < err && dx0 > -err && dx1 < err && dx1 > -err )
+ return false;
+
+ if( dy0 < err && dy0 > -err && dy1 < err && dy1 > -err )
+ return false;
+
+ double sl0 = dy0 / dx0;
+ double sl1 = dy1 / dx1;
+
+ double dsl = sl1 - sl0;
+
+ if( dsl < err && dsl > -err )
+ return false;
+
+ triplets.push_back( TRIPLET_3D( p0->o, p1->o, p2->o ) );
+
+ return true;
+}
+
+
+// add an extra vertex (to be called only by the COMBINE callback)
+VERTEX_3D* VRML_LAYER::AddExtraVertex( double aXpos, double aYpos, bool aPlatedHole )
+{
+ VERTEX_3D* vertex = new VERTEX_3D;
+
+ if( !vertex )
+ {
+ error = "AddExtraVertex(): could not allocate a new vertex";
+ return NULL;
+ }
+
+ if( eidx == 0 )
+ eidx = idx + hidx;
+
+ vertex->x = aXpos;
+ vertex->y = aYpos;
+ vertex->i = eidx++;
+ vertex->o = -1;
+ vertex->pth = aPlatedHole;
+
+ extra_verts.push_back( vertex );
+
+ return vertex;
+}
+
+
+// start a GL command list
+void VRML_LAYER::glStart( GLenum cmd )
+{
+ glcmd = cmd;
+
+ while( !vlist.empty() )
+ vlist.pop_back();
+}
+
+
+// process a vertex
+void VRML_LAYER::glPushVertex( VERTEX_3D* vertex )
+{
+ if( vertex->o < 0 )
+ {
+ vertex->o = ord++;
+ ordmap.push_back( vertex->i );
+ }
+
+ vlist.push_back( vertex );
+}
+
+
+// end a GL command list
+void VRML_LAYER::glEnd( void )
+{
+ switch( glcmd )
+ {
+ case GL_LINE_LOOP:
+ {
+ // add the loop to the list of outlines
+ std::list<int>* loop = new std::list<int>;
+
+ if( !loop )
+ break;
+
+ double firstX = 0.0;
+ double firstY = 0.0;
+ double lastX = 0.0;
+ double lastY = 0.0;
+ double curX, curY;
+ double area = 0.0;
+
+ if( vlist.size() > 0 )
+ {
+ loop->push_back( vlist[0]->o );
+ firstX = vlist[0]->x;
+ firstY = vlist[0]->y;
+ lastX = firstX;
+ lastY = firstY;
+ }
+
+ for( size_t i = 1; i < vlist.size(); ++i )
+ {
+ loop->push_back( vlist[i]->o );
+ curX = vlist[i]->x;
+ curY = vlist[i]->y;
+ area += ( curX - lastX ) * ( curY + lastY );
+ lastX = curX;
+ lastY = curY;
+ }
+
+ area += ( firstX - lastX ) * ( firstY + lastY );
+
+ outline.push_back( loop );
+
+ if( area <= 0.0 )
+ solid.push_back( true );
+ else
+ solid.push_back( false );
+ }
+ break;
+
+ case GL_TRIANGLE_FAN:
+ processFan();
+ break;
+
+ case GL_TRIANGLE_STRIP:
+ processStrip();
+ break;
+
+ case GL_TRIANGLES:
+ processTri();
+ break;
+
+ default:
+ break;
+ }
+
+ while( !vlist.empty() )
+ vlist.pop_back();
+
+ glcmd = 0;
+}
+
+
+// set the error message
+void VRML_LAYER::SetGLError( GLenum errorID )
+{
+ const char * msg = (const char*)gluErrorString( errorID );
+
+ // If errorID is an illegal id, gluErrorString returns NULL
+ if( msg )
+ error = msg;
+ else
+ error.clear();
+
+ if( error.empty() )
+ {
+ std::ostringstream ostr;
+ ostr << "Unknown OpenGL error: " << errorID;
+ error = ostr.str();
+ }
+}
+
+
+// process a GL_TRIANGLE_FAN list
+void VRML_LAYER::processFan( void )
+{
+ if( vlist.size() < 3 )
+ return;
+
+ VERTEX_3D* p0 = vlist[0];
+
+ int i;
+ int end = vlist.size();
+
+ for( i = 2; i < end; ++i )
+ {
+ addTriplet( p0, vlist[i - 1], vlist[i] );
+ }
+}
+
+
+// process a GL_TRIANGLE_STRIP list
+void VRML_LAYER::processStrip( void )
+{
+ // note: (source: http://www.opengl.org/wiki/Primitive)
+ // GL_TRIANGLE_STRIP​: Every group of 3 adjacent vertices forms a triangle.
+ // The face direction of the strip is determined by the winding of the
+ // first triangle. Each successive triangle will have its effective face
+ // order reverse, so the system compensates for that by testing it in the
+ // opposite way. A vertex stream of n length will generate n-2 triangles.
+
+ if( vlist.size() < 3 )
+ return;
+
+ int i;
+ int end = vlist.size();
+ bool flip = false;
+
+ for( i = 2; i < end; ++i )
+ {
+ if( flip )
+ {
+ addTriplet( vlist[i - 1], vlist[i - 2], vlist[i] );
+ flip = false;
+ }
+ else
+ {
+ addTriplet( vlist[i - 2], vlist[i - 1], vlist[i] );
+ flip = true;
+ }
+ }
+}
+
+
+// process a GL_TRIANGLES list
+void VRML_LAYER::processTri( void )
+{
+ // notes:
+ // 1. each successive group of 3 vertices is a triangle
+ // 2. as per OpenGL specification, any incomplete triangles are to be ignored
+
+ if( vlist.size() < 3 )
+ return;
+
+ int i;
+ int end = vlist.size();
+
+ for( i = 2; i < end; i += 3 )
+ addTriplet( vlist[i - 2], vlist[i - 1], vlist[i] );
+}
+
+
+int VRML_LAYER::checkNContours( bool holes )
+{
+ int nc = 0; // number of contours
+
+ if( contours.empty() )
+ return 0;
+
+ std::list<int>::const_iterator begin;
+ std::list<int>::const_iterator end;
+
+ for( size_t i = 0; i < contours.size(); ++i )
+ {
+ if( contours[i]->size() < 3 )
+ continue;
+
+ if( ( holes && areas[i] <= 0.0 ) || ( !holes && areas[i] > 0.0 ) )
+ continue;
+
+ ++nc;
+ }
+
+ return nc;
+}
+
+
+// push the internally held vertices
+void VRML_LAYER::pushVertices( bool holes )
+{
+ // push the internally held vertices
+ unsigned int i;
+
+ std::list<int>::const_iterator begin;
+ std::list<int>::const_iterator end;
+ GLdouble pt[3];
+ VERTEX_3D* vp;
+
+ for( i = 0; i < contours.size(); ++i )
+ {
+ if( contours[i]->size() < 3 )
+ continue;
+
+ if( ( holes && areas[i] <= 0.0 ) || ( !holes && areas[i] > 0.0 ) )
+ continue;
+
+ gluTessBeginContour( tess );
+
+ begin = contours[i]->begin();
+ end = contours[i]->end();
+
+ while( begin != end )
+ {
+ vp = vertices[ *begin ];
+ pt[0] = vp->x;
+ pt[1] = vp->y;
+ pt[2] = 0.0;
+ gluTessVertex( tess, pt, vp );
+ ++begin;
+ }
+
+ gluTessEndContour( tess );
+ }
+
+ return;
+}
+
+
+VERTEX_3D* VRML_LAYER::getVertexByIndex( int aPointIndex, VRML_LAYER* holes )
+{
+ if( aPointIndex < 0 || (unsigned int) aPointIndex >= ( idx + hidx + extra_verts.size() ) )
+ {
+ error = "getVertexByIndex():BUG: invalid index";
+ return NULL;
+ }
+
+ if( aPointIndex < idx )
+ {
+ // vertex is in the vertices[] list
+ return vertices[ aPointIndex ];
+ }
+ else if( aPointIndex >= idx + hidx )
+ {
+ // vertex is in the extra_verts[] list
+ return extra_verts[aPointIndex - idx - hidx];
+ }
+
+ // vertex is in the holes object
+ if( !holes )
+ {
+ error = "getVertexByIndex():BUG: invalid index";
+ return NULL;
+ }
+
+ VERTEX_3D* vp = holes->GetVertexByIndex( aPointIndex );
+
+ if( !vp )
+ {
+ std::ostringstream ostr;
+ ostr << "getVertexByIndex():FAILED: " << holes->GetError();
+ error = ostr.str();
+ return NULL;
+ }
+
+ return vp;
+}
+
+
+// retrieve the total number of vertices
+int VRML_LAYER::GetSize( void )
+{
+ return vertices.size();
+}
+
+
+// Inserts all contours into the given tesselator; this results in the
+// renumbering of all vertices from 'start'. Returns the end number.
+// Take care when using this call since tesselators cannot work on
+// the internal data concurrently
+int VRML_LAYER::Import( int start, GLUtesselator* tess )
+{
+ if( start < 0 )
+ {
+ error = "Import(): invalid index ( start < 0 )";
+ return -1;
+ }
+
+ if( !tess )
+ {
+ error = "Import(): NULL tesselator pointer";
+ return -1;
+ }
+
+ unsigned int i, j;
+
+ // renumber from 'start'
+ for( i = 0, j = vertices.size(); i < j; ++i )
+ {
+ vertices[i]->i = start++;
+ vertices[i]->o = -1;
+ }
+
+ // push each contour to the tesselator
+ VERTEX_3D* vp;
+ GLdouble pt[3];
+
+ std::list<int>::const_iterator cbeg;
+ std::list<int>::const_iterator cend;
+
+ for( i = 0; i < contours.size(); ++i )
+ {
+ if( contours[i]->size() < 3 )
+ continue;
+
+ cbeg = contours[i]->begin();
+ cend = contours[i]->end();
+
+ gluTessBeginContour( tess );
+
+ while( cbeg != cend )
+ {
+ vp = vertices[ *cbeg++ ];
+ pt[0] = vp->x;
+ pt[1] = vp->y;
+ pt[2] = 0.0;
+ gluTessVertex( tess, pt, vp );
+ }
+
+ gluTessEndContour( tess );
+ }
+
+ return start;
+}
+
+
+// return the vertex identified by index
+VERTEX_3D* VRML_LAYER::GetVertexByIndex( int aPointIndex )
+{
+ int i0 = vertices[0]->i;
+
+ if( aPointIndex < i0 || aPointIndex >= ( i0 + (int) vertices.size() ) )
+ {
+ error = "GetVertexByIndex(): invalid index";
+ return NULL;
+ }
+
+ return vertices[aPointIndex - i0];
+}
+
+
+// return the error string
+const std::string& VRML_LAYER::GetError( void )
+{
+ return error;
+}
+
+
+void VRML_LAYER::SetVertexOffsets( double aXoffset, double aYoffset )
+{
+ offsetX = aXoffset;
+ offsetY = aYoffset;
+ return;
+}
diff --git a/utils/idftools/vrml_layer.h b/utils/idftools/vrml_layer.h
new file mode 100644
index 0000000..92b5891
--- /dev/null
+++ b/utils/idftools/vrml_layer.h
@@ -0,0 +1,461 @@
+/*
+ * file: vrml_layer.h
+ *
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 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
+ */
+
+/**
+ * @file vrml_layer.h
+ */
+
+/*
+ * Classes and structures to support the tesselation of a
+ * PCB for VRML output.
+ */
+
+#ifndef VRML_LAYER_H
+#define VRML_LAYER_H
+
+
+#include <wx/glcanvas.h> // CALLBACK definition, needed on Windows
+ // alse needed on OSX to define __DARWIN__
+
+#ifdef __WXMAC__
+# ifdef __DARWIN__
+# include <OpenGL/glu.h>
+# else
+# include <glu.h>
+# endif
+#else
+# include <GL/glu.h>
+#endif
+
+#include <fstream>
+#include <vector>
+#include <list>
+#include <utility>
+
+#ifndef M_PI2
+#define M_PI2 ( M_PI / 2.0 )
+#endif
+
+#ifndef M_PI4
+#define M_PI4 ( M_PI / 4.0 )
+#endif
+
+
+struct VERTEX_3D
+{
+ double x;
+ double y;
+ int i; // vertex index
+ int o; // vertex order
+ bool pth; // true for plate-through hole
+};
+
+struct TRIPLET_3D
+{
+ int i1, i2, i3;
+
+ TRIPLET_3D( int p1, int p2, int p3 )
+ {
+ i1 = p1;
+ i2 = p2;
+ i3 = p3;
+ }
+};
+
+
+class VRML_LAYER
+{
+private:
+ // Arc parameters
+ int maxArcSeg; // maximum number of arc segments in a small circle
+ double minSegLength; // min. segment length
+ double maxSegLength; // max. segment length
+
+ // Vertex offsets to work around a suspected GLU tesselator bug
+ double offsetX;
+ double offsetY;
+
+ bool fix; // when true, no more vertices may be added by the user
+ int idx; // vertex index (number of contained vertices)
+ int ord; // vertex order (number of ordered vertices)
+ std::vector<VERTEX_3D*> vertices; // vertices of all contours
+ std::vector<std::list<int>*> contours; // lists of vertices for each contour
+ std::vector<bool>pth; // indicates whether a 'contour' is a PTH or not
+ std::vector<bool>solid; // indicates whether a 'contour' is a solid or a hole
+ std::vector< double > areas; // area of the contours (positive if winding is CCW)
+ std::list<TRIPLET_3D> triplets; // output facet triplet list (triplet of ORDER values)
+ std::list<std::list<int>*> outline; // indices for outline outputs (index by ORDER values)
+ std::vector<int> ordmap; // mapping of ORDER to INDEX
+
+ std::string error; // error message
+
+ int hidx; // number of vertices in the holes
+ int eidx; // index for extra vertices
+ std::vector<VERTEX_3D*> extra_verts; // extra vertices added for outlines and facets
+ std::vector<VERTEX_3D*> vlist; // vertex list for the GL command in progress
+ VRML_LAYER* pholes; // pointer to another layer object used for tesselation;
+ // this object is normally expected to hold only holes
+
+ GLUtesselator* tess; // local instance of the GLU tesselator
+
+ GLenum glcmd; // current GL command type ( fan, triangle, tri-strip, loop )
+
+ void clearTmp( void ); // clear ephemeral data used by the tesselation routine
+
+ // add a triangular facet (triplet) to the output index list
+ bool addTriplet( VERTEX_3D* p0, VERTEX_3D* p1, VERTEX_3D* p2 );
+
+ // retrieve a vertex given its index; the vertex may be contained in the
+ // vertices vector, extra_verts vector, or foreign VRML_LAYER object
+ VERTEX_3D* getVertexByIndex( int aPointIndex, VRML_LAYER* holes );
+
+ void processFan( void ); // process a GL_TRIANGLE_FAN list
+ void processStrip( void ); // process a GL_TRIANGLE_STRIP list
+ void processTri( void ); // process a GL_TRIANGLES list
+
+ void pushVertices( bool holes ); // push the internal vertices
+ bool pushOutline( VRML_LAYER* holes ); // push the outline vertices
+
+ // calculate number of sides on an arc (angle is in radians)
+ int calcNSides( double aRadius, double aAngle );
+
+ // returns the number of solid or hole contours
+ int checkNContours( bool holes );
+
+public:
+ /// set to true when a fault is encountered during tesselation
+ bool Fault;
+
+ VRML_LAYER();
+ virtual ~VRML_LAYER();
+
+ /**
+ * Function GetArcParams
+ * retieves the parameters used in calculating the number of vertices in an arc
+ *
+ * @param aMaxSeg is the maximum number of segments for an arc with cords of length aMinLength
+ * @param aMinLength is the minimum length of cords in an arc
+ * @param aMaxLength is the maximum length of cords in an arc
+ */
+ void GetArcParams( int& aMaxSeg, double& aMinLength, double& aMaxLength );
+
+ /**
+ * Function SetArcParams
+ * sets the parameters used in calculating the number of vertices in an arc.
+ * The default settings are reasonable for rendering for unit lengths of 1mm
+ *
+ * @param aMaxSeg is the maximum number of segments for an arc with cords of length aMinLength
+ * @param aMinLength is the minimum length of cords in an arc
+ * @param aMaxLength is the maximum length of cords in an arc
+ *
+ * @return bool: true if the parameters were accepted
+ */
+ bool SetArcParams( int aMaxSeg, double aMinLength, double aMaxLength );
+
+ /**
+ * Function Clear
+ * erases all data except for arc parameters.
+ */
+ void Clear( void );
+
+ /**
+ * Function GetSize
+ * returns the total number of vertices indexed
+ */
+ int GetSize( void );
+
+ /**
+ * Function GetNConours
+ * returns the number of stored contours
+ */
+ int GetNContours( void )
+ {
+ return contours.size();
+ }
+
+ /**
+ * Function NewContour
+ * creates a new list of vertices and returns an index to the list
+ *
+ * @param aPlatedHole is true if the new contour will represent a plated hole
+ *
+ * @return int: index to the list or -1 if the operation failed
+ */
+ int NewContour( bool aPlatedHole = false );
+
+ /**
+ * Function AddVertex
+ * adds a point to the requested contour
+ *
+ * @param aContour is an index previously returned by a call to NewContour()
+ * @param aXpos is the X coordinate of the vertex
+ * @param aYpos is the Y coordinate of the vertex
+ *
+ * @return bool: true if the vertex was added
+ */
+ bool AddVertex( int aContourID, double aXpos, double aYpos );
+
+ /**
+ * Function EnsureWinding
+ * checks the winding of a contour and ensures that it is a hole or
+ * a solid depending on the value of @param hole
+ *
+ * @param aContour is an index to a contour as returned by NewContour()
+ * @param aHoleFlag determines if the contour must be a hole
+ *
+ * @return bool: true if the operation suceeded
+ */
+ bool EnsureWinding( int aContourID, bool aHoleFlag );
+
+ /**
+ * Function AppendCircle
+ * adds a circular contour to the specified (empty) contour
+ *
+ * @param aXpos is the X coordinate of the hole center
+ * @param aYpos is the Y coordinate of the hole center
+ * @param aRadius is the radius of the hole
+ * @param aContourID is the contour index
+ * @param aHoleFlag determines if the contour to be created is a cutout
+ *
+ * @return bool: true if the new contour was successfully created
+ */
+ bool AppendCircle( double aXpos, double aYpos, double aRadius,
+ int aContourID, bool aHoleFlag = false );
+
+ /**
+ * Function AddCircle
+ * creates a circular contour and adds it to the internal list
+ *
+ * @param aXpos is the X coordinate of the hole center
+ * @param aYpos is the Y coordinate of the hole center
+ * @param aRadius is the radius of the hole
+ * @param aHoleFlag determines if the contour to be created is a cutout
+ * @param aPlatedHole is true if this is a plated hole
+ *
+ * @return bool: true if the new contour was successfully created
+ */
+ bool AddCircle( double aXpos, double aYpos, double aRadius,
+ bool aHoleFlag = false, bool aPlatedHole = false );
+
+ /**
+ * Function AddSlot
+ * creates and adds a slot feature to the list of contours
+ *
+ * @param aCenterX is the X coordinate of the slot's center
+ * @param aCenterY is the Y coordinate of the slot's center
+ * @param aSlotLength is the length of the slot along the major axis
+ * @param aSlotWidth is the width of the slot along the minor axis
+ * @param aAngle (degrees) is the orientation of the slot
+ * @param aHoleFlag determines whether the slot is a hole or a solid
+ * @param aPlatedHole is true if this is a plated slot
+ *
+ * @return bool: true if the slot was successfully created
+ */
+ bool AddSlot( double aCenterX, double aCenterY, double aSlotLength, double aSlotWidth,
+ double aAngle, bool aHoleFlag = false, bool aPlatedHole = false );
+
+ /**
+ * Function AppendArc
+ * adds an arc to the specified contour
+ *
+ * @param aCenterX is the X coordinate of the arc's center
+ * @param aCenterY is the Y coordinate of the arc's center
+ * @param aRadius is the radius of the arc
+ * @param aStartAngle (degrees) is the starting angle of the arc
+ * @param aAngle (degrees) is the measure of the arc
+ * @param aContourID is the contour's index
+ *
+ * @return bool: true if the slot was successfully created
+ */
+ bool AppendArc( double aCenterX, double aCenterY, double aRadius,
+ double aStartAngle, double aAngle, int aContourID );
+
+ /**
+ * Function AddArc
+ * creates a slotted arc and adds it to the internal list of contours
+ *
+ * @param aCenterX is the X coordinate of the arc's center
+ * @param aCenterY is the Y coordinate of the arc's center
+ * @param aStartX is the X coordinate of the starting point
+ * @param aStartY is the Y coordinate of the starting point
+ * @param aArcWidth is the width of the arc
+ * @param aAngle is the included angle (degrees)
+ * @param aHoleFlag determines whether the arc is to be a hole or a solid
+ * @param aPlatedHole is true if this is a plated slotted arc
+ *
+ * @return bool: true if the feature was successfully created
+ */
+ bool AddArc( double aCenterX, double aCenterY,
+ double aStartX, double aStartY,
+ double aArcWidth, double aAngle,
+ bool aHoleFlag = false, bool aPlatedHole = false );
+
+ /**
+ * Function Tesselate
+ * creates a list of outline vertices as well as the
+ * vertex sets required to render the surface.
+ *
+ * @param holes is an optional pointer to cutouts to be imposed on the
+ * surface.
+ * @param aHolesOnly is true if the outline contains only holes
+ *
+ * @return bool: true if the operation succeeded
+ */
+ bool Tesselate( VRML_LAYER* holes = NULL, bool aHolesOnly = false );
+
+ /**
+ * Function WriteVertices
+ * writes out the list of vertices required to render a
+ * planar surface.
+ *
+ * @param aZcoord is the Z coordinate of the plane
+ * @param aOutFile is the file to write to
+ * @param aPrecision is the precision of the output coordinates
+ *
+ * @return bool: true if the operation succeeded
+ */
+ bool WriteVertices( double aZcoord, std::ofstream& aOutFile, int aPrecision );
+
+ /**
+ * Function Write3DVertices
+ * writes out the list of vertices required to render an extruded solid
+ *
+ * @param aTopZ is the Z coordinate of the top plane
+ * @param aBottomZ is the Z coordinate of the bottom plane
+ * @param aOutFile is the file to write to
+ * @param aPrecision is the precision of the output coordinates
+ *
+ * @return bool: true if the operation succeeded
+ */
+ bool Write3DVertices( double aTopZ, double aBottomZ, std::ofstream& aOutFile, int aPrecision );
+
+ /**
+ * Function WriteIndices
+ * writes out the vertex sets required to render a planar
+ * surface.
+ *
+ * @param aTopFlag is true if the surface is to be visible from above;
+ * if false the surface will be visible from below.
+ * @param aOutFile is the file to write to
+ *
+ * @return bool: true if the operation succeeded
+ */
+ bool WriteIndices( bool aTopFlag, std::ofstream& aOutFile );
+
+ /**
+ * Function Write3DIndices
+ * writes out the vertex sets required to render an extruded solid
+ *
+ * @param aOutFile is the file to write to
+ * @param aIncludePlatedHoles is true if holes marked as plated should
+ * be rendered. Default is false since the user will usually
+ * render these holes in a different color
+ *
+ * @return bool: true if the operation succeeded
+ */
+ bool Write3DIndices( std::ofstream& aOutFile, bool aIncludePlatedHoles = false );
+
+ /**
+ * Function AddExtraVertex
+ * adds an extra vertex as required by the GLU tesselator.
+ *
+ * @param aXpos is the X coordinate of the newly created point
+ * @param aYpos is the Y coordinate of the newly created point
+ * @param aPlatedHole is true if this point is part of a plated hole
+ *
+ * @return VERTEX_3D*: is the new vertex or NULL if a vertex
+ * could not be created.
+ */
+ VERTEX_3D* AddExtraVertex( double aXpos, double aYpos, bool aPlatedHole );
+
+ /**
+ * Function glStart
+ * is invoked by the GLU tesselator callback to notify this object
+ * of the type of GL command which is applicable to the upcoming
+ * vertex list.
+ *
+ * @param cmd is the GL command
+ */
+ void glStart( GLenum cmd );
+
+ /**
+ * Function glPushVertex
+ * is invoked by the GLU tesselator callback; the supplied vertex is
+ * added to the internal list of vertices awaiting processing upon
+ * execution of glEnd()
+ *
+ * @param vertex is a vertex forming part of the GL command as previously
+ * set by glStart
+ */
+ void glPushVertex( VERTEX_3D* vertex );
+
+ /**
+ * Function glEnd
+ * is invoked by the GLU tesselator callback to notify this object
+ * that the vertex list is complete and ready for processing
+ */
+ void glEnd( void );
+
+ /**
+ * Function SetGLError
+ * sets the error message according to the specified OpenGL error
+ */
+ void SetGLError( GLenum error_id );
+
+ /**
+ * Function Import
+ * inserts all contours into the given tesselator; this
+ * results in the renumbering of all vertices from @param start.
+ * Take care when using this call since tesselators cannot work on
+ * the internal data concurrently.
+ *
+ * @param start is the starting number for vertex indices
+ * @param tess is a pointer to a GLU Tesselator object
+ *
+ * @return int: the number of vertices exported
+ */
+ int Import( int start, GLUtesselator* tess );
+
+ /**
+ * Function GetVertexByIndex
+ * returns a pointer to the requested vertex or
+ * NULL if no such vertex exists.
+ *
+ * @param aPointIndex is a vertex index
+ *
+ * @return VERTEX_3D*: the requested vertex or NULL
+ */
+ VERTEX_3D* GetVertexByIndex( int aPointIndex );
+
+ /*
+ * Function GetError
+ * Returns the error message related to the last failed operation
+ */
+ const std::string& GetError( void );
+
+ void SetVertexOffsets( double aXoffset, double aYoffset );
+};
+
+#endif // VRML_LAYER_H