/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2014-2017 Cirilo Bernardo * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. * * 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 #include #include #include #include #include #include #include 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 == nullptr ) { 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 BOARD_OUTLINE::BOARD_OUTLINE() { outlineType = OTLN_BOARD; single = false; owner = UNOWNED; parent = nullptr; thickness = 0.0; unit = UNIT_MM; } BOARD_OUTLINE::~BOARD_OUTLINE() { clear(); } IDF3::OUTLINE_TYPE BOARD_OUTLINE::GetOutlineType( void ) { return outlineType; } void BOARD_OUTLINE::readOutlines( std::istream& 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 = nullptr; IDF_SEGMENT* sp = nullptr; 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 ) { try { op = new IDF_OUTLINE; } catch( std::bad_alloc& ) { 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() ) ); } try { op = new IDF_OUTLINE; } catch( std::bad_alloc& ) { clearOutlines(); throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "memory allocation failed" ) ); } outlines.push_back( op ); loopidx = tmp; } } 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() ) ); } try { op = new IDF_OUTLINE; } catch( std::bad_alloc& ) { 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 == nullptr ) { 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; try { if( ang > -MIN_ANG && ang < MIN_ANG ) { sp = new IDF_SEGMENT( prePt, curPt ); } else { sp = new IDF_SEGMENT( prePt, curPt, ang, false ); } } catch( std::bad_alloc& ) { 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; } } // 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::ostream& 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::ostream& 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::ostream& aBoardFile, IDF_OUTLINE* aOutline, size_t aIndex ) { std::list::iterator bo; std::list::iterator eo; if( !aOutline ) throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "\n* BUG: nullptr 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::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; } } } void BOARD_OUTLINE::writeOutlines( std::ostream& 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; } } 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::istream& 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() ) ); } } void BOARD_OUTLINE::writeData( std::ostream& 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"; } 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: nullptr 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* 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 nullptr; } 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(); } void BOARD_OUTLINE::AddComment( const std::string& aComment ) { if( aComment.empty() ) return; comments.push_back( aComment ); } 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 nullptr; 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(); } OTHER_OUTLINE::OTHER_OUTLINE( IDF3_BOARD* aParent ) { setParent( aParent ); outlineType = OTLN_OTHER; side = LYR_INVALID; single = false; } 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::istream& 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() ) ); } } } void OTHER_OUTLINE::writeData( std::ostream& 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"; } 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; } 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::istream& 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() ) ); } } } 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() ) ); } } } void ROUTE_OUTLINE::writeData( std::ostream& 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"; } 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; } 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::istream& 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" ) ); } } void PLACE_OUTLINE::writeData( std::ostream& 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; } ROUTE_KO_OUTLINE::ROUTE_KO_OUTLINE( IDF3_BOARD* aParent ) : ROUTE_OUTLINE( aParent ) { outlineType = OTLN_ROUTE_KEEPOUT; return; } PLACE_KO_OUTLINE::PLACE_KO_OUTLINE( IDF3_BOARD* aParent ) : PLACE_OUTLINE( aParent ) { outlineType = OTLN_PLACE_KEEPOUT; return; } VIA_KO_OUTLINE::VIA_KO_OUTLINE( IDF3_BOARD* aParent ) : OTHER_OUTLINE( aParent ) { single = true; outlineType = OTLN_VIA_KEEPOUT; } 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 = std::move( aGroupName ); return true; } const std::string& GROUP_OUTLINE::GetGroupName( void ) { return groupName; } void GROUP_OUTLINE::readData( std::istream& 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" ) ); } } void GROUP_OUTLINE::writeData( std::ostream& 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"; } 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; } 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::istream& 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() ) ); } } } bool IDF3_COMP_OUTLINE::writeProperties( std::ostream& 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::istream& 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() ) ); } } } void IDF3_COMP_OUTLINE::writeData( std::ostream& 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"; } } 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(); } const std::string& IDF3_COMP_OUTLINE::GetGeomName( void ) { return geometry; } void IDF3_COMP_OUTLINE::SetPartName( const std::string& aPartName ) { part = aPartName; uid.clear(); } 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_SEGMENT* sp; IDF_OUTLINE* ol; p1.x = 1.5 * cos( a ); p1.y = 1.5 * sin( a ); try { ol = new IDF_OUTLINE; } catch( std::bad_alloc& ) { 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 ); } try { sp = new IDF_SEGMENT( p1, p2 ); } catch( std::bad_alloc& ) { 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 ); try { sp = new IDF_SEGMENT( p1, p2 ); } catch( std::bad_alloc& ) { Clear(); return false; } ol->push( sp ); outlines.push_back( ol ); return true; }