1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2014-2017  Cirilo Bernardo
5  * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
23  */
24 
25 #include <iostream>
26 #include <iomanip>
27 #include <sstream>
28 #include <cmath>
29 #include <utility>
30 
31 #include <idf_helpers.h>
32 #include <idf_outlines.h>
33 #include <idf_parser.h>
34 
35 using namespace IDF3;
36 using namespace std;
37 
38 
GetOutlineTypeString(IDF3::OUTLINE_TYPE aOutlineType)39 static std::string GetOutlineTypeString( IDF3::OUTLINE_TYPE aOutlineType )
40 {
41     switch( aOutlineType )
42     {
43     case OTLN_BOARD:
44         return ".BOARD_OUTLINE";
45 
46     case OTLN_OTHER:
47         return ".OTHER_OUTLINE";
48 
49     case OTLN_PLACE:
50         return ".PLACEMENT_OUTLINE";
51 
52     case OTLN_ROUTE:
53         return ".ROUTE_OUTLINE";
54 
55     case OTLN_PLACE_KEEPOUT:
56         return ".PLACE_KEEPOUT";
57 
58     case OTLN_ROUTE_KEEPOUT:
59         return ".ROUTE_KEEPOUT";
60 
61     case OTLN_VIA_KEEPOUT:
62         return ".VIA_KEEPOUT";
63 
64     case OTLN_GROUP_PLACE:
65         return ".PLACE_REGION";
66 
67     case OTLN_COMPONENT:
68         return "COMPONENT OUTLINE";
69 
70     default:
71         break;
72     }
73 
74     std::ostringstream ostr;
75     ostr << "[INVALID OUTLINE TYPE VALUE]:" << aOutlineType;
76 
77     return ostr.str();
78 }
79 
80 
81 #ifndef DISABLE_IDF_OWNERSHIP
CheckOwnership(int aSourceLine,const char * aSourceFunc,IDF3_BOARD * aParent,IDF3::KEY_OWNER aOwnerCAD,IDF3::OUTLINE_TYPE aOutlineType,std::string & aErrorString)82 static bool CheckOwnership( int aSourceLine, const char* aSourceFunc,
83                             IDF3_BOARD* aParent, IDF3::KEY_OWNER aOwnerCAD,
84                             IDF3::OUTLINE_TYPE aOutlineType, std::string& aErrorString )
85 {
86     if( aParent == nullptr )
87     {
88         ostringstream ostr;
89         ostr << "* " << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
90         ostr << "* BUG: outline's parent not set; cannot enforce ownership rules\n";
91         ostr << "* outline type: " << GetOutlineTypeString( aOutlineType );
92         aErrorString = ostr.str();
93 
94         return false;
95     }
96 
97     // note: component outlines have no owner so we don't care about
98     // who modifies them
99     if( aOwnerCAD == UNOWNED || aOutlineType == IDF3::OTLN_COMPONENT )
100         return true;
101 
102     IDF3::CAD_TYPE parentCAD = aParent->GetCadType();
103 
104     if( aOwnerCAD == MCAD && parentCAD == CAD_MECH )
105         return true;
106 
107     if( aOwnerCAD == ECAD && parentCAD == CAD_ELEC )
108         return true;
109 
110     do
111     {
112         ostringstream ostr;
113         ostr << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
114         ostr << "* ownership violation; CAD type is ";
115 
116         if( parentCAD == CAD_MECH )
117             ostr << "MCAD ";
118         else
119             ostr << "ECAD ";
120 
121         ostr << "while outline owner is " << GetOwnerString( aOwnerCAD ) << "\n";
122         ostr << "* outline type: " << GetOutlineTypeString( aOutlineType );
123         aErrorString = ostr.str();
124 
125     } while( 0 );
126 
127     return false;
128 }
129 #endif
130 
131 
BOARD_OUTLINE()132 BOARD_OUTLINE::BOARD_OUTLINE()
133 {
134     outlineType = OTLN_BOARD;
135     single = false;
136     owner = UNOWNED;
137     parent = nullptr;
138     thickness = 0.0;
139     unit = UNIT_MM;
140 }
141 
142 
~BOARD_OUTLINE()143 BOARD_OUTLINE::~BOARD_OUTLINE()
144 {
145     clear();
146 }
147 
148 
GetOutlineType(void)149 IDF3::OUTLINE_TYPE BOARD_OUTLINE::GetOutlineType( void )
150 {
151     return outlineType;
152 }
153 
154 
readOutlines(std::istream & aBoardFile,IDF3::IDF_VERSION aIdfVersion)155 void BOARD_OUTLINE::readOutlines( std::istream& aBoardFile, IDF3::IDF_VERSION aIdfVersion )
156 {
157     // reads the outline data from a file
158     double x, y, ang;
159     double dLoc  = 1e-5;    // distances are equal when closer than 0.1 micron
160     bool comment = false;
161     bool quoted  = false;
162     bool closed  = false;
163     int idx      = 0;
164     int loopidx  = -1;
165     int tmp      = 0;
166     int npts     = 0;
167     std::string iline;
168     std::string entry;
169     std::stringstream tstr;
170     IDF_OUTLINE* op = nullptr;
171     IDF_SEGMENT* sp = nullptr;
172     IDF_POINT prePt;
173     IDF_POINT curPt;
174     std::streampos pos;
175 
176     // destroy any existing outline data
177     clearOutlines();
178 
179     while( aBoardFile.good() )
180     {
181         if( !FetchIDFLine( aBoardFile, iline, comment, pos ) )
182             continue;
183 
184         idx = 0;
185         GetIDFString( iline, entry, quoted, idx );
186 
187         if( quoted )
188         {
189             ostringstream ostr;
190 
191             ostr << "\n* invalid outline: RECORD 3, FIELD 1 of " <<
192                     GetOutlineTypeString( outlineType );
193             ostr << " is quoted\n";
194             ostr << "* line: '" << iline << "'";
195 
196             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
197         }
198 
199         // check for the end of the section
200         if( entry.size() >= 5 && CompareToken( ".END_", entry.substr( 0, 5 ) ) )
201         {
202             // rewind to the start of the last line; the routine invoking
203             // this is responsible for checking that the current '.END_ ...'
204             // matches the section header.
205             if( aBoardFile.eof() )
206                 aBoardFile.clear();
207 
208             aBoardFile.seekg( pos );
209 
210             if( outlines.size() > 0 )
211             {
212                 if( npts > 0 && !closed )
213                 {
214                     ostringstream ostr;
215                     ostr << "invalid outline (not closed)\n";
216                     ostr << "* file position: " << pos;
217 
218                     throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
219                 }
220 
221                 // verify winding
222                 if( !single )
223                 {
224                     if( !outlines.front()->IsCCW() )
225                     {
226                         ERROR_IDF << "invalid IDF3 file (BOARD_OUTLINE)\n";
227                         cerr << "* WARNING: first outline is not in CCW order\n";
228                         return;
229                     }
230 
231                     if( outlines.size() > 1 && outlines.back()->IsCCW() &&
232                         !outlines.back()->IsCircle() )
233                     {
234                         ERROR_IDF << "invalid IDF3 file (BOARD_OUTLINE)\n";
235                         cerr << "* WARNING: final cutout does not have points in CW order\n";
236                         cerr << "* file position: " << pos << "\n";
237                         return;
238                     }
239                 }
240             }
241 
242             return;
243         }
244 
245         tstr.clear();
246         tstr << entry;
247 
248         tstr >> tmp;
249 
250         if( tstr.fail() )
251         {
252             if( outlineType == OTLN_COMPONENT && CompareToken( "PROP", entry ) )
253             {
254                 aBoardFile.seekg( pos );
255                 return;
256             }
257 
258             do
259             {
260                 ostringstream ostr;
261 
262                 ostr << "\n* invalid outline: RECORD 3, FIELD 1 of " <<
263                 GetOutlineTypeString( outlineType );
264                 ostr << " is not numeric\n";
265                 ostr << "* line: '" << iline << "'\n";
266                 ostr << "* file position: " << pos;
267 
268                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
269 
270             } while( 0 );
271         }
272 
273         if( tmp != loopidx )
274         {
275             // index change
276             if( npts > 0 && !closed )
277             {
278                 ostringstream ostr;
279                 ostr << "invalid outline ( outline # " << loopidx << " not closed)\n";
280                 ostr << "* file position: " << pos;
281 
282                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
283             }
284 
285             if( tmp < 0 )
286             {
287                 ostringstream ostr;
288 
289                 ostr << "\n* invalid outline: RECORD 3, FIELD 1 of " <<
290                         GetOutlineTypeString( outlineType );
291                 ostr << " is invalid\n";
292                 ostr << "* line: '" << iline << "'\n";
293                 ostr << "* file position: " << pos;
294 
295                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
296             }
297 
298             if( loopidx == -1 )
299             {
300                 // first outline
301                 if( single )
302                 {
303                     // outline may have a Loop Index of 0 or 1
304                     if( tmp == 0 || tmp == 1 )
305                     {
306                         op = new IDF_OUTLINE;
307 
308                         if( op == nullptr )
309                         {
310                             clearOutlines();
311                             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
312                                               "memory allocation failed" ) );
313                         }
314 
315                         outlines.push_back( op );
316                         loopidx = tmp;
317                     }
318                     else
319                     {
320                         ostringstream ostr;
321 
322                         ostr << "\n* invalid outline: RECORD 3, FIELD 1 of " <<
323                                 GetOutlineTypeString( outlineType );
324                         ostr << " is invalid (must be 0 or 1)\n";
325                         ostr << "* line: '" << iline << "'\n";
326                         ostr << "* file position: " << pos;
327 
328                         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
329                     }
330                 }
331                 else
332                 {
333                     // outline *MUST* have a Loop Index of 0
334                     if( tmp != 0 )
335                     {
336                         ostringstream ostr;
337 
338                         ostr << "\n* invalid outline: RECORD 3, FIELD 1 of " <<
339                                 GetOutlineTypeString( outlineType );
340                         ostr << " is invalid (must be 0)\n";
341                         ostr << "* line: '" << iline << "'\n";
342                         ostr << "* file position: " << pos;
343 
344                         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
345                     }
346 
347                     op = new IDF_OUTLINE;
348 
349                     if( op == nullptr )
350                     {
351                         clearOutlines();
352                         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
353                                           "memory allocation failed" ) );
354                     }
355 
356                     outlines.push_back( op );
357                     loopidx = tmp;
358                 }
359             }
360             else
361             {
362                 // outline for cutout
363                 if( single )
364                 {
365                     ostringstream ostr;
366 
367                     ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType );
368                     ostr << " section may only have one outline\n";
369                     ostr << "* line: '" << iline << "'\n";
370                     ostr << "* file position: " << pos;
371 
372                     throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
373                 }
374 
375                 if( tmp - loopidx != 1 )
376                 {
377                     ostringstream ostr;
378 
379                     ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType );
380                     ostr << " section must have cutouts in numeric order from 1 onwards\n";
381                     ostr << "* line: '" << iline << "'\n";
382                     ostr << "* file position: " << pos;
383 
384                     throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
385                 }
386 
387                 // verify winding of previous outline
388                 if( ( loopidx == 0 && !op->IsCCW() )
389                     || ( loopidx > 0 && op->IsCCW() && !op->IsCircle() ) )
390                 {
391                     ostringstream ostr;
392 
393                     ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
394                     ostr << "* violation of loop point order rules by Loop Index " << loopidx <<
395                             "\n";
396                     ostr << "* line: '" << iline << "'\n";
397                     ostr << "* file position: " << pos;
398 
399                     throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
400                 }
401 
402                 op = new IDF_OUTLINE;
403 
404                 if( op == nullptr )
405                 {
406                     clearOutlines();
407                     throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
408                                       "memory allocation failed" ) );
409                 }
410 
411                 outlines.push_back( op );
412                 loopidx = tmp;
413             }
414             // end of index change code
415             npts = 0;
416             closed = false;
417         }
418 
419         if( op == nullptr )
420         {
421             ostringstream ostr;
422 
423             ostr << "\n* invalid outline: RECORD 3, FIELD 1 of " <<
424                     GetOutlineTypeString( outlineType );
425             ostr << " is invalid\n";
426             ostr << "* line: '" << iline << "'\n";
427             ostr << "* file position: " << pos;
428 
429             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
430         }
431 
432         if( !GetIDFString( iline, entry, quoted, idx ) )
433         {
434             ostringstream ostr;
435 
436             ostr << "\n* invalid outline: RECORD 3, FIELD 2 of ";
437             ostr << GetOutlineTypeString( outlineType ) << " does not exist\n";
438             ostr << "* line: '" << iline << "'\n";
439             ostr << "* file position: " << pos;
440 
441             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
442         }
443 
444         if( quoted )
445         {
446             ostringstream ostr;
447 
448             ostr << "\n* invalid outline: RECORD 3, FIELD 2 of ";
449             ostr << GetOutlineTypeString( outlineType ) << " must not be in quotes\n";
450             ostr << "* line: '" << iline << "'\n";
451             ostr << "* file position: " << pos;
452 
453             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
454         }
455 
456         tstr.clear();
457         tstr << entry;
458 
459         tstr >> x;
460 
461         if( tstr.fail() )
462         {
463             ostringstream ostr;
464 
465             ostr << "\n* invalid outline: RECORD 3, FIELD 2 of ";
466             ostr << GetOutlineTypeString( outlineType ) << " is an invalid X value\n";
467             ostr << "* line: '" << iline << "'\n";
468             ostr << "* file position: " << pos;
469 
470             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
471         }
472 
473         if( !GetIDFString( iline, entry, quoted, idx ) )
474         {
475             ostringstream ostr;
476 
477             ostr << "\n* invalid outline: RECORD 3, FIELD 3 of ";
478             ostr << GetOutlineTypeString( outlineType ) << " does not exist\n";
479             ostr << "* line: '" << iline << "'\n";
480             ostr << "* file position: " << pos;
481 
482             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
483         }
484 
485         if( quoted )
486         {
487             ostringstream ostr;
488 
489             ostr << "\n* invalid outline: RECORD 3, FIELD 3 of ";
490             ostr << GetOutlineTypeString( outlineType ) << " must not be in quotes\n";
491             ostr << "* line: '" << iline << "'\n";
492             ostr << "* file position: " << pos;
493 
494             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
495         }
496 
497         tstr.clear();
498         tstr << entry;
499 
500         tstr >> y;
501 
502         if( tstr.fail() )
503         {
504             ostringstream ostr;
505 
506             ostr << "\n* invalid outline: RECORD 3, FIELD 3 of ";
507             ostr << GetOutlineTypeString( outlineType ) << " is an invalid Y value\n";
508             ostr << "* line: '" << iline << "'\n";
509             ostr << "* file position: " << pos;
510 
511             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
512         }
513 
514         if( !GetIDFString( iline, entry, quoted, idx ) )
515         {
516             ostringstream ostr;
517 
518             ostr << "\n* invalid outline: RECORD 3, FIELD 4 of ";
519             ostr << GetOutlineTypeString( outlineType ) << " does not exist\n";
520             ostr << "* line: '" << iline << "'\n";
521             ostr << "* file position: " << pos;
522 
523             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
524         }
525 
526         if( quoted )
527         {
528             ostringstream ostr;
529 
530             ostr << "\n* invalid outline: RECORD 3, FIELD 4 of ";
531             ostr << GetOutlineTypeString( outlineType ) << " must not be in quotes\n";
532             ostr << "* line: '" << iline << "'\n";
533             ostr << "* file position: " << pos;
534 
535             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
536         }
537 
538         tstr.clear();
539         tstr << entry;
540 
541         tstr >> ang;
542 
543         if( tstr.fail() )
544         {
545             ostringstream ostr;
546 
547             ostr << "\n* invalid outline: RECORD 3, FIELD 4 of ";
548             ostr << GetOutlineTypeString( outlineType ) << " is not a valid angle\n";
549             ostr << "* line: '" << iline << "'\n";
550             ostr << "* file position: " << pos;
551 
552             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
553         }
554 
555         // the line was successfully read; convert to mm if necessary
556         if( unit == UNIT_THOU )
557         {
558             x *= IDF_THOU_TO_MM;
559             y *= IDF_THOU_TO_MM;
560         }
561         else if( ( aIdfVersion == IDF_V2 ) && ( unit == UNIT_TNM ) )
562         {
563             x *= IDF_TNM_TO_MM;
564             y *= IDF_TNM_TO_MM;
565         }
566         else if( unit != UNIT_MM )
567         {
568             ostringstream ostr;
569             ostr << "\n* BUG: invalid UNIT type: " << unit;
570 
571             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
572         }
573 
574         if( npts++ == 0 )
575         {
576             // first point
577             prePt.x = x;
578             prePt.y = y;
579 
580             // ensure that the first point is not an arc specification
581             if( ang < -MIN_ANG || ang > MIN_ANG )
582             {
583                 ostringstream ostr;
584 
585                 ostr << "\n* invalid outline: RECORD 3 of ";
586                 ostr << GetOutlineTypeString( outlineType ) << "\n";
587                 ostr << "* violation: first point of an outline has a non-zero angle\n";
588                 ostr << "* line: '" << iline << "'\n";
589                 ostr << "* file position: " << pos;
590 
591                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
592             }
593         }
594         else
595         {
596             // Nth point
597             if( closed )
598             {
599                 ostringstream ostr;
600 
601                 ostr << "\n* invalid outline: RECORD 3 of ";
602                 ostr << GetOutlineTypeString( outlineType ) << "\n";
603                 ostr << "* violation: adding a segment to a closed outline\n";
604                 ostr << "* line: '" << iline << "'\n";
605                 ostr << "* file position: " << pos;
606 
607                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
608             }
609 
610             curPt.x = x;
611             curPt.y = y;
612 
613             if( ang > -MIN_ANG && ang < MIN_ANG )
614             {
615                 sp = new IDF_SEGMENT( prePt, curPt );
616             }
617             else
618             {
619                 sp = new IDF_SEGMENT( prePt, curPt, ang, false );
620             }
621 
622             if( sp == nullptr )
623             {
624                 clearOutlines();
625                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
626                                   "memory allocation failed" ) );
627             }
628 
629             if( sp->IsCircle() )
630             {
631                 // this is  a circle; the loop is closed
632                 if( op->size() != 0 )
633                 {
634                     delete sp;
635 
636                     ostringstream ostr;
637 
638                     ostr << "\n* invalid outline: RECORD 3 of ";
639                     ostr << GetOutlineTypeString( outlineType ) << "\n";
640                     ostr << "* violation: adding a circle to a non-empty outline\n";
641                     ostr << "* line: '" << iline << "'\n";
642                     ostr << "* file position: " << pos;
643 
644                     throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
645                 }
646 
647                 closed = true;
648             }
649             else if( op->size() != 0 )
650             {
651                 if( curPt.Matches( op->front()->startPoint, dLoc ) )
652                     closed = true;
653             }
654 
655             op->push( sp );
656             prePt.x = x;
657             prePt.y = y;
658         }
659     }
660 
661     // NOTE:
662     // 1. ideally we would ensure that there are no arcs with a radius of 0
663 
664     throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
665                       "problems reading file (premature end of outline)" ) );
666 
667     return;
668 }
669 
670 
writeComments(std::ostream & aBoardFile)671 bool BOARD_OUTLINE::writeComments( std::ostream& aBoardFile )
672 {
673     if( comments.empty() )
674         return true;
675 
676     list< string >::const_iterator itS = comments.begin();
677     list< string >::const_iterator itE = comments.end();
678 
679     while( itS != itE )
680     {
681         aBoardFile << "# " << *itS << "\n";
682         ++itS;
683     }
684 
685     return !aBoardFile.fail();
686 }
687 
688 
writeOwner(std::ostream & aBoardFile)689 bool BOARD_OUTLINE::writeOwner( std::ostream& aBoardFile )
690 {
691     switch( owner )
692     {
693     case ECAD:
694         aBoardFile << "ECAD\n";
695         break;
696 
697     case MCAD:
698         aBoardFile << "MCAD\n";
699         break;
700 
701     default:
702         aBoardFile << "UNOWNED\n";
703         break;
704     }
705 
706     return !aBoardFile.fail();
707 }
708 
709 
writeOutline(std::ostream & aBoardFile,IDF_OUTLINE * aOutline,size_t aIndex)710 void BOARD_OUTLINE::writeOutline( std::ostream& aBoardFile, IDF_OUTLINE* aOutline, size_t aIndex )
711 {
712     std::list<IDF_SEGMENT*>::iterator bo;
713     std::list<IDF_SEGMENT*>::iterator eo;
714 
715     if( !aOutline )
716         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
717                           "\n* BUG: nullptr outline pointer" ) );
718 
719     if( aOutline->size() == 1 )
720     {
721         if( !aOutline->front()->IsCircle() )
722             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
723                               "bad outline (single segment item, not circle)" ) );
724 
725         if( single )
726             aIndex = 0;
727 
728         // NOTE: a circle always has an angle of 360, never -360,
729         // otherwise SolidWorks chokes on the file.
730         if( unit != UNIT_THOU )
731         {
732             aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
733             << aOutline->front()->startPoint.x << " "
734             << aOutline->front()->startPoint.y << " 0\n";
735 
736             aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
737             << aOutline->front()->endPoint.x << " "
738             << aOutline->front()->endPoint.y << " 360\n";
739         }
740         else
741         {
742             aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
743             << (aOutline->front()->startPoint.x / IDF_THOU_TO_MM) << " "
744             << (aOutline->front()->startPoint.y / IDF_THOU_TO_MM) << " 0\n";
745 
746             aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
747             << (aOutline->front()->endPoint.x / IDF_THOU_TO_MM) << " "
748             << (aOutline->front()->endPoint.y / IDF_THOU_TO_MM) << " 360\n";
749         }
750 
751         return;
752     }
753 
754     if( single )
755     {
756         // only indices 0 (CCW) and 1 (CW) are valid; set the index according to
757         // the outline's winding
758         if( aOutline->IsCCW() )
759             aIndex = 0;
760         else
761             aIndex = 1;
762     }
763 
764 
765     // check if we must reverse things
766     if( ( aOutline->IsCCW() && ( aIndex > 0 ) ) || ( ( !aOutline->IsCCW() ) && ( aIndex == 0 ) ) )
767     {
768         eo  = aOutline->begin();
769         bo  = aOutline->end();
770         --bo;
771 
772         // ensure that the very last point is the same as the very first point
773         if( aOutline->size() > 1 )
774         {
775             std::list<IDF_SEGMENT*>::iterator to = eo;
776             ++to;
777             (*to)->startPoint = (*eo)->endPoint;
778         }
779 
780         // for the first item we write out both points
781         if( unit != UNIT_THOU )
782         {
783             if( aOutline->front()->angle < MIN_ANG && aOutline->front()->angle > -MIN_ANG )
784             {
785                 aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 5 )
786                            << aOutline->front()->endPoint.x << " " << aOutline->front()->endPoint.y
787                            << " 0\n";
788 
789                 aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 5 )
790                            << aOutline->front()->startPoint.x << " "
791                            << aOutline->front()->startPoint.y << " 0\n";
792             }
793             else
794             {
795                 aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 5 )
796                            << aOutline->front()->endPoint.x << " " << aOutline->front()->endPoint.y
797                            << " 0\n";
798 
799                 aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 5 )
800                            << aOutline->front()->startPoint.x << " "
801                            << aOutline->front()->startPoint.y << " " << setprecision( 3 )
802                            << -aOutline->front()->angle << "\n";
803             }
804         }
805         else
806         {
807             if( aOutline->front()->angle < MIN_ANG && aOutline->front()->angle > -MIN_ANG )
808             {
809                 aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 1 )
810                            << ( aOutline->front()->endPoint.x / IDF_THOU_TO_MM ) << " "
811                            << ( aOutline->front()->endPoint.y / IDF_THOU_TO_MM ) << " 0\n";
812 
813                 aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 1 )
814                            << ( aOutline->front()->startPoint.x / IDF_THOU_TO_MM ) << " "
815                            << ( aOutline->front()->startPoint.y / IDF_THOU_TO_MM ) << " 0\n";
816             }
817             else
818             {
819                 aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 1 )
820                            << ( aOutline->front()->endPoint.x / IDF_THOU_TO_MM ) << " "
821                            << ( aOutline->front()->endPoint.y / IDF_THOU_TO_MM ) << " 0\n";
822 
823                 aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 1 )
824                            << ( aOutline->front()->startPoint.x / IDF_THOU_TO_MM ) << " "
825                            << ( aOutline->front()->startPoint.y / IDF_THOU_TO_MM ) << " "
826                            << setprecision( 3 ) << -aOutline->front()->angle << "\n";
827             }
828         }
829 
830         // for all other segments we only write out the start point
831         while( bo != eo )
832         {
833             if( unit != UNIT_THOU )
834             {
835                 if( ( *bo )->angle < MIN_ANG && ( *bo )->angle > -MIN_ANG )
836                 {
837                     aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 5 )
838                                << ( *bo )->startPoint.x << " " << ( *bo )->startPoint.y << " 0\n";
839                 }
840                 else
841                 {
842                     aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 5 )
843                                << ( *bo )->startPoint.x << " " << ( *bo )->startPoint.y << " "
844                                << setprecision( 3 ) << -( *bo )->angle << "\n";
845                 }
846             }
847             else
848             {
849                 if( ( *bo )->angle < MIN_ANG && ( *bo )->angle > -MIN_ANG )
850                 {
851                     aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 1 )
852                                << ( ( *bo )->startPoint.x / IDF_THOU_TO_MM ) << " "
853                                << ( ( *bo )->startPoint.y / IDF_THOU_TO_MM ) << " 0\n";
854                 }
855                 else
856                 {
857                     aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 1 )
858                                << ( ( *bo )->startPoint.x / IDF_THOU_TO_MM ) << " "
859                                << ( ( *bo )->startPoint.y / IDF_THOU_TO_MM ) << " "
860                                << setprecision( 3 ) << -( *bo )->angle << "\n";
861                 }
862             }
863 
864             --bo;
865         }
866     }
867     else
868     {
869         // ensure that the very last point is the same as the very first point
870         if( aOutline->size() > 1 )
871             aOutline->back()-> endPoint = aOutline->front()->startPoint;
872 
873         bo  = aOutline->begin();
874         eo  = aOutline->end();
875 
876         // for the first item we write out both points
877         if( unit != UNIT_THOU )
878         {
879             if( ( *bo )->angle < MIN_ANG && ( *bo )->angle > -MIN_ANG )
880             {
881                 aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 5 )
882                            << ( *bo )->startPoint.x << " " << ( *bo )->startPoint.y << " 0\n";
883 
884                 aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 5 )
885                            << ( *bo )->endPoint.x << " " << ( *bo )->endPoint.y << " 0\n";
886             }
887             else
888             {
889                 aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 5 )
890                            << ( *bo )->startPoint.x << " " << ( *bo )->startPoint.y << " 0\n";
891 
892                 aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 5 )
893                            << ( *bo )->endPoint.x << " " << ( *bo )->endPoint.y << " "
894                            << setprecision( 3 ) << ( *bo )->angle << "\n";
895             }
896         }
897         else
898         {
899             if( ( *bo )->angle < MIN_ANG && ( *bo )->angle > -MIN_ANG )
900             {
901                 aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 1 )
902                            << ( ( *bo )->startPoint.x / IDF_THOU_TO_MM ) << " "
903                            << ( ( *bo )->startPoint.y / IDF_THOU_TO_MM ) << " 0\n";
904 
905                 aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 1 )
906                            << ( ( *bo )->endPoint.x / IDF_THOU_TO_MM ) << " "
907                            << ( ( *bo )->endPoint.y / IDF_THOU_TO_MM ) << " 0\n";
908             }
909             else
910             {
911                 aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 1 )
912                            << ( ( *bo )->startPoint.x / IDF_THOU_TO_MM ) << " "
913                            << ( ( *bo )->startPoint.y / IDF_THOU_TO_MM ) << " 0\n";
914 
915                 aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 1 )
916                            << ( ( *bo )->endPoint.x / IDF_THOU_TO_MM ) << " "
917                            << ( ( *bo )->endPoint.y / IDF_THOU_TO_MM ) << " " << setprecision( 3 )
918                            << ( *bo )->angle << "\n";
919             }
920         }
921 
922         ++bo;
923 
924         // for all other segments we only write out the last point
925         while( bo != eo )
926         {
927             if( unit != UNIT_THOU )
928             {
929                 if( ( *bo )->angle < MIN_ANG && ( *bo )->angle > -MIN_ANG )
930                 {
931                     aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 5 )
932                                << ( *bo )->endPoint.x << " " << ( *bo )->endPoint.y << " 0\n";
933                 }
934                 else
935                 {
936                     aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 5 )
937                                << ( *bo )->endPoint.x << " " << ( *bo )->endPoint.y << " "
938                                << setprecision( 3 ) << ( *bo )->angle << "\n";
939                 }
940             }
941             else
942             {
943                 if( ( *bo )->angle < MIN_ANG && ( *bo )->angle > -MIN_ANG )
944                 {
945                     aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 1 )
946                                << ( ( *bo )->endPoint.x / IDF_THOU_TO_MM ) << " "
947                                << ( ( *bo )->endPoint.y / IDF_THOU_TO_MM ) << " 0\n";
948                 }
949                 else
950                 {
951                     aBoardFile << aIndex << " " << setiosflags( ios::fixed ) << setprecision( 1 )
952                                << ( ( *bo )->endPoint.x / IDF_THOU_TO_MM ) << " "
953                                << ( ( *bo )->endPoint.y / IDF_THOU_TO_MM ) << " "
954                                << setprecision( 3 ) << ( *bo )->angle << "\n";
955                 }
956             }
957 
958             ++bo;
959         }
960     }
961 }
962 
963 
writeOutlines(std::ostream & aBoardFile)964 void BOARD_OUTLINE::writeOutlines( std::ostream& aBoardFile )
965 {
966     if( outlines.empty() )
967         return;
968 
969     int idx = 0;
970     std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
971     std::list< IDF_OUTLINE* >::iterator itE = outlines.end();
972 
973     while( itS != itE )
974     {
975         writeOutline( aBoardFile, *itS, idx++ );
976         ++itS;
977     }
978 }
979 
980 
SetUnit(IDF3::IDF_UNIT aUnit)981 bool BOARD_OUTLINE::SetUnit( IDF3::IDF_UNIT aUnit )
982 {
983     // note: although UNIT_TNM is accepted here without reservation,
984     // this can only affect data being read from a file.
985     if( aUnit != UNIT_MM && aUnit != UNIT_THOU && aUnit != UNIT_TNM )
986     {
987         ostringstream ostr;
988         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
989         ostr << "* BUG: invalid IDF UNIT (must be one of UNIT_MM or UNIT_THOU): " << aUnit << "\n";
990         ostr << "* outline type: " << GetOutlineTypeString( outlineType );
991         errormsg = ostr.str();
992 
993         return false;
994     }
995 
996     unit = aUnit;
997     return true;
998 }
999 
1000 
GetUnit(void)1001 IDF3::IDF_UNIT BOARD_OUTLINE::GetUnit( void )
1002 {
1003     return unit;
1004 }
1005 
1006 
setThickness(double aThickness)1007 bool BOARD_OUTLINE::setThickness( double aThickness )
1008 {
1009     if( aThickness < 0.0 )
1010     {
1011         ostringstream ostr;
1012         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
1013         ostr << "* BUG: aThickness < 0.0\n";
1014         ostr << "* outline type: " << GetOutlineTypeString( outlineType );
1015         errormsg = ostr.str();
1016 
1017         return false;
1018     }
1019 
1020     thickness = aThickness;
1021     return true;
1022 }
1023 
1024 
SetThickness(double aThickness)1025 bool BOARD_OUTLINE::SetThickness( double aThickness )
1026 {
1027 #ifndef DISABLE_IDF_OWNERSHIP
1028     if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
1029         return false;
1030 #endif
1031 
1032     return setThickness( aThickness );
1033 }
1034 
1035 
GetThickness(void)1036 double BOARD_OUTLINE::GetThickness( void )
1037 {
1038     return thickness;
1039 }
1040 
readData(std::istream & aBoardFile,const std::string & aHeader,IDF3::IDF_VERSION aIdfVersion)1041 void BOARD_OUTLINE::readData( std::istream& aBoardFile, const std::string& aHeader,
1042                               IDF3::IDF_VERSION aIdfVersion )
1043 {
1044     //  BOARD_OUTLINE (PANEL_OUTLINE)
1045     //      .BOARD_OUTLINE  [OWNER]
1046     //      [thickness]
1047     //      [outlines]
1048 
1049     // check RECORD 1
1050     std::string token;
1051     bool quoted = false;
1052     int  idx = 0;
1053     std::streampos pos;
1054 
1055     pos = aBoardFile.tellg();
1056 
1057     if( !GetIDFString( aHeader, token, quoted, idx ) )
1058         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1059                           "invalid invocation: blank header line" ) );
1060 
1061     if( quoted )
1062     {
1063         ostringstream ostr;
1064 
1065         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1066         ostr << "* violation: section names may not be in quotes\n";
1067         ostr << "* line: '" << aHeader << "'\n";
1068         ostr << "* file position: " << pos;
1069 
1070         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1071     }
1072 
1073     if( !CompareToken( ".BOARD_OUTLINE", token ) )
1074     {
1075         ostringstream ostr;
1076 
1077         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1078         ostr << "* violation: not a board outline\n";
1079         ostr << "* line: '" << aHeader << "'\n";
1080         ostr << "* file position: " << pos;
1081 
1082         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1083     }
1084 
1085     if( !GetIDFString( aHeader, token, quoted, idx ) )
1086     {
1087         if( aIdfVersion > IDF_V2 )
1088             ERROR_IDF << "no OWNER; setting to UNOWNED\n";
1089 
1090         owner = UNOWNED;
1091     }
1092     else
1093     {
1094         if( !ParseOwner( token, owner ) )
1095         {
1096             ERROR_IDF << "invalid OWNER (reverting to UNOWNED): " << token << "\n";
1097             owner = UNOWNED;
1098         }
1099     }
1100 
1101     // check RECORD 2
1102     std::string iline;
1103     bool comment = false;
1104     while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
1105 
1106     if( ( !aBoardFile.good() && !aBoardFile.eof() ) || iline.empty() )
1107     {
1108         ostringstream ostr;
1109 
1110         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1111         ostr << "* violation: premature end\n";
1112         ostr << "* file position: " << pos;
1113 
1114         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1115     }
1116 
1117     idx = 0;
1118 
1119     if( comment )
1120     {
1121         ostringstream ostr;
1122 
1123         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1124         ostr << "* violation: comment within .BOARD_OUTLINE section\n";
1125         ostr << "* line: '" << iline << "'\n";
1126         ostr << "* file position: " << pos;
1127 
1128         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1129     }
1130 
1131     if( !GetIDFString( iline, token, quoted, idx ) )
1132     {
1133         ostringstream ostr;
1134 
1135         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1136         ostr << "* violation: no thickness specified\n";
1137         ostr << "* file position: " << pos;
1138 
1139         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1140     }
1141 
1142     std::stringstream teststr;
1143     teststr << token;
1144 
1145     teststr >> thickness;
1146 
1147     if( teststr.fail() )
1148     {
1149         ostringstream ostr;
1150 
1151         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1152         ostr << "* violation: invalid RECORD 2 (thickness)\n";
1153         ostr << "* line: '" << iline << "'\n";
1154         ostr << "* file position: " << pos;
1155 
1156         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1157     }
1158 
1159     if( unit == UNIT_THOU )
1160     {
1161         thickness *= IDF_THOU_TO_MM;
1162     }
1163     else if( ( aIdfVersion == IDF_V2 ) && ( unit == UNIT_TNM ) )
1164     {
1165         thickness *= IDF_TNM_TO_MM;
1166     }
1167     else if( unit != UNIT_MM )
1168     {
1169         ostringstream ostr;
1170         ostr << "\n* BUG: invalid UNIT type: " << unit;
1171 
1172         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1173     }
1174 
1175     // for some unknown reason IDF allows 0 or negative thickness, but this
1176     // is a problem so we fix it here
1177     if( thickness <= 0.0 )
1178     {
1179         if( thickness == 0.0 )
1180         {
1181             ERROR_IDF << "\n* WARNING: setting board thickness to default 1.6mm (";
1182             cerr << thickness << ")\n";
1183             thickness = 1.6;
1184         }
1185         else
1186         {
1187             thickness = -thickness;
1188             ERROR_IDF << "\n* WARNING: setting board thickness to positive number (";
1189             cerr << thickness << ")\n";
1190         }
1191     }
1192 
1193     // read RECORD 3 values
1194     readOutlines( aBoardFile, aIdfVersion );
1195 
1196     // check RECORD 4
1197     while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
1198 
1199     if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
1200     {
1201         ostringstream ostr;
1202 
1203         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1204         ostr << "* violation: premature end\n";
1205         ostr << "* file position: " << pos;
1206 
1207         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1208     }
1209 
1210     idx = 0;
1211 
1212     if( comment )
1213     {
1214         ostringstream ostr;
1215 
1216         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1217         ostr << "* violation: comment within section\n";
1218         ostr << "* line: '" << iline << "'\n";
1219         ostr << "* file position: " << pos;
1220 
1221         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1222     }
1223 
1224     if( !CompareToken( ".END_BOARD_OUTLINE", iline ) )
1225     {
1226         ostringstream ostr;
1227 
1228         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1229         ostr << "* violation: no .END_BOARD_OUTLINE found\n";
1230         ostr << "* file position: " << pos;
1231 
1232         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1233     }
1234 }
1235 
1236 
writeData(std::ostream & aBoardFile)1237 void BOARD_OUTLINE::writeData( std::ostream& aBoardFile )
1238 {
1239     writeComments( aBoardFile );
1240 
1241     // note: a BOARD_OUTLINE section is required, even if it is empty
1242     aBoardFile << ".BOARD_OUTLINE ";
1243 
1244     writeOwner( aBoardFile );
1245 
1246     if( unit != UNIT_THOU )
1247         aBoardFile << setiosflags( ios::fixed ) << setprecision( 5 ) << thickness << "\n";
1248     else
1249         aBoardFile << setiosflags( ios::fixed ) << setprecision( 1 )
1250                    << ( thickness / IDF_THOU_TO_MM ) << "\n";
1251 
1252     writeOutlines( aBoardFile );
1253 
1254     aBoardFile << ".END_BOARD_OUTLINE\n\n";
1255 }
1256 
clear(void)1257 void BOARD_OUTLINE::clear( void )
1258 {
1259     comments.clear();
1260     clearOutlines();
1261 
1262     owner = UNOWNED;
1263     return;
1264 }
1265 
1266 
Clear(void)1267 bool BOARD_OUTLINE::Clear( void )
1268 {
1269 #ifndef DISABLE_IDF_OWNERSHIP
1270     if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
1271         return false;
1272 #endif
1273 
1274     clear();
1275 
1276     return true;
1277 }
1278 
1279 
setParent(IDF3_BOARD * aParent)1280 void BOARD_OUTLINE::setParent( IDF3_BOARD* aParent )
1281 {
1282     parent = aParent;
1283 }
1284 
1285 
GetParent(void)1286 IDF3_BOARD* BOARD_OUTLINE::GetParent( void )
1287 {
1288     return parent;
1289 }
1290 
1291 
addOutline(IDF_OUTLINE * aOutline)1292 bool BOARD_OUTLINE::addOutline( IDF_OUTLINE* aOutline )
1293 {
1294     std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
1295     std::list< IDF_OUTLINE* >::iterator itE = outlines.end();
1296 
1297     try
1298     {
1299         while( itS != itE )
1300         {
1301             if( *itS == aOutline )
1302                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
1303                                   "duplicate outline pointer" ) );
1304 
1305             ++itS;
1306         }
1307 
1308         outlines.push_back( aOutline );
1309 
1310     }
1311     catch( const std::exception& e )
1312     {
1313         errormsg = e.what();
1314 
1315         return false;
1316     }
1317 
1318     return true;
1319 }
1320 
1321 
AddOutline(IDF_OUTLINE * aOutline)1322 bool BOARD_OUTLINE::AddOutline( IDF_OUTLINE* aOutline )
1323 {
1324 #ifndef DISABLE_IDF_OWNERSHIP
1325     if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
1326         return false;
1327 #endif
1328 
1329     return addOutline( aOutline );
1330 }
1331 
1332 
DelOutline(IDF_OUTLINE * aOutline)1333 bool BOARD_OUTLINE::DelOutline( IDF_OUTLINE* aOutline )
1334 {
1335     std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
1336     std::list< IDF_OUTLINE* >::iterator itE = outlines.end();
1337 
1338     if( !aOutline )
1339     {
1340         ostringstream ostr;
1341         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
1342         ostr << "* BUG: nullptr aOutline pointer\n";
1343         ostr << "* outline type: " << GetOutlineTypeString( outlineType );
1344         errormsg = ostr.str();
1345 
1346         return false;
1347     }
1348 
1349     if( outlines.empty() )
1350     {
1351         errormsg.clear();
1352         return false;
1353     }
1354 
1355     // if there are more than 1 outlines it makes no sense to delete
1356     // the first outline (board outline) since that would have the
1357     // undesirable effect of substituting a cutout outline as the board outline
1358     if( aOutline == outlines.front() )
1359     {
1360         if( outlines.size() > 1 )
1361         {
1362             ostringstream ostr;
1363             ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
1364             ostr << "* BUG: attempting to delete first outline in list\n";
1365             ostr << "* outline type: " << GetOutlineTypeString( outlineType );
1366             errormsg = ostr.str();
1367 
1368             return false;
1369         }
1370 
1371         outlines.clear();
1372         return true;
1373     }
1374 
1375     while( itS != itE )
1376     {
1377         if( *itS == aOutline )
1378         {
1379             outlines.erase( itS );
1380             return true;
1381         }
1382 
1383         ++itS;
1384     }
1385 
1386     errormsg.clear();
1387     return false;
1388 }
1389 
1390 
DelOutline(size_t aIndex)1391 bool BOARD_OUTLINE::DelOutline( size_t aIndex )
1392 {
1393     std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
1394 
1395     if( outlines.empty() )
1396     {
1397         errormsg.clear();
1398         return false;
1399     }
1400 
1401     if( aIndex >= outlines.size() )
1402     {
1403         ostringstream ostr;
1404         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
1405         ostr << "* BUG: index out of bounds (" << aIndex << " / " << outlines.size() << ")\n";
1406         ostr << "* outline type: " << GetOutlineTypeString( outlineType );
1407         errormsg = ostr.str();
1408 
1409         return false;
1410     }
1411 
1412     if( aIndex == 0 )
1413     {
1414         // if there are more than 1 outlines it makes no sense to delete
1415         // the first outline (board outline) since that would have the
1416         // undesirable effect of substituting a cutout outline as the board outline
1417         if( outlines.size() > 1 )
1418         {
1419             ostringstream ostr;
1420             ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
1421             ostr << "* BUG: attempting to delete first outline in list\n";
1422             ostr << "* outline type: " << GetOutlineTypeString( outlineType );
1423             errormsg = ostr.str();
1424 
1425             return false;
1426         }
1427 
1428         delete *itS;
1429         outlines.clear();
1430 
1431         return true;
1432     }
1433 
1434     for( ; aIndex > 0; --aIndex )
1435         ++itS;
1436 
1437     delete *itS;
1438     outlines.erase( itS );
1439 
1440     return true;
1441 }
1442 
1443 
GetOutlines(void)1444 const std::list< IDF_OUTLINE* >*const BOARD_OUTLINE::GetOutlines( void )
1445 {
1446     return &outlines;
1447 }
1448 
1449 
OutlinesSize(void)1450 size_t BOARD_OUTLINE::OutlinesSize( void )
1451 {
1452     return outlines.size();
1453 }
1454 
1455 
GetOutline(size_t aIndex)1456 IDF_OUTLINE* BOARD_OUTLINE::GetOutline( size_t aIndex )
1457 {
1458     if( aIndex >= outlines.size() )
1459     {
1460         ostringstream ostr;
1461         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
1462         ostr <<  "* aIndex (" << aIndex << ") is out of range (" << outlines.size() << ")";
1463         errormsg = ostr.str();
1464 
1465         return nullptr;
1466     }
1467 
1468     std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
1469 
1470     for( ; aIndex > 0; --aIndex )
1471         ++itS;
1472 
1473     return *itS;
1474 }
1475 
1476 
GetOwner(void)1477 IDF3::KEY_OWNER BOARD_OUTLINE::GetOwner( void )
1478 {
1479     return owner;
1480 }
1481 
1482 
SetOwner(IDF3::KEY_OWNER aOwner)1483 bool BOARD_OUTLINE::SetOwner( IDF3::KEY_OWNER aOwner )
1484 {
1485 #ifndef DISABLE_IDF_OWNERSHIP
1486     if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
1487         return false;
1488 #endif
1489 
1490     owner = aOwner;
1491     return true;
1492 }
1493 
1494 
IsSingle(void)1495 bool BOARD_OUTLINE::IsSingle( void )
1496 {
1497     return single;
1498 }
1499 
1500 
clearOutlines(void)1501 void BOARD_OUTLINE::clearOutlines( void )
1502 {
1503     std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
1504     std::list< IDF_OUTLINE* >::iterator itE = outlines.end();
1505 
1506     while( itS != itE )
1507     {
1508         delete *itS;
1509         ++itS;
1510     }
1511 
1512     outlines.clear();
1513 }
1514 
1515 
AddComment(const std::string & aComment)1516 void BOARD_OUTLINE::AddComment( const std::string& aComment )
1517 {
1518     if( aComment.empty() )
1519         return;
1520 
1521     comments.push_back( aComment );
1522 }
1523 
1524 
CommentsSize(void)1525 size_t BOARD_OUTLINE::CommentsSize( void )
1526 {
1527     return comments.size();
1528 }
1529 
1530 
GetComments(void)1531 std::list< std::string >* BOARD_OUTLINE::GetComments( void )
1532 {
1533     return &comments;
1534 }
1535 
1536 
GetComment(size_t aIndex)1537 const std::string* BOARD_OUTLINE::GetComment( size_t aIndex )
1538 {
1539     if( aIndex >= comments.size() )
1540         return nullptr;
1541 
1542     std::list< std::string >::iterator itS = comments.begin();
1543 
1544     for( ; aIndex > 0; --aIndex )
1545         ++itS;
1546 
1547     return &(*itS);
1548 }
1549 
1550 
DeleteComment(size_t aIndex)1551 bool  BOARD_OUTLINE::DeleteComment( size_t aIndex )
1552 {
1553     if( aIndex >= comments.size() )
1554         return false;
1555 
1556     std::list< std::string >::iterator itS = comments.begin();
1557 
1558     for( ; aIndex > 0; --aIndex )
1559         ++itS;
1560 
1561     comments.erase( itS );
1562     return true;
1563 }
1564 
1565 
ClearComments(void)1566 void  BOARD_OUTLINE::ClearComments( void )
1567 {
1568     comments.clear();
1569 }
1570 
1571 
OTHER_OUTLINE(IDF3_BOARD * aParent)1572 OTHER_OUTLINE::OTHER_OUTLINE( IDF3_BOARD* aParent )
1573 {
1574     setParent( aParent );
1575     outlineType = OTLN_OTHER;
1576     side = LYR_INVALID;
1577     single = false;
1578 }
1579 
1580 
SetOutlineIdentifier(const std::string & aUniqueID)1581 bool OTHER_OUTLINE::SetOutlineIdentifier( const std::string& aUniqueID )
1582 {
1583 #ifndef DISABLE_IDF_OWNERSHIP
1584     if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
1585         return false;
1586 #endif
1587 
1588     uniqueID = aUniqueID;
1589 
1590     return true;
1591 }
1592 
1593 
GetOutlineIdentifier(void)1594 const std::string& OTHER_OUTLINE::GetOutlineIdentifier( void )
1595 {
1596     return uniqueID;
1597 }
1598 
1599 
SetSide(IDF3::IDF_LAYER aSide)1600 bool OTHER_OUTLINE::SetSide( IDF3::IDF_LAYER aSide )
1601 {
1602 #ifndef DISABLE_IDF_OWNERSHIP
1603     if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
1604         return false;
1605 #endif
1606 
1607     switch( aSide )
1608     {
1609         case LYR_TOP:
1610         case LYR_BOTTOM:
1611             side = aSide;
1612             break;
1613 
1614         default:
1615             do
1616             {
1617                 ostringstream ostr;
1618                 ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
1619                 ostr << "* BUG: invalid side (" << aSide << "); must be one of TOP/BOTTOM\n";
1620                 ostr << "* outline type: " << GetOutlineTypeString( outlineType );
1621                 errormsg = ostr.str();
1622             } while( 0 );
1623 
1624             side = LYR_INVALID;
1625             return false;
1626 
1627             break;
1628     }
1629 
1630     return true;
1631 }
1632 
1633 
GetSide(void)1634 IDF3::IDF_LAYER OTHER_OUTLINE::GetSide( void )
1635 {
1636     return side;
1637 }
1638 
1639 
readData(std::istream & aBoardFile,const std::string & aHeader,IDF3::IDF_VERSION aIdfVersion)1640 void OTHER_OUTLINE::readData( std::istream& aBoardFile, const std::string& aHeader,
1641                               IDF3::IDF_VERSION aIdfVersion )
1642 {
1643     // OTHER_OUTLINE/VIA_KEEPOUT
1644     //     .OTHER_OUTLINE  [OWNER]
1645     //     [outline identifier] [thickness] [board side: Top/Bot] {not present in VA\IA KEEPOUT}
1646     //     [outline]
1647 
1648     // check RECORD 1
1649     std::string token;
1650     bool quoted = false;
1651     int  idx = 0;
1652     std::streampos pos = aBoardFile.tellg();
1653 
1654     if( !GetIDFString( aHeader, token, quoted, idx ) )
1655     {
1656         ostringstream ostr;
1657         ostr << "\n* BUG: invalid invocation: blank header line\n";
1658 
1659         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1660     }
1661 
1662     if( quoted )
1663     {
1664         ostringstream ostr;
1665 
1666         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1667         ostr << "* violation: section names must not be in quotes\n";
1668         ostr << "* line: '" << aHeader << "'\n";
1669         ostr << "* file position: " << pos;
1670 
1671         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1672     }
1673 
1674     if( outlineType == OTLN_OTHER )
1675     {
1676         if( !CompareToken( ".OTHER_OUTLINE", token ) )
1677         {
1678             ostringstream ostr;
1679 
1680             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1681             ostr << "* BUG: not an .OTHER outline\n";
1682 
1683             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1684         }
1685     }
1686     else
1687     {
1688         if( !CompareToken( ".VIA_KEEPOUT", token ) )
1689         {
1690             ostringstream ostr;
1691 
1692             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1693             ostr << "* BUG: not a .VIA_KEEPOUT outline\n";
1694 
1695             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1696         }
1697     }
1698 
1699     if( !GetIDFString( aHeader, token, quoted, idx ) )
1700     {
1701         if( aIdfVersion > IDF_V2 )
1702             ERROR_IDF << "no OWNER; setting to UNOWNED\n";
1703 
1704         owner = UNOWNED;
1705     }
1706     else
1707     {
1708         if( !ParseOwner( token, owner ) )
1709         {
1710             ERROR_IDF << "invalid OWNER (reverting to UNOWNED): " << token << "\n";
1711             owner = UNOWNED;
1712         }
1713     }
1714 
1715     std::string iline;
1716     bool comment = false;
1717 
1718     if( outlineType == OTLN_OTHER )
1719     {
1720         // check RECORD 2
1721         // [outline identifier] [thickness] [board side: Top/Bot]
1722         while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
1723 
1724         if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
1725         {
1726             ostringstream ostr;
1727 
1728             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1729             ostr << "* violation: premature end\n";
1730             ostr << "* file position: " << pos;
1731 
1732             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1733         }
1734 
1735         idx = 0;
1736         if( comment )
1737         {
1738             ostringstream ostr;
1739 
1740             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1741             ostr << "* violation: comment within .OTHER_OUTLINE section\n";
1742             ostr << "* line: '" << iline << "'\n";
1743             ostr << "* file position: " << pos;
1744 
1745             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1746         }
1747 
1748         if( !GetIDFString( iline, token, quoted, idx ) )
1749         {
1750             ostringstream ostr;
1751 
1752             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1753             ostr << "* violation: no outline identifier\n";
1754             ostr << "* line: '" << iline << "'\n";
1755             ostr << "* file position: " << pos;
1756 
1757             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1758         }
1759 
1760         uniqueID = token;
1761 
1762         if( !GetIDFString( iline, token, quoted, idx ) )
1763         {
1764             ostringstream ostr;
1765 
1766             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1767             ostr << "* violation: no thickness\n";
1768             ostr << "* line: '" << iline << "'\n";
1769             ostr << "* file position: " << pos;
1770 
1771             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1772         }
1773 
1774         std::stringstream teststr;
1775         teststr << token;
1776 
1777         teststr >> thickness;
1778 
1779         if( teststr.fail() )
1780         {
1781             ostringstream ostr;
1782 
1783             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1784             ostr << "* violation: invalid thickness\n";
1785             ostr << "* line: '" << iline << "'\n";
1786             ostr << "* file position: " << pos;
1787 
1788             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1789         }
1790 
1791         if( unit == UNIT_THOU )
1792         {
1793             thickness *= IDF_THOU_TO_MM;
1794         }
1795         else if( ( aIdfVersion == IDF_V2 ) && ( unit == UNIT_TNM ) )
1796         {
1797             thickness *= IDF_TNM_TO_MM;
1798         }
1799         else if( unit != UNIT_MM )
1800         {
1801             ostringstream ostr;
1802             ostr << "\n* BUG: invalid UNIT type: " << unit;
1803 
1804             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1805         }
1806 
1807         if( aIdfVersion == IDF_V2 )
1808         {
1809             side = LYR_TOP;
1810         }
1811         else
1812         {
1813             if( !GetIDFString( iline, token, quoted, idx ) )
1814             {
1815                 ostringstream ostr;
1816 
1817                 ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1818                 ostr << "* violation: no board side\n";
1819                 ostr << "* line: '" << iline << "'\n";
1820                 ostr << "* file position: " << pos;
1821 
1822                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1823             }
1824 
1825             if( !ParseIDFLayer( token, side ) || ( side != LYR_TOP && side != LYR_BOTTOM ) )
1826             {
1827                 ostringstream ostr;
1828 
1829                 ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1830                 ostr << "* violation: invalid side (must be TOP or BOTTOM only)\n";
1831                 ostr << "* line: '" << iline << "'\n";
1832                 ostr << "* file position: " << pos;
1833 
1834                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1835             }
1836         }
1837 
1838     }
1839 
1840     // read RECORD 3 values
1841     readOutlines( aBoardFile, aIdfVersion );
1842 
1843     // check RECORD 4
1844     while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
1845 
1846     if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
1847     {
1848         ostringstream ostr;
1849 
1850         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1851         ostr << "* violation: premature end\n";
1852         ostr << "* file position: " << pos;
1853 
1854         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1855     }
1856 
1857     idx = 0;
1858 
1859     if( comment )
1860     {
1861         ostringstream ostr;
1862 
1863         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1864         ostr << "* violation: comment within section\n";
1865         ostr << "* line: '" << iline << "'\n";
1866         ostr << "* file position: " << pos;
1867 
1868         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1869     }
1870 
1871     if( outlineType == OTLN_OTHER )
1872     {
1873         if( !CompareToken( ".END_OTHER_OUTLINE", iline ) )
1874         {
1875             ostringstream ostr;
1876 
1877             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1878             ostr << "* violation: no .END_OTHER_OUTLINE found\n";
1879             ostr << "* file position: " << pos;
1880 
1881             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1882         }
1883     }
1884     else
1885     {
1886         if( !CompareToken( ".END_VIA_KEEPOUT", iline ) )
1887         {
1888             ostringstream ostr;
1889 
1890             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
1891             ostr << "* violation: no .END_VIA_KEEPOUT found\n";
1892             ostr << "* file position: " << pos;
1893 
1894             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1895         }
1896     }
1897 }
1898 
1899 
writeData(std::ostream & aBoardFile)1900 void OTHER_OUTLINE::writeData( std::ostream& aBoardFile )
1901 {
1902     // this section is optional; do not write if not required
1903     if( outlines.empty() )
1904         return;
1905 
1906     writeComments( aBoardFile );
1907 
1908     // write RECORD 1
1909     if( outlineType == OTLN_OTHER )
1910         aBoardFile << ".OTHER_OUTLINE ";
1911     else
1912         aBoardFile << ".VIA_KEEPOUT ";
1913 
1914     writeOwner( aBoardFile );
1915 
1916     // write RECORD 2
1917     if( outlineType == OTLN_OTHER )
1918     {
1919         aBoardFile << "\"" << uniqueID << "\" ";
1920 
1921         if( unit != UNIT_THOU )
1922             aBoardFile << setiosflags( ios::fixed ) << setprecision( 5 ) << thickness << " ";
1923         else
1924             aBoardFile << setiosflags( ios::fixed ) << setprecision( 1 )
1925                        << ( thickness / IDF_THOU_TO_MM ) << " ";
1926 
1927         switch( side )
1928         {
1929         case LYR_TOP:
1930         case LYR_BOTTOM:
1931             WriteLayersText( aBoardFile, side );
1932             break;
1933 
1934         default:
1935             do
1936             {
1937                 ostringstream ostr;
1938                 ostr << "\n* invalid OTHER_OUTLINE side (neither top nor bottom): ";
1939                 ostr << side;
1940                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
1941             } while( 0 );
1942 
1943             break;
1944         }
1945     }
1946 
1947     // write RECORD 3
1948     writeOutlines( aBoardFile );
1949 
1950     // write RECORD 4
1951     if( outlineType == OTLN_OTHER )
1952         aBoardFile << ".END_OTHER_OUTLINE\n\n";
1953     else
1954         aBoardFile << ".END_VIA_KEEPOUT\n\n";
1955 }
1956 
1957 
Clear(void)1958 bool OTHER_OUTLINE::Clear( void )
1959 {
1960 #ifndef DISABLE_IDF_OWNERSHIP
1961     if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
1962         return false;
1963 #endif
1964 
1965     clear();
1966     side = LYR_INVALID;
1967     uniqueID.clear();
1968 
1969     return true;
1970 }
1971 
1972 
ROUTE_OUTLINE(IDF3_BOARD * aParent)1973 ROUTE_OUTLINE::ROUTE_OUTLINE( IDF3_BOARD* aParent )
1974 {
1975     setParent( aParent );
1976     outlineType = OTLN_ROUTE;
1977     single = true;
1978     layers = LYR_INVALID;
1979 }
1980 
1981 
SetLayers(IDF3::IDF_LAYER aLayer)1982 bool ROUTE_OUTLINE::SetLayers( IDF3::IDF_LAYER aLayer )
1983 {
1984 #ifndef DISABLE_IDF_OWNERSHIP
1985     if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
1986         return false;
1987 #endif
1988 
1989     layers = aLayer;
1990 
1991     return true;
1992 }
1993 
1994 
GetLayers(void)1995 IDF3::IDF_LAYER ROUTE_OUTLINE::GetLayers( void )
1996 {
1997     return layers;
1998 }
1999 
2000 
readData(std::istream & aBoardFile,const std::string & aHeader,IDF3::IDF_VERSION aIdfVersion)2001 void ROUTE_OUTLINE::readData( std::istream& aBoardFile, const std::string& aHeader,
2002                               IDF3::IDF_VERSION aIdfVersion )
2003 {
2004     //  ROUTE_OUTLINE (or ROUTE_KEEPOUT)
2005     //      .ROUTE_OUTLINE [OWNER]
2006     //      [layers]
2007     //      [outline]
2008 
2009     // check RECORD 1
2010     std::string token;
2011     bool quoted = false;
2012     int  idx = 0;
2013     std::streampos pos = aBoardFile.tellg();
2014 
2015     if( !GetIDFString( aHeader, token, quoted, idx ) )
2016     {
2017         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2018                           "\n* BUG: invalid invocation; blank header line" ) );
2019     }
2020 
2021     if( quoted )
2022     {
2023         ostringstream ostr;
2024 
2025         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2026         ostr << "* violation: section names must not be in quotes\n";
2027         ostr << "* line: '" << aHeader << "'\n";
2028         ostr << "* file position: " << pos;
2029 
2030         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2031     }
2032 
2033     if( outlineType == OTLN_ROUTE )
2034     {
2035         if( !CompareToken( ".ROUTE_OUTLINE", token ) )
2036             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2037                               "\n* BUG: not a ROUTE outline" ) );
2038     }
2039     else
2040     {
2041         if( !CompareToken( ".ROUTE_KEEPOUT", token ) )
2042             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2043                               "\n* BUG: not a ROUTE KEEPOUT outline" ) );
2044     }
2045 
2046     if( !GetIDFString( aHeader, token, quoted, idx ) )
2047     {
2048         if( aIdfVersion > IDF_V2 )
2049             ERROR_IDF << "no OWNER; setting to UNOWNED\n";
2050 
2051         owner = UNOWNED;
2052     }
2053     else
2054     {
2055         if( !ParseOwner( token, owner ) )
2056         {
2057             ERROR_IDF << "invalid OWNER (reverting to UNOWNED): " << token << "\n";
2058             owner = UNOWNED;
2059         }
2060     }
2061 
2062     // check RECORD 2
2063     // [layers: TOP, BOTTOM, BOTH, INNER, ALL]
2064     std::string iline;
2065     bool comment = false;
2066 
2067     if( aIdfVersion > IDF_V2 || outlineType == OTLN_ROUTE_KEEPOUT )
2068     {
2069         while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
2070 
2071         if( !aBoardFile.good() )
2072         {
2073             ostringstream ostr;
2074 
2075             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2076             ostr << "* violation: premature end\n";
2077             ostr << "* file position: " << pos;
2078 
2079             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2080         }
2081 
2082         idx = 0;
2083 
2084         if( comment )
2085         {
2086             ostringstream ostr;
2087 
2088             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2089             ostr << "* violation: comment within a section\n";
2090             ostr << "* line: '" << iline << "'\n";
2091             ostr << "* file position: " << pos;
2092 
2093             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2094         }
2095 
2096         if( !GetIDFString( iline, token, quoted, idx ) )
2097         {
2098             ostringstream ostr;
2099 
2100             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2101             ostr << "* violation: no layers specification\n";
2102             ostr << "* line: '" << iline << "'\n";
2103             ostr << "* file position: " << pos;
2104 
2105             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2106         }
2107 
2108         if( quoted )
2109         {
2110             ostringstream ostr;
2111 
2112             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2113             ostr << "* violation: layers specification must not be in quotes\n";
2114             ostr << "* line: '" << iline << "'\n";
2115             ostr << "* file position: " << pos;
2116 
2117             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2118         }
2119 
2120         if( !ParseIDFLayer( token, layers ) )
2121         {
2122             ostringstream ostr;
2123 
2124             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2125             ostr << "* violation: invalid layers specification\n";
2126             ostr << "* line: '" << iline << "'\n";
2127             ostr << "* file position: " << pos;
2128 
2129             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2130         }
2131 
2132         if( aIdfVersion == IDF_V2 )
2133         {
2134             if( layers == LYR_INNER || layers == LYR_ALL )
2135             {
2136                 ostringstream ostr;
2137 
2138                 ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2139                 ostr << "* violation: IDFv2 allows only TOP/BOTTOM/BOTH; layer was '";
2140                 ostr << token << "'\n";
2141                 ostr << "* line: '" << iline << "'\n";
2142                 ostr << "* file position: " << pos;
2143 
2144                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2145             }
2146         }
2147     }
2148     else
2149     {
2150         layers = LYR_ALL;
2151     }
2152 
2153     // read RECORD 3 values
2154     readOutlines( aBoardFile, aIdfVersion );
2155 
2156     // check RECORD 4
2157     while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
2158 
2159     if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
2160     {
2161         ostringstream ostr;
2162 
2163         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2164         ostr << "* violation: premature end\n";
2165         ostr << "* file position: " << pos;
2166 
2167         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2168     }
2169 
2170     idx = 0;
2171 
2172     if( comment )
2173     {
2174         ostringstream ostr;
2175 
2176         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2177         ostr << "* violation: comment within section\n";
2178         ostr << "* line: '" << iline << "'\n";
2179         ostr << "* file position: " << pos;
2180 
2181         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2182     }
2183 
2184     if( outlineType == OTLN_ROUTE )
2185     {
2186         if( !CompareToken( ".END_ROUTE_OUTLINE", iline ) )
2187         {
2188             ostringstream ostr;
2189 
2190             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2191             ostr << "* violation: no .END_ROUTE_OUTLINE found\n";
2192             ostr << "* file position: " << pos;
2193 
2194             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2195         }
2196     }
2197     else
2198     {
2199         if( !CompareToken( ".END_ROUTE_KEEPOUT", iline ) )
2200         {
2201             ostringstream ostr;
2202 
2203             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2204             ostr << "* violation: no .END_ROUTE_KEEPOUT found\n";
2205             ostr << "* file position: " << pos;
2206 
2207             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2208         }
2209     }
2210 }
2211 
2212 
writeData(std::ostream & aBoardFile)2213 void ROUTE_OUTLINE::writeData( std::ostream& aBoardFile )
2214 {
2215     // this section is optional; do not write if not required
2216     if( outlines.empty() )
2217         return;
2218 
2219     if( layers == LYR_INVALID )
2220         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2221                           "layer not specified" ) );
2222 
2223     writeComments( aBoardFile );
2224 
2225     // write RECORD 1
2226     if( outlineType == OTLN_ROUTE )
2227         aBoardFile << ".ROUTE_OUTLINE ";
2228     else
2229         aBoardFile << ".ROUTE_KEEPOUT ";
2230 
2231     writeOwner( aBoardFile );
2232 
2233     // write RECORD 2
2234     WriteLayersText( aBoardFile, layers );
2235     aBoardFile << "\n";
2236 
2237     // write RECORD 3
2238     writeOutlines( aBoardFile );
2239 
2240     // write RECORD 4
2241     if( outlineType == OTLN_ROUTE )
2242         aBoardFile << ".END_ROUTE_OUTLINE\n\n";
2243     else
2244         aBoardFile << ".END_ROUTE_KEEPOUT\n\n";
2245 }
2246 
2247 
Clear(void)2248 bool ROUTE_OUTLINE::Clear( void )
2249 {
2250 #ifndef DISABLE_IDF_OWNERSHIP
2251     if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
2252         return false;
2253 #endif
2254 
2255     clear();
2256     layers = LYR_INVALID;
2257 
2258     return true;
2259 }
2260 
2261 
PLACE_OUTLINE(IDF3_BOARD * aParent)2262 PLACE_OUTLINE::PLACE_OUTLINE( IDF3_BOARD* aParent )
2263 {
2264     setParent( aParent );
2265     outlineType = OTLN_PLACE;
2266     single = true;
2267     thickness = -1.0;
2268     side = LYR_INVALID;
2269 }
2270 
2271 
SetSide(IDF3::IDF_LAYER aSide)2272 bool PLACE_OUTLINE::SetSide( IDF3::IDF_LAYER aSide )
2273 {
2274 #ifndef DISABLE_IDF_OWNERSHIP
2275     if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
2276         return false;
2277 #endif
2278 
2279     switch( aSide )
2280     {
2281     case LYR_TOP:
2282     case LYR_BOTTOM:
2283     case LYR_BOTH:
2284         side = aSide;
2285         break;
2286 
2287     default:
2288         do
2289         {
2290             side = LYR_INVALID;
2291             ostringstream ostr;
2292             ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
2293             ostr << "* BUG: invalid layer (" << aSide << "): must be one of TOP/BOTTOM/BOTH\n";
2294             ostr << "* outline type: " << GetOutlineTypeString( outlineType );
2295             errormsg = ostr.str();
2296 
2297             return false;
2298         } while( 0 );
2299 
2300         break;
2301     }
2302 
2303     return true;
2304 }
2305 
2306 
GetSide(void)2307 IDF3::IDF_LAYER PLACE_OUTLINE::GetSide( void )
2308 {
2309     return side;
2310 }
2311 
2312 
SetMaxHeight(double aHeight)2313 bool PLACE_OUTLINE::SetMaxHeight( double aHeight )
2314 {
2315 #ifndef DISABLE_IDF_OWNERSHIP
2316     if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
2317         return false;
2318 #endif
2319 
2320     if( aHeight < 0.0 )
2321     {
2322         thickness = 0.0;
2323 
2324         do
2325         {
2326             ostringstream ostr;
2327             ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
2328             ostr << "* BUG: invalid height (" << aHeight << "): must be >= 0.0";
2329             ostr << "* outline type: " << GetOutlineTypeString( outlineType );
2330             errormsg = ostr.str();
2331 
2332             return false;
2333         } while( 0 );
2334     }
2335 
2336     thickness = aHeight;
2337     return true;
2338 }
2339 
2340 
GetMaxHeight(void)2341 double PLACE_OUTLINE::GetMaxHeight( void )
2342 {
2343     return thickness;
2344 }
2345 
2346 
readData(std::istream & aBoardFile,const std::string & aHeader,IDF3::IDF_VERSION aIdfVersion)2347 void PLACE_OUTLINE::readData( std::istream& aBoardFile, const std::string& aHeader,
2348                               IDF3::IDF_VERSION aIdfVersion )
2349 {
2350     //  PLACE_OUTLINE/KEEPOUT
2351     //      .PLACE_OUTLINE [OWNER]
2352     //      [board side: Top/Bot/Both] [height]
2353     //      [outline]
2354 
2355     // check RECORD 1
2356     std::string token;
2357     bool quoted = false;
2358     int  idx = 0;
2359     std::streampos pos = aBoardFile.tellg();
2360 
2361     if( !GetIDFString( aHeader, token, quoted, idx ) )
2362         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2363                           "\n* BUG: invalid invocation: blank header line\n" ) );
2364 
2365     if( quoted )
2366     {
2367         ostringstream ostr;
2368 
2369         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2370         ostr << "* violation: section name must not be in quotes\n";
2371         ostr << "* line: '" << aHeader << "'\n";
2372         ostr << "* file position: " << pos;
2373 
2374         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2375     }
2376 
2377     if( outlineType == OTLN_PLACE )
2378     {
2379         if( !CompareToken( ".PLACE_OUTLINE", token ) )
2380             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2381                               "\n* BUG: not a .PLACE_OUTLINE" ) );
2382     }
2383     else
2384     {
2385         if( !CompareToken( ".PLACE_KEEPOUT", token ) )
2386             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2387                               "\n* BUG: not a .PLACE_KEEPOUT" ) );
2388     }
2389 
2390     if( !GetIDFString( aHeader, token, quoted, idx ) )
2391     {
2392         if( aIdfVersion > IDF_V2 )
2393             ERROR_IDF << "no OWNER; setting to UNOWNED\n";
2394 
2395         owner = UNOWNED;
2396     }
2397     else
2398     {
2399         if( !ParseOwner( token, owner ) )
2400         {
2401             ERROR_IDF << "invalid OWNER (reverting to UNOWNED): " << token << "\n";
2402             owner = UNOWNED;
2403         }
2404     }
2405 
2406     // check RECORD 2
2407     // [board side: Top/Bot/Both] [height]
2408     std::string iline;
2409     bool comment = false;
2410 
2411     if( aIdfVersion > IDF_V2 || outlineType == OTLN_PLACE_KEEPOUT )
2412     {
2413         while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
2414 
2415         if( !aBoardFile.good() )
2416         {
2417             ostringstream ostr;
2418 
2419             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2420             ostr << "* violation: premature end\n";
2421             ostr << "* file position: " << pos;
2422 
2423             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2424         }
2425 
2426         idx = 0;
2427 
2428         if( comment )
2429         {
2430             ostringstream ostr;
2431 
2432             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2433             ostr << "* violation: comment within the section\n";
2434             ostr << "* line: '" << iline << "'\n";
2435             ostr << "* file position: " << pos;
2436 
2437             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2438         }
2439 
2440         if( !GetIDFString( iline, token, quoted, idx ) )
2441         {
2442             ostringstream ostr;
2443 
2444             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2445             ostr << "* violation: no board side information\n";
2446             ostr << "* line: '" << iline << "'\n";
2447             ostr << "* file position: " << pos;
2448 
2449             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2450         }
2451 
2452         if( !ParseIDFLayer( token, side ) ||
2453             ( side != LYR_TOP && side != LYR_BOTTOM && side != LYR_BOTH ) )
2454         {
2455             ostringstream ostr;
2456 
2457             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2458             ostr << "* violation: invalid board side: must be one of TOP/BOTTOM/BOTH\n";
2459             ostr << "* line: '" << iline << "'\n";
2460             ostr << "* file position: " << pos;
2461 
2462             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2463         }
2464 
2465         if( GetIDFString( iline, token, quoted, idx ) )
2466         {
2467             std::stringstream teststr;
2468             teststr << token;
2469 
2470             teststr >> thickness;
2471 
2472             if( teststr.fail() )
2473             {
2474                 ostringstream ostr;
2475 
2476                 ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2477                 ostr << "* violation: invalid height\n";
2478                 ostr << "* line: '" << iline << "'\n";
2479                 ostr << "* file position: " << pos;
2480 
2481                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2482             }
2483 
2484             if( thickness < 0.0 )
2485             {
2486                 ostringstream ostr;
2487 
2488                 ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2489                 ostr << "* violation: thickness < 0\n";
2490                 ostr << "* line: '" << iline << "'\n";
2491                 ostr << "* file position: " << pos;
2492 
2493                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2494             }
2495 
2496             if( unit == UNIT_THOU )
2497             {
2498                 thickness *= IDF_THOU_TO_MM;
2499             }
2500             else if( ( aIdfVersion == IDF_V2 ) && ( unit == UNIT_TNM ) )
2501             {
2502                 thickness *= IDF_TNM_TO_MM;
2503             }
2504             else if( unit != UNIT_MM )
2505             {
2506                 ostringstream ostr;
2507                 ostr << "\n* BUG: invalid UNIT type: " << unit;
2508 
2509                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2510             }
2511 
2512             if( thickness < 0.0 )
2513                 thickness = 0.0;
2514         }
2515         else
2516         {
2517             // for OTLN_PLACE, thickness may be omitted, but is required for OTLN_PLACE_KEEPOUT
2518             if( outlineType == OTLN_PLACE_KEEPOUT )
2519             {
2520                 ostringstream ostr;
2521 
2522                 ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2523                 ostr << "* violation: missing thickness\n";
2524                 ostr << "* line: '" << iline << "'\n";
2525                 ostr << "* file position: " << pos;
2526 
2527                 throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2528             }
2529 
2530             thickness = -1.0;
2531         }
2532     }
2533     else
2534     {
2535         side = LYR_TOP;
2536         thickness = 0.0;
2537     }
2538 
2539     // read RECORD 3 values
2540     readOutlines( aBoardFile, aIdfVersion );
2541 
2542     // check RECORD 4
2543     while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
2544 
2545     if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
2546     {
2547         ostringstream ostr;
2548 
2549         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2550         ostr << "* violation: premature end\n";
2551         ostr << "* file position: " << pos;
2552 
2553         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2554     }
2555 
2556     idx = 0;
2557 
2558     if( comment )
2559     {
2560         ostringstream ostr;
2561 
2562         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2563         ostr << "* violation: comment within section\n";
2564         ostr << "* line: '" << iline << "'\n";
2565         ostr << "* file position: " << pos;
2566 
2567         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2568     }
2569 
2570     if( outlineType == OTLN_PLACE )
2571     {
2572         if( !GetIDFString( iline, token, quoted, idx )
2573             || !CompareToken( ".END_PLACE_OUTLINE", token ) )
2574             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2575                               "invalid .PLACE_OUTLINE section: no .END_PLACE_OUTLINE found" ) );
2576     }
2577     else
2578     {
2579         if( !GetIDFString( iline, token, quoted, idx )
2580             || !CompareToken( ".END_PLACE_KEEPOUT", token ) )
2581             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2582                               "invalid .PLACE_KEEPOUT section: no .END_PLACE_KEEPOUT found" ) );
2583     }
2584 }
2585 
2586 
writeData(std::ostream & aBoardFile)2587 void PLACE_OUTLINE::writeData( std::ostream& aBoardFile )
2588 {
2589     // this section is optional; do not write if not required
2590     if( outlines.empty() )
2591         return;
2592 
2593     writeComments( aBoardFile );
2594 
2595     // write RECORD 1
2596     if( outlineType == OTLN_PLACE )
2597         aBoardFile << ".PLACE_OUTLINE ";
2598     else
2599         aBoardFile << ".PLACE_KEEPOUT ";
2600 
2601     writeOwner( aBoardFile );
2602 
2603     // write RECORD 2
2604     switch( side )
2605     {
2606     case LYR_TOP:
2607     case LYR_BOTTOM:
2608     case LYR_BOTH:
2609         WriteLayersText( aBoardFile, side );
2610         break;
2611 
2612     default:
2613         do
2614         {
2615             ostringstream ostr;
2616             ostr << "\n* invalid PLACE_OUTLINE/KEEPOUT side (";
2617             ostr << side << "); must be one of TOP/BOTTOM/BOTH";
2618             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2619         } while( 0 );
2620 
2621         break;
2622     }
2623 
2624     // thickness is optional for OTLN_PLACE, but mandatory for OTLN_PLACE_KEEPOUT
2625     if( thickness < 0.0 && outlineType == OTLN_PLACE_KEEPOUT )
2626     {
2627         aBoardFile << "\n";
2628     }
2629     else
2630     {
2631         aBoardFile << " ";
2632 
2633         if( unit != UNIT_THOU )
2634             aBoardFile << setiosflags( ios::fixed ) << setprecision( 5 ) << thickness << "\n";
2635         else
2636             aBoardFile << setiosflags( ios::fixed ) << setprecision( 1 )
2637                        << ( thickness / IDF_THOU_TO_MM ) << "\n";
2638     }
2639 
2640     // write RECORD 3
2641     writeOutlines( aBoardFile );
2642 
2643     // write RECORD 4
2644     if( outlineType == OTLN_PLACE )
2645         aBoardFile << ".END_PLACE_OUTLINE\n\n";
2646     else
2647         aBoardFile << ".END_PLACE_KEEPOUT\n\n";
2648 
2649     return;
2650 }
2651 
2652 
Clear(void)2653 bool PLACE_OUTLINE::Clear( void )
2654 {
2655 #ifndef DISABLE_IDF_OWNERSHIP
2656     if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
2657         return false;
2658 #endif
2659 
2660     clear();
2661     thickness = 0.0;
2662     side = LYR_INVALID;
2663 
2664     return true;
2665 }
2666 
2667 
ROUTE_KO_OUTLINE(IDF3_BOARD * aParent)2668 ROUTE_KO_OUTLINE::ROUTE_KO_OUTLINE( IDF3_BOARD* aParent )
2669     : ROUTE_OUTLINE( aParent )
2670 {
2671     outlineType = OTLN_ROUTE_KEEPOUT;
2672     return;
2673 }
2674 
2675 
PLACE_KO_OUTLINE(IDF3_BOARD * aParent)2676 PLACE_KO_OUTLINE::PLACE_KO_OUTLINE( IDF3_BOARD* aParent )
2677     : PLACE_OUTLINE( aParent )
2678 {
2679     outlineType = OTLN_PLACE_KEEPOUT;
2680     return;
2681 }
2682 
2683 
VIA_KO_OUTLINE(IDF3_BOARD * aParent)2684 VIA_KO_OUTLINE::VIA_KO_OUTLINE( IDF3_BOARD* aParent )
2685     : OTHER_OUTLINE( aParent )
2686 {
2687     single = true;
2688     outlineType = OTLN_VIA_KEEPOUT;
2689 }
2690 
2691 
GROUP_OUTLINE(IDF3_BOARD * aParent)2692 GROUP_OUTLINE::GROUP_OUTLINE( IDF3_BOARD* aParent )
2693 {
2694     setParent( aParent );
2695     outlineType = OTLN_GROUP_PLACE;
2696     thickness = 0.0;
2697     side = LYR_INVALID;
2698     single = true;
2699     return;
2700 }
2701 
2702 
SetSide(IDF3::IDF_LAYER aSide)2703 bool GROUP_OUTLINE::SetSide( IDF3::IDF_LAYER aSide )
2704 {
2705 #ifndef DISABLE_IDF_OWNERSHIP
2706     if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
2707         return false;
2708 #endif
2709 
2710     switch( aSide )
2711     {
2712     case LYR_TOP:
2713     case LYR_BOTTOM:
2714     case LYR_BOTH:
2715         side = aSide;
2716         break;
2717 
2718     default:
2719         do
2720         {
2721             ostringstream ostr;
2722             ostr << "invalid side (" << aSide << "); must be one of TOP/BOTTOM/BOTH\n";
2723             ostr << "* outline type: " << GetOutlineTypeString( outlineType );
2724             errormsg = ostr.str();
2725 
2726             return false;
2727         } while( 0 );
2728 
2729         break;
2730     }
2731 
2732     return true;
2733 }
2734 
2735 
GetSide(void)2736 IDF3::IDF_LAYER GROUP_OUTLINE::GetSide( void )
2737 {
2738     return side;
2739 }
2740 
2741 
SetGroupName(std::string aGroupName)2742 bool GROUP_OUTLINE::SetGroupName( std::string aGroupName )
2743 {
2744 #ifndef DISABLE_IDF_OWNERSHIP
2745     if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
2746         return false;
2747 #endif
2748 
2749     groupName = std::move( aGroupName );
2750 
2751     return true;
2752 }
2753 
2754 
GetGroupName(void)2755 const std::string& GROUP_OUTLINE::GetGroupName( void )
2756 {
2757     return groupName;
2758 }
2759 
2760 
readData(std::istream & aBoardFile,const std::string & aHeader,IDF3::IDF_VERSION aIdfVersion)2761 void GROUP_OUTLINE::readData( std::istream& aBoardFile, const std::string& aHeader,
2762                               IDF3::IDF_VERSION aIdfVersion )
2763 {
2764     //  Placement Group
2765     //      .PLACE_REGION [OWNER]
2766     //      [side: Top/Bot/Both ] [component group name]
2767     //      [outline]
2768 
2769     // check RECORD 1
2770     std::string token;
2771     bool quoted = false;
2772     int  idx = 0;
2773     std::streampos pos = aBoardFile.tellg();
2774 
2775     if( !GetIDFString( aHeader, token, quoted, idx ) )
2776         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2777                           "\n* BUG: invalid invocation: blank header line" ) );
2778 
2779     if( quoted )
2780     {
2781         ostringstream ostr;
2782 
2783         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2784         ostr << "* violation: section name must not be in quotes\n";
2785         ostr << "* line: '" << aHeader << "'\n";
2786         ostr << "* file position: " << pos;
2787 
2788         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2789     }
2790 
2791     if( !CompareToken( ".PLACE_REGION", token ) )
2792         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "\n* BUG: not a .PLACE_REGION" ) );
2793 
2794     if( !GetIDFString( aHeader, token, quoted, idx ) )
2795     {
2796         if( aIdfVersion > IDF_V2 )
2797             ERROR_IDF << "no OWNER; setting to UNOWNED\n";
2798 
2799         owner = UNOWNED;
2800     }
2801     else
2802     {
2803         if( !ParseOwner( token, owner ) )
2804         {
2805             ERROR_IDF << "invalid OWNER (reverting to UNOWNED): " << token << "\n";
2806             owner = UNOWNED;
2807         }
2808     }
2809 
2810     std::string iline;
2811     bool comment = false;
2812 
2813     // check RECORD 2
2814     // [side: Top/Bot/Both ] [component group name]
2815     while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
2816 
2817     if( !aBoardFile.good() )
2818     {
2819         ostringstream ostr;
2820 
2821         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2822         ostr << "* violation: premature end\n";
2823         ostr << "* file position: " << pos;
2824 
2825         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2826     }
2827 
2828     idx = 0;
2829 
2830     if( comment )
2831     {
2832         ostringstream ostr;
2833 
2834         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2835         ostr << "* violation: comment within section\n";
2836         ostr << "* line: '" << iline << "'\n";
2837         ostr << "* file position: " << pos;
2838 
2839         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2840     }
2841 
2842     if( !GetIDFString( iline, token, quoted, idx ) )
2843     {
2844         ostringstream ostr;
2845 
2846         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2847         ostr << "* violation: no board side specified\n";
2848         ostr << "* line: '" << iline << "'\n";
2849         ostr << "* file position: " << pos;
2850 
2851         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2852     }
2853 
2854     if( !ParseIDFLayer( token, side ) ||
2855         ( side != LYR_TOP && side != LYR_BOTTOM && side != LYR_BOTH ) )
2856     {
2857         ostringstream ostr;
2858 
2859         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2860         ostr << "* violation: invalid board side, must be one of TOP/BOTTOM/BOTH\n";
2861         ostr << "* line: '" << iline << "'\n";
2862         ostr << "* file position: " << pos;
2863 
2864         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2865     }
2866 
2867     if( !GetIDFString( iline, token, quoted, idx ) )
2868     {
2869         ostringstream ostr;
2870 
2871         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2872         ostr << "* violation: no outline identifier\n";
2873         ostr << "* line: '" << iline << "'\n";
2874         ostr << "* file position: " << pos;
2875 
2876         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2877     }
2878 
2879     groupName = token;
2880 
2881     // read RECORD 3 values
2882     readOutlines( aBoardFile, aIdfVersion );
2883 
2884     // check RECORD 4
2885     while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
2886 
2887     if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
2888     {
2889         ostringstream ostr;
2890 
2891         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2892         ostr << "* violation: premature end\n";
2893         ostr << "* file position: " << pos;
2894 
2895         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2896     }
2897 
2898     idx = 0;
2899 
2900     if( comment )
2901     {
2902         ostringstream ostr;
2903 
2904         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
2905         ostr << "* violation: comment within section\n";
2906         ostr << "* line: '" << iline << "'\n";
2907         ostr << "* file position: " << pos;
2908 
2909         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2910     }
2911 
2912     if( !GetIDFString( iline, token, quoted, idx ) || !CompareToken( ".END_PLACE_REGION", token ) )
2913         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
2914                           "\n* invalid .PLACE_REGION section: no .END_PLACE_REGION found" ) );
2915 }
2916 
2917 
writeData(std::ostream & aBoardFile)2918 void GROUP_OUTLINE::writeData( std::ostream& aBoardFile )
2919 {
2920     // this section is optional; do not write if not required
2921     if( outlines.empty() )
2922         return;
2923 
2924     writeComments( aBoardFile );
2925 
2926     // write RECORD 1
2927     aBoardFile << ".PLACE_REGION ";
2928 
2929     writeOwner( aBoardFile );
2930 
2931     // write RECORD 2
2932     switch( side )
2933     {
2934     case LYR_TOP:
2935     case LYR_BOTTOM:
2936     case LYR_BOTH:
2937         WriteLayersText( aBoardFile, side );
2938         break;
2939 
2940     default:
2941         do
2942         {
2943             ostringstream ostr;
2944             ostr << "\n* invalid PLACE_REGION side (must be TOP/BOTTOM/BOTH): ";
2945             ostr << side;
2946 
2947             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
2948         } while( 0 );
2949 
2950         break;
2951     }
2952 
2953     aBoardFile << " \"" << groupName << "\"\n";
2954 
2955     // write RECORD 3
2956     writeOutlines( aBoardFile );
2957 
2958     // write RECORD 4
2959     aBoardFile << ".END_PLACE_REGION\n\n";
2960 }
2961 
2962 
Clear(void)2963 bool GROUP_OUTLINE::Clear( void )
2964 {
2965 #ifndef DISABLE_IDF_OWNERSHIP
2966     if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
2967         return false;
2968 #endif
2969 
2970     clear();
2971     thickness = 0.0;
2972     side = LYR_INVALID;
2973     groupName.clear();
2974 
2975     return true;
2976 }
2977 
2978 
IDF3_COMP_OUTLINE(IDF3_BOARD * aParent)2979 IDF3_COMP_OUTLINE::IDF3_COMP_OUTLINE( IDF3_BOARD* aParent )
2980 {
2981     setParent( aParent );
2982     single = true;
2983     outlineType = OTLN_COMPONENT;
2984     compType = COMP_INVALID;
2985     refNum = 0;
2986     return;
2987 }
2988 
2989 
readProperties(std::istream & aLibFile)2990 void IDF3_COMP_OUTLINE::readProperties( std::istream& aLibFile )
2991 {
2992     bool quoted = false;
2993     bool comment = false;
2994     std::string iline;
2995     std::string token;
2996     std::streampos pos;
2997     std::string pname;      // property name
2998     std::string pval;       // property value
2999     int idx = 0;
3000 
3001     while( aLibFile.good() )
3002     {
3003         if( !FetchIDFLine( aLibFile, iline, comment, pos ) )
3004             continue;
3005 
3006         idx = 0;
3007 
3008         if( comment )
3009         {
3010             ostringstream ostr;
3011 
3012             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3013             ostr << "* violation: comment within section\n";
3014             ostr << "* line: '" << iline << "'\n";
3015             ostr << "* file position: " << pos;
3016 
3017             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3018         }
3019 
3020         if( !GetIDFString( iline, token, quoted, idx ) )
3021         {
3022             ostringstream ostr;
3023 
3024             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3025             ostr << "* violation: bad property section (no PROP)\n";
3026             ostr << "* line: '" << iline << "'\n";
3027             ostr << "* file position: " << pos;
3028 
3029             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3030         }
3031 
3032         if( quoted )
3033         {
3034             ostringstream ostr;
3035 
3036             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3037             ostr << "* violation: PROP or .END must not be quoted\n";
3038             ostr << "* line: '" << iline << "'\n";
3039             ostr << "* file position: " << pos;
3040 
3041             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3042         }
3043 
3044         if( token.size() >= 5 && CompareToken( ".END_", token.substr( 0, 5 ) ) )
3045         {
3046             if(aLibFile.eof())
3047                 aLibFile.clear();
3048 
3049             aLibFile.seekg( pos );
3050             return;
3051         }
3052 
3053         if( !CompareToken( "PROP", token ) )
3054         {
3055             ostringstream ostr;
3056 
3057             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3058             ostr << "* violation: expecting PROP or .END_ELECTRICAL\n";
3059             ostr << "* line: '" << iline << "'\n";
3060             ostr << "* file position: " << pos;
3061 
3062             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3063         }
3064 
3065         if( !GetIDFString( iline, token, quoted, idx ) )
3066         {
3067             ostringstream ostr;
3068 
3069             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3070             ostr << "* violation: no PROP name\n";
3071             ostr << "* line: '" << iline << "'\n";
3072             ostr << "* file position: " << pos;
3073 
3074             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3075         }
3076 
3077         pname = token;
3078 
3079         if( !GetIDFString( iline, token, quoted, idx ) )
3080         {
3081             ostringstream ostr;
3082 
3083             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3084             ostr << "* violation: no PROP value\n";
3085             ostr << "* line: '" << iline << "'\n";
3086             ostr << "* file position: " << pos;
3087 
3088             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3089         }
3090 
3091         pval = token;
3092 
3093         if( props.insert( pair< string, string >(pname, pval) ).second == false )
3094         {
3095             ostringstream ostr;
3096 
3097             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3098             ostr << "* violation: duplicate property name \"" << pname << "\"\n";
3099             ostr << "* line: '" << iline << "'\n";
3100             ostr << "* file position: " << pos;
3101 
3102             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3103         }
3104     }
3105 }
3106 
3107 
writeProperties(std::ostream & aLibFile)3108 bool IDF3_COMP_OUTLINE::writeProperties( std::ostream& aLibFile )
3109 {
3110     if( props.empty() )
3111         return true;
3112 
3113     std::map< std::string, std::string >::const_iterator itS = props.begin();
3114     std::map< std::string, std::string >::const_iterator itE = props.end();
3115 
3116     while( itS != itE )
3117     {
3118         aLibFile << "PROP " << "\"" << itS->first << "\" \""
3119         << itS->second << "\"\n";
3120         ++itS;
3121     }
3122 
3123     return !aLibFile.fail();
3124 }
3125 
3126 
readData(std::istream & aLibFile,const std::string & aHeader,IDF3::IDF_VERSION aIdfVersion)3127 void IDF3_COMP_OUTLINE::readData( std::istream& aLibFile, const std::string& aHeader,
3128                                   IDF3::IDF_VERSION aIdfVersion )
3129 {
3130     //  .ELECTRICAL/.MECHANICAL
3131     //  [GEOM] [PART] [UNIT] [HEIGHT]
3132     //  [outline]
3133     //  [PROP] [prop name] [prop value]
3134     // check RECORD 1
3135     std::string token;
3136     bool quoted = false;
3137     int  idx = 0;
3138     std::streampos pos = aLibFile.tellg();
3139 
3140     if( !GetIDFString( aHeader, token, quoted, idx ) )
3141         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
3142                           "\n* BUG: invalid invocation: blank header line" ) );
3143 
3144     if( quoted )
3145     {
3146         ostringstream ostr;
3147 
3148         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3149         ostr << "* violation: section name must not be in quotes\n";
3150         ostr << "* line: '" << aHeader << "'\n";
3151         ostr << "* file position: " << pos;
3152 
3153         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3154     }
3155 
3156     if( CompareToken( ".ELECTRICAL", token ) )
3157     {
3158         compType = COMP_ELEC;
3159     }
3160     else if( CompareToken( ".MECHANICAL", token ) )
3161     {
3162         compType = COMP_MECH;
3163     }
3164     else
3165     {
3166         ostringstream ostr;
3167 
3168         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3169         ostr << "* violation: expecting .ELECTRICAL or .MECHANICAL header\n";
3170         ostr << "* line: '" << aHeader << "'\n";
3171         ostr << "* file position: " << pos;
3172 
3173         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3174     }
3175 
3176     // check RECORD 2
3177     // [GEOM] [PART] [UNIT] [HEIGHT]
3178     std::string iline;
3179     bool comment = false;
3180 
3181     while( aLibFile.good() && !FetchIDFLine( aLibFile, iline, comment, pos ) );
3182 
3183     if( !aLibFile.good() )
3184     {
3185         ostringstream ostr;
3186 
3187         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3188         ostr << "* violation: premature end\n";
3189         ostr << "* file position: " << pos;
3190 
3191         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3192     }
3193 
3194     idx = 0;
3195 
3196     if( comment )
3197     {
3198         ostringstream ostr;
3199 
3200         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3201         ostr << "* violation: comment within section\n";
3202         ostr << "* line: '" << iline << "'\n";
3203         ostr << "* file position: " << pos;
3204 
3205         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3206     }
3207 
3208     if( !GetIDFString( iline, token, quoted, idx ) )
3209     {
3210         ostringstream ostr;
3211 
3212         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3213         ostr << "* violation: no GEOMETRY NAME\n";
3214         ostr << "* line: '" << iline << "'\n";
3215         ostr << "* file position: " << pos;
3216 
3217         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3218     }
3219 
3220     geometry = token;
3221 
3222     if( !GetIDFString( iline, token, quoted, idx ) )
3223     {
3224         ostringstream ostr;
3225 
3226         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3227         ostr << "* violation: no PART NAME\n";
3228         ostr << "* line: '" << iline << "'\n";
3229         ostr << "* file position: " << pos;
3230 
3231         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3232     }
3233 
3234     part = token;
3235 
3236     if( part.empty() && geometry.empty() )
3237     {
3238         ostringstream ostr;
3239 
3240         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3241         ostr << "* violation: both GEOMETRY and PART names are empty\n";
3242         ostr << "* line: '" << iline << "'\n";
3243         ostr << "* file position: " << pos;
3244 
3245         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3246     }
3247 
3248     if( !GetIDFString( iline, token, quoted, idx ) )
3249     {
3250         ostringstream ostr;
3251 
3252         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3253         ostr << "* violation: no UNIT type\n";
3254         ostr << "* line: '" << iline << "'\n";
3255         ostr << "* file position: " << pos;
3256 
3257         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3258     }
3259 
3260     if( CompareToken( "MM", token ) )
3261     {
3262         unit = UNIT_MM;
3263     }
3264     else if( CompareToken( "THOU", token ) )
3265     {
3266         unit = UNIT_THOU;
3267     }
3268     else if( aIdfVersion == IDF_V2 && !CompareToken( "TNM", token ) )
3269     {
3270         unit = UNIT_TNM;
3271     }
3272     else
3273     {
3274         ostringstream ostr;
3275 
3276         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3277         ostr << "* violation: invalid UNIT '" << token << "': must be one of MM or THOU\n";
3278         ostr << "* line: '" << iline << "'\n";
3279         ostr << "* file position: " << pos;
3280 
3281         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3282     }
3283 
3284     if( !GetIDFString( iline, token, quoted, idx ) )
3285     {
3286         ostringstream ostr;
3287 
3288         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3289         ostr << "* violation: no height specified\n";
3290         ostr << "* line: '" << iline << "'\n";
3291         ostr << "* file position: " << pos;
3292 
3293         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3294     }
3295 
3296     std::istringstream teststr;
3297     teststr.str( token );
3298 
3299     teststr >> thickness;
3300     if( teststr.fail() )
3301     {
3302         ostringstream ostr;
3303 
3304         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3305         ostr << "* violation: invalid height '" << token << "'\n";
3306         ostr << "* line: '" << iline << "'\n";
3307         ostr << "* file position: " << pos;
3308 
3309         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3310     }
3311 
3312     if( unit == UNIT_THOU )
3313     {
3314         thickness *= IDF_THOU_TO_MM;
3315     }
3316     else if( ( aIdfVersion == IDF_V2 ) && ( unit == UNIT_TNM ) )
3317     {
3318         thickness *= IDF_TNM_TO_MM;
3319     }
3320     else if( unit != UNIT_MM )
3321     {
3322         ostringstream ostr;
3323         ostr << "\n* BUG: invalid UNIT type: " << unit;
3324 
3325         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3326     }
3327 
3328     // read RECORD 3 values
3329     readOutlines( aLibFile, aIdfVersion );
3330 
3331     if( compType == COMP_ELEC && aIdfVersion > IDF_V2 )
3332         readProperties( aLibFile );
3333 
3334     // check RECORD 4
3335     while( aLibFile.good() && !FetchIDFLine( aLibFile, iline, comment, pos ) );
3336 
3337     if( ( !aLibFile.good() && aLibFile.eof() ) && iline.empty() )
3338     {
3339         ostringstream ostr;
3340 
3341         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3342         ostr << "* violation: premature end\n";
3343         ostr << "* file position: " << pos;
3344 
3345         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3346     }
3347 
3348     idx = 0;
3349 
3350     if( comment )
3351     {
3352         ostringstream ostr;
3353 
3354         ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3355         ostr << "* violation: comment within section\n";
3356         ostr << "* line: '" << iline << "'\n";
3357         ostr << "* file position: " << pos;
3358 
3359         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3360     }
3361 
3362     if( compType == COMP_ELEC )
3363     {
3364         if( !CompareToken( ".END_ELECTRICAL", iline ) )
3365         {
3366             ostringstream ostr;
3367 
3368             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3369             ostr << "* violation: no .END_ELECTRICAL found\n";
3370             ostr << "* line: '" << iline << "'\n";
3371             ostr << "* file position: " << pos;
3372 
3373             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3374         }
3375     }
3376     else
3377     {
3378         if( !CompareToken( ".END_MECHANICAL", iline ) )
3379         {
3380             ostringstream ostr;
3381 
3382             ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
3383             ostr << "* violation: no .END_MECHANICAL found\n";
3384             ostr << "* line: '" << iline << "'\n";
3385             ostr << "* file position: " << pos;
3386 
3387             throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3388         }
3389     }
3390 }
3391 
3392 
writeData(std::ostream & aLibFile)3393 void IDF3_COMP_OUTLINE::writeData( std::ostream& aLibFile )
3394 {
3395     if( refNum == 0 )
3396         return;    // nothing to do
3397 
3398     if( compType != COMP_ELEC && compType != COMP_MECH )
3399     {
3400         ostringstream ostr;
3401         ostr << "\n* component type not set or invalid: " << compType;
3402 
3403         throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
3404     }
3405 
3406     writeComments( aLibFile );
3407 
3408     // note: the outline section is required, even if it is empty
3409     if( compType == COMP_ELEC )
3410         aLibFile << ".ELECTRICAL\n";
3411     else
3412         aLibFile << ".MECHANICAL\n";
3413 
3414     // RECORD 2
3415     // [GEOM] [PART] [UNIT] [HEIGHT]
3416     aLibFile << "\"" << geometry << "\" \"" << part << "\" ";
3417 
3418     if( unit != UNIT_THOU )
3419         aLibFile << "MM " << setiosflags( ios::fixed ) << setprecision( 5 ) << thickness << "\n";
3420     else
3421         aLibFile << "THOU " << setiosflags( ios::fixed ) << setprecision( 1 )
3422                  << ( thickness / IDF_THOU_TO_MM ) << "\n";
3423 
3424     writeOutlines( aLibFile );
3425 
3426     if( compType == COMP_ELEC )
3427     {
3428         writeProperties( aLibFile );
3429         aLibFile << ".END_ELECTRICAL\n\n";
3430     }
3431     else
3432     {
3433         aLibFile << ".END_MECHANICAL\n\n";
3434     }
3435 }
3436 
3437 
Clear(void)3438 bool IDF3_COMP_OUTLINE::Clear( void )
3439 {
3440 #ifndef DISABLE_IDF_OWNERSHIP
3441     if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
3442         return false;
3443 #endif
3444 
3445     clear();
3446     uid.clear();
3447     geometry.clear();
3448     part.clear();
3449     compType = COMP_INVALID;
3450     refNum = 0;
3451     props.clear();
3452 
3453     return true;
3454 }
3455 
3456 
SetComponentClass(IDF3::COMP_TYPE aCompClass)3457 bool IDF3_COMP_OUTLINE::SetComponentClass( IDF3::COMP_TYPE aCompClass )
3458 {
3459     switch( aCompClass )
3460     {
3461     case COMP_ELEC:
3462     case COMP_MECH:
3463         compType = aCompClass;
3464         break;
3465 
3466     default:
3467         do
3468         {
3469             ostringstream ostr;
3470             ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
3471             ostr << "* BUG: invalid component class (must be ELECTRICAL or MECHANICAL): ";
3472             ostr << aCompClass << "\n";
3473             errormsg = ostr.str();
3474 
3475             return false;
3476         } while( 0 );
3477 
3478         break;
3479     }
3480 
3481     return true;
3482 }
3483 
3484 
GetComponentClass(void)3485 IDF3::COMP_TYPE IDF3_COMP_OUTLINE::GetComponentClass( void )
3486 {
3487     return compType;
3488 }
3489 
3490 
SetGeomName(const std::string & aGeomName)3491 void IDF3_COMP_OUTLINE::SetGeomName( const std::string& aGeomName )
3492 {
3493     geometry = aGeomName;
3494     uid.clear();
3495 }
3496 
3497 
GetGeomName(void)3498 const std::string& IDF3_COMP_OUTLINE::GetGeomName( void )
3499 {
3500     return geometry;
3501 }
3502 
3503 
SetPartName(const std::string & aPartName)3504 void IDF3_COMP_OUTLINE::SetPartName( const std::string& aPartName )
3505 {
3506     part = aPartName;
3507     uid.clear();
3508 }
3509 
3510 
GetPartName(void)3511 const std::string& IDF3_COMP_OUTLINE::GetPartName( void )
3512 {
3513     return part;
3514 }
3515 
3516 
GetUID(void)3517 const std::string& IDF3_COMP_OUTLINE::GetUID( void )
3518 {
3519     if( !uid.empty() )
3520         return uid;
3521 
3522     if( geometry.empty() && part.empty() )
3523         return uid;
3524 
3525     uid = geometry + "_" + part;
3526 
3527     return uid;
3528 }
3529 
3530 
incrementRef(void)3531 int IDF3_COMP_OUTLINE::incrementRef( void )
3532 {
3533     return ++refNum;
3534 }
3535 
3536 
decrementRef(void)3537 int IDF3_COMP_OUTLINE::decrementRef( void )
3538 {
3539     if( refNum == 0 )
3540     {
3541         ostringstream ostr;
3542         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
3543         ostr << "* BUG:  decrementing refNum beyond 0";
3544         errormsg = ostr.str();
3545 
3546         return -1;
3547     }
3548 
3549     --refNum;
3550     return refNum;
3551 }
3552 
3553 
CreateDefaultOutline(const std::string & aGeom,const std::string & aPart)3554 bool IDF3_COMP_OUTLINE::CreateDefaultOutline( const std::string& aGeom, const std::string& aPart )
3555 {
3556     Clear();
3557 
3558     if( aGeom.empty() && aPart.empty() )
3559     {
3560         geometry  = "NOGEOM";
3561         part      = "NOPART";
3562         uid       = "NOGEOM_NOPART";
3563     }
3564     else
3565     {
3566         geometry  = aGeom;
3567         part      = aPart;
3568         uid       = aGeom + "_" + aPart;
3569     }
3570 
3571     compType  = COMP_ELEC;
3572     thickness = 5.0;
3573     unit      = UNIT_MM;
3574 
3575     // Create a star shape 5mm high with points on 5 and 3 mm circles
3576     double a, da;
3577     da = M_PI / 5.0;
3578     a = da / 2.0;
3579 
3580     IDF_POINT p1, p2;
3581     IDF_OUTLINE* ol = new IDF_OUTLINE;
3582     IDF_SEGMENT* sp;
3583 
3584     p1.x = 1.5 * cos( a );
3585     p1.y = 1.5 * sin( a );
3586 
3587     if( ol == nullptr )
3588         return false;
3589 
3590     for( int i = 0; i < 10; ++i )
3591     {
3592         if( i & 1 )
3593         {
3594             p2.x = 2.5 * cos( a );
3595             p2.y = 2.5 * sin( a );
3596         }
3597         else
3598         {
3599             p2.x = 1.5 * cos( a );
3600             p2.y = 1.5 * sin( a );
3601         }
3602 
3603         sp = new IDF_SEGMENT( p1, p2 );
3604 
3605         if( sp == nullptr )
3606         {
3607             Clear();
3608             return false;
3609         }
3610 
3611         ol->push( sp );
3612         a += da;
3613         p1 = p2;
3614     }
3615 
3616     a = da / 2.0;
3617     p2.x = 1.5 * cos( a );
3618     p2.y = 1.5 * sin( a );
3619 
3620     sp = new IDF_SEGMENT( p1, p2 );
3621 
3622     if( sp == nullptr )
3623     {
3624         Clear();
3625         return false;
3626     }
3627 
3628     ol->push( sp );
3629     outlines.push_back( ol );
3630 
3631     return true;
3632 }
3633