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