summaryrefslogtreecommitdiff
path: root/utils/idftools/idf_outlines.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'utils/idftools/idf_outlines.cpp')
-rw-r--r--utils/idftools/idf_outlines.cpp3614
1 files changed, 3614 insertions, 0 deletions
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;
+}