KiCad PCB EDA Suite
pcb_parser.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2012 CERN
5 * Copyright (C) 2012-2022 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
30#include <cerrno>
31#include <charconv>
32#include <confirm.h>
33#include <macros.h>
34#include <title_block.h>
35#include <trigo.h>
36
37#include <board.h>
39#include <fp_shape.h>
40#include <fp_textbox.h>
41#include <pcb_dimension.h>
42#include <pcb_shape.h>
43#include <pcb_bitmap.h>
44#include <pcb_group.h>
45#include <pcb_target.h>
46#include <pcb_track.h>
47#include <pcb_textbox.h>
48#include <pad.h>
49#include <zone.h>
50#include <footprint.h>
52#include <font/font.h>
53#include <ignore.h>
54#include <netclass.h>
57#include <pcb_plot_params.h>
58#include <locale_io.h>
59#include <zones.h>
61#include <convert_basic_shapes_to_polygon.h> // for RECT_CHAMFER_POSITIONS definition
62#include <math/util.h> // KiROUND, Clamp
63#include <string_utils.h>
64#include <wx/log.h>
65#include <progress_reporter.h>
67
68// For some reason wxWidgets is built with wxUSE_BASE64 unset so expose the wxWidgets
69// base64 code. Needed for PCB_BITMAP
70#define wxUSE_BASE64 1
71#include <wx/base64.h>
72#include <wx/mstream.h>
73
74using namespace PCB_KEYS_T;
75
76
78{
81 m_tooRecent = false;
83 m_layerIndices.clear();
84 m_layerMasks.clear();
85 m_resetKIIDMap.clear();
86
87 // Add untranslated default (i.e. English) layernames.
88 // Some may be overridden later if parsing a board rather than a footprint.
89 // The English name will survive if parsing only a footprint.
90 for( int layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
91 {
92 std::string untranslated = TO_UTF8( wxString( LSET::Name( PCB_LAYER_ID( layer ) ) ) );
93
94 m_layerIndices[ untranslated ] = PCB_LAYER_ID( layer );
95 m_layerMasks[ untranslated ] = LSET( PCB_LAYER_ID( layer ) );
96 }
97
98 m_layerMasks[ "*.Cu" ] = LSET::AllCuMask();
99 m_layerMasks[ "*In.Cu" ] = LSET::InternalCuMask();
100 m_layerMasks[ "F&B.Cu" ] = LSET( 2, F_Cu, B_Cu );
101 m_layerMasks[ "*.Adhes" ] = LSET( 2, B_Adhes, F_Adhes );
102 m_layerMasks[ "*.Paste" ] = LSET( 2, B_Paste, F_Paste );
103 m_layerMasks[ "*.Mask" ] = LSET( 2, B_Mask, F_Mask );
104 m_layerMasks[ "*.SilkS" ] = LSET( 2, B_SilkS, F_SilkS );
105 m_layerMasks[ "*.Fab" ] = LSET( 2, B_Fab, F_Fab );
106 m_layerMasks[ "*.CrtYd" ] = LSET( 2, B_CrtYd, F_CrtYd );
107
108 // This is for the first pretty & *.kicad_pcb formats, which had
109 // Inner1_Cu - Inner14_Cu with the numbering sequence
110 // reversed from the subsequent format's In1_Cu - In30_Cu numbering scheme.
111 // The newer format brought in an additional 16 Cu layers and flipped the cu stack but
112 // kept the gap between one of the outside layers and the last cu internal.
113
114 for( int i=1; i<=14; ++i )
115 {
116 std::string key = StrPrintf( "Inner%d.Cu", i );
117
118 m_layerMasks[ key ] = LSET( PCB_LAYER_ID( In15_Cu - i ) );
119 }
120}
121
122
124{
126 {
127 TIME_PT curTime = CLOCK::now();
128 unsigned curLine = reader->LineNumber();
129 auto delta = std::chrono::duration_cast<TIMEOUT>( curTime - m_lastProgressTime );
130
131 if( delta > std::chrono::milliseconds( 250 ) )
132 {
133 m_progressReporter->SetCurrentProgress( ( (double) curLine )
134 / std::max( 1U, m_lineCount ) );
135
137 THROW_IO_ERROR( ( "Open cancelled by user." ) );
138
139 m_lastProgressTime = curTime;
140 }
141 }
142}
143
144
146{
147 int curr_level = 0;
148 T token;
149
150 while( ( token = NextTok() ) != T_EOF )
151 {
152 if( token == T_LEFT )
153 curr_level--;
154
155 if( token == T_RIGHT )
156 {
157 curr_level++;
158
159 if( curr_level > 0 )
160 return;
161 }
162 }
163}
164
165
166void PCB_PARSER::pushValueIntoMap( int aIndex, int aValue )
167{
168 // Add aValue in netcode mapping (m_netCodes) at index aNetCode
169 // ensure there is room in m_netCodes for that, and add room if needed.
170
171 if( (int)m_netCodes.size() <= aIndex )
172 m_netCodes.resize( static_cast<std::size_t>( aIndex ) + 1 );
173
174 m_netCodes[aIndex] = aValue;
175}
176
177
179{
180 // There should be no major rounding issues here, since the values in
181 // the file are in mm and get converted to nano-meters.
182 // See test program tools/test-nm-biu-to-ascii-mm-round-tripping.cpp
183 // to confirm or experiment. Use a similar strategy in both places, here
184 // and in the test program. Make that program with:
185 // $ make test-nm-biu-to-ascii-mm-round-tripping
186 auto retval = parseDouble() * pcbIUScale.IU_PER_MM;
187
188 // N.B. we currently represent board units as integers. Any values that are
189 // larger or smaller than those board units represent undefined behavior for
190 // the system. We limit values to the largest that is visible on the screen
191 // This is the diagonal distance of the full screen ~1.5m
192 constexpr double int_limit =
193 std::numeric_limits<int>::max() * 0.7071; // 0.7071 = roughly 1/sqrt(2)
194 return KiROUND( Clamp<double>( -int_limit, retval, int_limit ) );
195}
196
197
198int PCB_PARSER::parseBoardUnits( const char* aExpected )
199{
200 auto retval = parseDouble( aExpected ) * pcbIUScale.IU_PER_MM;
201
202 // N.B. we currently represent board units as integers. Any values that are
203 // larger or smaller than those board units represent undefined behavior for
204 // the system. We limit values to the largest that is visible on the screen
205 constexpr double int_limit = std::numeric_limits<int>::max() * 0.7071;
206
207 // Use here #KiROUND, not EKIROUND (see comments about them) when having a function as
208 // argument, because it will be called twice with #KIROUND.
209 return KiROUND( Clamp<double>( -int_limit, retval, int_limit ) );
210}
211
212
214{
215 T token = NextTok();
216
217 if( token == T_yes )
218 return true;
219 else if( token == T_no )
220 return false;
221 else
222 Expecting( "yes or no" );
223
224 return false;
225}
226
227
229{
230 int year, month, day;
231
232 year = m_requiredVersion / 10000;
233 month = ( m_requiredVersion / 100 ) - ( year * 100 );
234 day = m_requiredVersion - ( year * 10000 ) - ( month * 100 );
235
236 // wx throws an assertion, not a catchable exception, when the date is invalid.
237 // User input shouldn't give wx asserts, so check manually and throw a proper
238 // error instead
239 if( day <= 0 || month <= 0 || month > 12 ||
240 day > wxDateTime::GetNumberOfDays( (wxDateTime::Month)( month - 1 ), year ) )
241 {
242 wxString err;
243 err.Printf( _( "Cannot interpret date code %d" ), m_requiredVersion );
244 THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
245 }
246
247 wxDateTime date( day, (wxDateTime::Month)( month - 1 ), year, 0, 0, 0, 0 );
248 return date.FormatDate();
249}
250
251
253{
254 if( CurTok() != T_LEFT )
255 NeedLEFT();
256
257 VECTOR2I pt;
258 T token = NextTok();
259
260 if( token != T_xy )
261 Expecting( T_xy );
262
263 pt.x = parseBoardUnits( "X coordinate" );
264 pt.y = parseBoardUnits( "Y coordinate" );
265
266 NeedRIGHT();
267
268 return pt;
269}
270
271
273{
274 if( CurTok() != T_LEFT )
275 NeedLEFT();
276
277 T token = NextTok();
278
279 switch( token )
280 {
281 case T_xy:
282 {
283 int x = parseBoardUnits( "X coordinate" );
284 int y = parseBoardUnits( "Y coordinate" );
285
286 NeedRIGHT();
287
288 aPoly.Append( x, y );
289 break;
290 }
291 case T_arc:
292 {
293 bool has_start = false;
294 bool has_mid = false;
295 bool has_end = false;
296
297 VECTOR2I arc_start, arc_mid, arc_end;
298
299 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
300 {
301 if( token != T_LEFT )
302 Expecting( T_LEFT );
303
304 token = NextTok();
305
306 switch( token )
307 {
308 case T_start:
309 arc_start.x = parseBoardUnits( "start x" );
310 arc_start.y = parseBoardUnits( "start y" );
311 has_start = true;
312 break;
313
314 case T_mid:
315 arc_mid.x = parseBoardUnits( "mid x" );
316 arc_mid.y = parseBoardUnits( "mid y" );
317 has_mid = true;
318 break;
319
320 case T_end:
321 arc_end.x = parseBoardUnits( "end x" );
322 arc_end.y = parseBoardUnits( "end y" );
323 has_end = true;
324 break;
325
326 default:
327 Expecting( "start, mid or end" );
328 }
329
330 NeedRIGHT();
331 }
332
333 if( !has_start )
334 Expecting( "start" );
335
336 if( !has_mid )
337 Expecting( "mid" );
338
339 if( !has_end )
340 Expecting( "end" );
341
342 SHAPE_ARC arc( arc_start, arc_mid, arc_end, 0 );
343
344 aPoly.Append( arc );
345
346 if( token != T_RIGHT )
347 Expecting( T_RIGHT );
348
349 break;
350 }
351 default:
352 Expecting( "xy or arc" );
353 }
354}
355
356
357void PCB_PARSER::parseXY( int* aX, int* aY )
358{
359 VECTOR2I pt = parseXY();
360
361 if( aX )
362 *aX = pt.x;
363
364 if( aY )
365 *aY = pt.y;
366}
367
368
369std::pair<wxString, wxString> PCB_PARSER::parseProperty()
370{
371 wxString pName;
372 wxString pValue;
373
374 NeedSYMBOL();
375 pName = FromUTF8();
376 NeedSYMBOL();
377 pValue = FromUTF8();
378 NeedRIGHT();
379
380 return { pName, pValue };
381}
382
383
385{
386 wxCHECK_RET( CurTok() == T_effects,
387 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as EDA_TEXT." ) );
388
389 // These are not written out if center/center, so we have to make sure we start that way.
392
393 // In version 20210606 the notation for overbars was changed from `~...~` to `~{...}`.
394 // We need to convert the old syntax to the new one.
395 if( m_requiredVersion < 20210606 )
396 aText->SetText( ConvertToNewOverbarNotation( aText->GetText() ) );
397
398 T token;
399
400 // Prior to v5.0 text size was omitted from file format if equal to 60mils
401 // Now, it is always explicitly written to file
402 bool foundTextSize = false;
403 wxString faceName;
404
405 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
406 {
407 if( token == T_LEFT )
408 token = NextTok();
409
410 switch( token )
411 {
412 case T_font:
413 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
414 {
415 if( token == T_LEFT )
416 continue;
417
418 switch( token )
419 {
420 case T_face:
421 NeedSYMBOL();
422 faceName = FromUTF8();
423 NeedRIGHT();
424 break;
425
426 case T_size:
427 {
428 wxSize sz;
429 sz.SetHeight( parseBoardUnits( "text height" ) );
430 sz.SetWidth( parseBoardUnits( "text width" ) );
431 aText->SetTextSize( sz );
432 NeedRIGHT();
433
434 foundTextSize = true;
435 break;
436 }
437
438 case T_line_spacing:
439 aText->SetLineSpacing( parseDouble( "line spacing" ) );
440 NeedRIGHT();
441 break;
442
443 case T_thickness:
444 aText->SetTextThickness( parseBoardUnits( "text thickness" ) );
445 NeedRIGHT();
446 break;
447
448 case T_bold:
449 aText->SetBold( true );
450 break;
451
452 case T_italic:
453 aText->SetItalic( true );
454 break;
455
456 default:
457 Expecting( "face, size, line_spacing, thickness, bold, or italic" );
458 }
459 }
460
461 if( !faceName.IsEmpty() )
462 {
463 aText->SetFont( KIFONT::FONT::GetFont( faceName, aText->IsBold(),
464 aText->IsItalic() ) );
465 }
466
467 break;
468
469 case T_justify:
470 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
471 {
472 if( token == T_LEFT )
473 continue;
474
475 switch( token )
476 {
477 case T_left:
479 break;
480
481 case T_right:
483 break;
484
485 case T_top:
487 break;
488
489 case T_bottom:
491 break;
492
493 case T_mirror:
494 aText->SetMirrored( true );
495 break;
496
497 default:
498 Expecting( "left, right, top, bottom, or mirror" );
499 }
500
501 }
502
503 break;
504
505 case T_hide:
506 aText->SetVisible( false );
507 break;
508
509 default:
510 Expecting( "font, justify, or hide" );
511 }
512 }
513
514 // Text size was not specified in file, force legacy default units
515 // 60mils is 1.524mm
516 if( !foundTextSize )
517 {
518 const double defaultTextSize = 1.524 * pcbIUScale.IU_PER_MM;
519
520 aText->SetTextSize( wxSize( defaultTextSize, defaultTextSize ) );
521 }
522}
523
524
526{
527 T token;
528
529 NeedSYMBOLorNUMBER();
530 wxString cacheText = FROM_UTF8( CurText() );
531 EDA_ANGLE cacheAngle( parseDouble( "render cache angle" ), DEGREES_T );
532
533 text->SetupRenderCache( cacheText, cacheAngle );
534
535 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
536 {
537 if( token != T_LEFT )
538 Expecting( T_LEFT );
539
540 token = NextTok();
541
542 if( token != T_polygon )
543 Expecting( T_polygon );
544
545 SHAPE_POLY_SET poly;
546
547 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
548 {
549 if( token != T_LEFT )
550 Expecting( T_LEFT );
551
552 token = NextTok();
553
554 if( token != T_pts )
555 Expecting( T_pts );
556
557 SHAPE_LINE_CHAIN lineChain;
558
559 while( (token = NextTok() ) != T_RIGHT )
560 parseOutlinePoints( lineChain );
561
562 lineChain.SetClosed( true );
563
564 if( poly.OutlineCount() == 0 )
565 poly.AddOutline( lineChain );
566 else
567 poly.AddHole( lineChain );
568 }
569
570 text->AddRenderCacheGlyph( poly );
571 }
572}
573
574
576{
577 wxCHECK_MSG( CurTok() == T_model, nullptr,
578 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as FP_3DMODEL." ) );
579
580 T token;
581
582 FP_3DMODEL* n3D = new FP_3DMODEL;
583 NeedSYMBOLorNUMBER();
584 n3D->m_Filename = FromUTF8();
585
586 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
587 {
588 if( token == T_LEFT )
589 token = NextTok();
590
591 switch( token )
592 {
593 case T_at:
594 NeedLEFT();
595 token = NextTok();
596
597 if( token != T_xyz )
598 Expecting( T_xyz );
599
600 /* Note:
601 * Prior to KiCad v5, model offset was designated by "at",
602 * and the units were in inches.
603 * Now we use mm, but support reading of legacy files
604 */
605
606 n3D->m_Offset.x = parseDouble( "x value" ) * 25.4f;
607 n3D->m_Offset.y = parseDouble( "y value" ) * 25.4f;
608 n3D->m_Offset.z = parseDouble( "z value" ) * 25.4f;
609
610 NeedRIGHT(); // xyz
611 NeedRIGHT(); // at
612 break;
613
614 case T_hide:
615 n3D->m_Show = false;
616 break;
617
618 case T_opacity:
619 n3D->m_Opacity = parseDouble( "opacity value" );
620 NeedRIGHT();
621 break;
622
623 case T_offset:
624 NeedLEFT();
625 token = NextTok();
626
627 if( token != T_xyz )
628 Expecting( T_xyz );
629
630 /*
631 * 3D model offset is in mm
632 */
633 n3D->m_Offset.x = parseDouble( "x value" );
634 n3D->m_Offset.y = parseDouble( "y value" );
635 n3D->m_Offset.z = parseDouble( "z value" );
636
637 NeedRIGHT(); // xyz
638 NeedRIGHT(); // offset
639 break;
640
641 case T_scale:
642 NeedLEFT();
643 token = NextTok();
644
645 if( token != T_xyz )
646 Expecting( T_xyz );
647
648 n3D->m_Scale.x = parseDouble( "x value" );
649 n3D->m_Scale.y = parseDouble( "y value" );
650 n3D->m_Scale.z = parseDouble( "z value" );
651
652 NeedRIGHT(); // xyz
653 NeedRIGHT(); // scale
654 break;
655
656 case T_rotate:
657 NeedLEFT();
658 token = NextTok();
659
660 if( token != T_xyz )
661 Expecting( T_xyz );
662
663 n3D->m_Rotation.x = parseDouble( "x value" );
664 n3D->m_Rotation.y = parseDouble( "y value" );
665 n3D->m_Rotation.z = parseDouble( "z value" );
666
667 NeedRIGHT(); // xyz
668 NeedRIGHT(); // rotate
669 break;
670
671 default:
672 Expecting( "at, hide, opacity, offset, scale, or rotate" );
673 }
674
675 }
676
677 return n3D;
678}
679
680
682{
683 T token;
684 BOARD_ITEM* item;
685 LOCALE_IO toggle;
686
687 m_groupInfos.clear();
688
689 // FOOTPRINTS can be prefixed with an initial block of single line comments and these are
690 // kept for Format() so they round trip in s-expression form. BOARDs might eventually do
691 // the same, but currently do not.
692 std::unique_ptr<wxArrayString> initial_comments( ReadCommentLines() );
693
694 token = CurTok();
695
696 if( token == -1 ) // EOF
697 Unexpected( token );
698
699 if( token != T_LEFT )
700 Expecting( T_LEFT );
701
702 switch( NextTok() )
703 {
704 case T_kicad_pcb:
705 if( m_board == nullptr )
706 m_board = new BOARD();
707
708 item = (BOARD_ITEM*) parseBOARD();
709 break;
710
711 case T_module: // legacy token
712 case T_footprint:
713 item = (BOARD_ITEM*) parseFOOTPRINT( initial_comments.release() );
714
715 // Locking a footprint has no meaning outside of a board.
716 item->SetLocked( false );
717 break;
718
719 default:
720 wxString err;
721 err.Printf( _( "Unknown token '%s'" ), FromUTF8() );
722 THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
723 }
724
725 resolveGroups( item );
726
727 return item;
728}
729
730
732{
733 try
734 {
735 return parseBOARD_unchecked();
736 }
737 catch( const PARSE_ERROR& parse_error )
738 {
739 if( m_tooRecent )
740 throw FUTURE_FORMAT_ERROR( parse_error, GetRequiredVersion() );
741 else
742 throw;
743 }
744}
745
746
748{
749 T token;
750 std::map<wxString, wxString> properties;
751
752 parseHeader();
753
754 std::vector<BOARD_ITEM*> bulkAddedItems;
755 BOARD_ITEM* item = nullptr;
756
757 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
758 {
759 checkpoint();
760
761 if( token != T_LEFT )
762 Expecting( T_LEFT );
763
764 token = NextTok();
765
766 if( token == T_page && m_requiredVersion <= 20200119 )
767 token = T_paper;
768
769 switch( token )
770 {
771 case T_host: // legacy token
772 NeedSYMBOL();
773 m_board->SetGenerator( FromUTF8() );
774
775 // Older formats included build data
777 NeedSYMBOL();
778
779 NeedRIGHT();
780 break;
781
782 case T_generator:
783 NeedSYMBOL();
784 m_board->SetGenerator( FromUTF8() );
785 NeedRIGHT();
786 break;
787
788 case T_general:
790 break;
791
792 case T_paper:
794 break;
795
796 case T_title_block:
798 break;
799
800 case T_layers:
801 parseLayers();
802 break;
803
804 case T_setup:
805 parseSetup();
806 break;
807
808 case T_property:
809 properties.insert( parseProperty() );
810 break;
811
812 case T_net:
814 break;
815
816 case T_net_class:
819 break;
820
821 case T_gr_arc:
822 case T_gr_curve:
823 case T_gr_line:
824 case T_gr_poly:
825 case T_gr_circle:
826 case T_gr_rect:
827 item = parsePCB_SHAPE();
828 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
829 bulkAddedItems.push_back( item );
830 break;
831
832 case T_image:
833 item = parsePCB_BITMAP( m_board );
834 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
835 bulkAddedItems.push_back( item );
836 break;
837
838 case T_gr_text:
839 item = parsePCB_TEXT();
840 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
841 bulkAddedItems.push_back( item );
842 break;
843
844 case T_gr_text_box:
845 item = parsePCB_TEXTBOX();
846 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
847 bulkAddedItems.push_back( item );
848 break;
849
850 case T_dimension:
851 item = parseDIMENSION( m_board, false );
852 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
853 bulkAddedItems.push_back( item );
854 break;
855
856 case T_module: // legacy token
857 case T_footprint:
858 item = parseFOOTPRINT();
859 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
860 bulkAddedItems.push_back( item );
861 break;
862
863 case T_segment:
864 item = parsePCB_TRACK();
865 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
866 bulkAddedItems.push_back( item );
867 break;
868
869 case T_arc:
870 item = parseARC();
871 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
872 bulkAddedItems.push_back( item );
873 break;
874
875 case T_group:
877 break;
878
879 case T_via:
880 item = parsePCB_VIA();
881 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
882 bulkAddedItems.push_back( item );
883 break;
884
885 case T_zone:
886 item = parseZONE( m_board );
887 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
888 bulkAddedItems.push_back( item );
889 break;
890
891 case T_target:
892 item = parsePCB_TARGET();
893 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
894 bulkAddedItems.push_back( item );
895 break;
896
897 default:
898 wxString err;
899 err.Printf( _( "Unknown token '%s'" ), FromUTF8() );
900 THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
901 }
902 }
903
904 if( bulkAddedItems.size() > 0 )
905 m_board->FinalizeBulkAdd( bulkAddedItems );
906
907 m_board->SetProperties( properties );
908
909 if( m_undefinedLayers.size() > 0 )
910 {
911 bool deleteItems;
912 std::vector<BOARD_ITEM*> deleteList;
913 wxString msg = wxString::Format( _( "Items found on undefined layers. Do you wish to\n"
914 "rescue them to the User.Comments layer?" ) );
915 wxString details = wxString::Format( _( "Undefined layers:" ) );
916
917 for( const wxString& undefinedLayer : m_undefinedLayers )
918 details += wxT( "\n " ) + undefinedLayer;
919
920 wxRichMessageDialog dlg( nullptr, msg, _( "Warning" ),
921 wxYES_NO | wxCANCEL | wxCENTRE | wxICON_WARNING | wxSTAY_ON_TOP );
922 dlg.ShowDetailedText( details );
923 dlg.SetYesNoCancelLabels( _( "Rescue" ), _( "Delete" ), _( "Cancel" ) );
924
925 switch( dlg.ShowModal() )
926 {
927 case wxID_YES: deleteItems = false; break;
928 case wxID_NO: deleteItems = true; break;
929 case wxID_CANCEL:
930 default: THROW_IO_ERROR( wxT( "CANCEL" ) );
931 }
932
933 auto visitItem = [&]( BOARD_ITEM* curr_item )
934 {
935 if( curr_item->GetLayer() == Rescue )
936 {
937 if( deleteItems )
938 deleteList.push_back( curr_item );
939 else
940 curr_item->SetLayer( Cmts_User );
941 }
942 };
943
944 for( PCB_TRACK* track : m_board->Tracks() )
945 {
946 if( track->Type() == PCB_VIA_T )
947 {
948 PCB_VIA* via = static_cast<PCB_VIA*>( track );
949 PCB_LAYER_ID top_layer, bottom_layer;
950
951 if( via->GetViaType() == VIATYPE::THROUGH )
952 continue;
953
954 via->LayerPair( &top_layer, &bottom_layer );
955
956 if( top_layer == Rescue || bottom_layer == Rescue )
957 {
958 if( deleteItems )
959 deleteList.push_back( via );
960 else
961 {
962 if( top_layer == Rescue )
963 top_layer = F_Cu;
964
965 if( bottom_layer == Rescue )
966 bottom_layer = B_Cu;
967
968 via->SetLayerPair( top_layer, bottom_layer );
969 }
970 }
971 }
972 else
973 {
974 visitItem( track );
975 }
976 }
977
978 for( BOARD_ITEM* zone : m_board->Zones() )
979 visitItem( zone );
980
981 for( BOARD_ITEM* drawing : m_board->Drawings() )
982 visitItem( drawing );
983
984 for( FOOTPRINT* fp : m_board->Footprints() )
985 {
986 for( BOARD_ITEM* drawing : fp->GraphicalItems() )
987 visitItem( drawing );
988
989 for( BOARD_ITEM* zone : fp->Zones() )
990 visitItem( zone );
991 }
992
993 for( BOARD_ITEM* curr_item : deleteList )
994 m_board->Delete( curr_item );
995
996 m_undefinedLayers.clear();
997 }
998
999 return m_board;
1000}
1001
1002
1004{
1005 auto getItem = [&]( const KIID& aId )
1006 {
1007 BOARD_ITEM* aItem = nullptr;
1008
1009 if( dynamic_cast<BOARD*>( aParent ) )
1010 {
1011 aItem = static_cast<BOARD*>( aParent )->GetItem( aId );
1012 }
1013 else if( aParent->Type() == PCB_FOOTPRINT_T )
1014 {
1015 static_cast<FOOTPRINT*>( aParent )->RunOnChildren(
1016 [&]( BOARD_ITEM* child )
1017 {
1018 if( child->m_Uuid == aId )
1019 aItem = child;
1020 } );
1021 }
1022
1023 return aItem;
1024 };
1025
1026 // Now that we've parsed the other Uuids in the file we can resolve the uuids referred
1027 // to in the group declarations we saw.
1028 //
1029 // First add all group objects so subsequent GetItem() calls for nested groups work.
1030
1031 for( size_t idx = 0; idx < m_groupInfos.size(); idx++ )
1032 {
1033 GROUP_INFO& aGrp = m_groupInfos[idx];
1034 PCB_GROUP* group = new PCB_GROUP( aGrp.parent );
1035
1036 group->SetName( aGrp.name );
1037 const_cast<KIID&>( group->m_Uuid ) = aGrp.uuid;
1038
1039 if( aGrp.locked )
1040 group->SetLocked( true );
1041
1042 if( aGrp.parent->Type() == PCB_FOOTPRINT_T )
1043 static_cast<FOOTPRINT*>( aGrp.parent )->Add( group, ADD_MODE::INSERT, true );
1044 else
1045 static_cast<BOARD*>( aGrp.parent )->Add( group, ADD_MODE::INSERT, true );
1046 }
1047
1048 wxString error;
1049
1050 for( size_t idx = 0; idx < m_groupInfos.size(); idx++ )
1051 {
1052 GROUP_INFO& aGrp = m_groupInfos[idx];
1053 BOARD_ITEM* bItem = getItem( aGrp.uuid );
1054
1055 if( bItem == nullptr || bItem->Type() != PCB_GROUP_T )
1056 continue;
1057
1058 PCB_GROUP* group = static_cast<PCB_GROUP*>( bItem );
1059
1060 for( const KIID& aUuid : aGrp.memberUuids )
1061 {
1062 BOARD_ITEM* item;
1063
1064 if( m_appendToExisting )
1065 item = getItem( m_resetKIIDMap[ aUuid.AsString() ] );
1066 else
1067 item = getItem( aUuid );
1068
1069 if( item && item->Type() != NOT_USED )
1070 {
1071 switch( item->Type() )
1072 {
1073 // We used to allow fp items in non-footprint groups. It was a mistake.
1074 case PCB_FP_TEXT_T:
1075 case PCB_FP_TEXTBOX_T:
1076 case PCB_FP_SHAPE_T:
1077 case PCB_FP_ZONE_T:
1078 case PCB_PAD_T:
1079 if( item->GetParent() == group->GetParent() )
1080 group->AddItem( item );
1081
1082 break;
1083
1084 // This is the deleted item singleton, which means we didn't find the uuid.
1085 case NOT_USED:
1086 break;
1087
1088 default:
1089 group->AddItem( item );
1090 }
1091 }
1092 }
1093 }
1094
1095 // Don't allow group cycles
1096 if( m_board )
1097 m_board->GroupsSanityCheck( true );
1098}
1099
1100
1102{
1103 wxCHECK_RET( CurTok() == T_kicad_pcb,
1104 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a header." ) );
1105
1106 NeedLEFT();
1107
1108 T tok = NextTok();
1109
1110 if( tok == T_version )
1111 {
1112 m_requiredVersion = parseInt( FromUTF8().mb_str( wxConvUTF8 ) );
1114 NeedRIGHT();
1115 }
1116 else
1117 {
1118 m_requiredVersion = 20201115; // Last version before we started writing version #s
1119 // in footprint files as well as board files.
1121 }
1122
1124}
1125
1126
1128{
1129 wxCHECK_RET( CurTok() == T_general,
1130 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
1131 wxT( " as a general section." ) );
1132
1133 T token;
1134
1135 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1136 {
1137 if( token != T_LEFT )
1138 Expecting( T_LEFT );
1139
1140 token = NextTok();
1141
1142 switch( token )
1143 {
1144 case T_thickness:
1146 NeedRIGHT();
1147 break;
1148
1149 default: // Skip everything but the board thickness.
1150 while( ( token = NextTok() ) != T_RIGHT )
1151 {
1152 if( !IsSymbol( token ) && token != T_NUMBER )
1153 Expecting( "symbol or number" );
1154 }
1155 }
1156 }
1157}
1158
1159
1161{
1162 wxCHECK_RET( ( CurTok() == T_page && m_requiredVersion <= 20200119 ) || CurTok() == T_paper,
1163 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a PAGE_INFO." ) );
1164
1165 T token;
1166 PAGE_INFO pageInfo;
1167
1168 NeedSYMBOL();
1169
1170 wxString pageType = FromUTF8();
1171
1172 if( !pageInfo.SetType( pageType ) )
1173 {
1174 wxString err;
1175 err.Printf( _( "Page type '%s' is not valid." ), FromUTF8() );
1176 THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
1177 }
1178
1179 if( pageType == PAGE_INFO::Custom )
1180 {
1181 double width = parseDouble( "width" ); // width in mm
1182
1183 const double Mils2mm = 0.0254;
1184
1185 // Perform some controls to avoid crashes if the size is edited by hands
1186 if( width < MIN_PAGE_SIZE_MILS*Mils2mm )
1188 else if( width > MAX_PAGE_SIZE_PCBNEW_MILS*Mils2mm )
1190
1191 double height = parseDouble( "height" ); // height in mm
1192
1193 if( height < MIN_PAGE_SIZE_MILS*Mils2mm )
1194 height = MIN_PAGE_SIZE_MILS*Mils2mm;
1195 else if( height > MAX_PAGE_SIZE_PCBNEW_MILS*Mils2mm )
1197
1198 pageInfo.SetWidthMils( EDA_UNIT_UTILS::Mm2mils( width ) );
1199 pageInfo.SetHeightMils( EDA_UNIT_UTILS::Mm2mils( height ) );
1200 }
1201
1202 token = NextTok();
1203
1204 if( token == T_portrait )
1205 {
1206 pageInfo.SetPortrait( true );
1207 NeedRIGHT();
1208 }
1209 else if( token != T_RIGHT )
1210 {
1211 Expecting( "portrait|)" );
1212 }
1213
1214 m_board->SetPageSettings( pageInfo );
1215}
1216
1217
1219{
1220 wxCHECK_RET( CurTok() == T_title_block,
1221 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as TITLE_BLOCK." ) );
1222
1223 T token;
1224 TITLE_BLOCK titleBlock;
1225
1226 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1227 {
1228 if( token != T_LEFT )
1229 Expecting( T_LEFT );
1230
1231 token = NextTok();
1232
1233 switch( token )
1234 {
1235 case T_title:
1236 NextTok();
1237 titleBlock.SetTitle( FromUTF8() );
1238 break;
1239
1240 case T_date:
1241 NextTok();
1242 titleBlock.SetDate( FromUTF8() );
1243 break;
1244
1245 case T_rev:
1246 NextTok();
1247 titleBlock.SetRevision( FromUTF8() );
1248 break;
1249
1250 case T_company:
1251 NextTok();
1252 titleBlock.SetCompany( FromUTF8() );
1253 break;
1254
1255 case T_comment:
1256 {
1257 int commentNumber = parseInt( "comment" );
1258
1259 switch( commentNumber )
1260 {
1261 case 1:
1262 NextTok();
1263 titleBlock.SetComment( 0, FromUTF8() );
1264 break;
1265
1266 case 2:
1267 NextTok();
1268 titleBlock.SetComment( 1, FromUTF8() );
1269 break;
1270
1271 case 3:
1272 NextTok();
1273 titleBlock.SetComment( 2, FromUTF8() );
1274 break;
1275
1276 case 4:
1277 NextTok();
1278 titleBlock.SetComment( 3, FromUTF8() );
1279 break;
1280
1281 case 5:
1282 NextTok();
1283 titleBlock.SetComment( 4, FromUTF8() );
1284 break;
1285
1286 case 6:
1287 NextTok();
1288 titleBlock.SetComment( 5, FromUTF8() );
1289 break;
1290
1291 case 7:
1292 NextTok();
1293 titleBlock.SetComment( 6, FromUTF8() );
1294 break;
1295
1296 case 8:
1297 NextTok();
1298 titleBlock.SetComment( 7, FromUTF8() );
1299 break;
1300
1301 case 9:
1302 NextTok();
1303 titleBlock.SetComment( 8, FromUTF8() );
1304 break;
1305
1306 default:
1307 wxString err;
1308 err.Printf( wxT( "%d is not a valid title block comment number" ), commentNumber );
1309 THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
1310 }
1311
1312 break;
1313 }
1314
1315 default:
1316 Expecting( "title, date, rev, company, or comment" );
1317 }
1318
1319 NeedRIGHT();
1320 }
1321
1322 m_board->SetTitleBlock( titleBlock );
1323}
1324
1325
1327{
1328 T token;
1329
1330 std::string name;
1331 std::string userName;
1332 std::string type;
1333 bool isVisible = true;
1334
1335 aLayer->clear();
1336
1337 if( CurTok() != T_LEFT )
1338 Expecting( T_LEFT );
1339
1340 // this layer_num is not used, we DO depend on LAYER_T however.
1341 int layer_num = parseInt( "layer index" );
1342
1343 NeedSYMBOLorNUMBER();
1344 name = CurText();
1345
1346 NeedSYMBOL();
1347 type = CurText();
1348
1349 token = NextTok();
1350
1351 // @todo Figure out why we are looking for a hide token in the layer definition.
1352 if( token == T_hide )
1353 {
1354 isVisible = false;
1355 NeedRIGHT();
1356 }
1357 else if( token == T_STRING )
1358 {
1359 userName = CurText();
1360 NeedRIGHT();
1361 }
1362 else if( token != T_RIGHT )
1363 {
1364 Expecting( "hide, user defined name, or )" );
1365 }
1366
1367 aLayer->m_name = FROM_UTF8( name.c_str() );
1368 aLayer->m_type = LAYER::ParseType( type.c_str() );
1369 aLayer->m_number = layer_num;
1370 aLayer->m_visible = isVisible;
1371
1372 if( !userName.empty() )
1373 aLayer->m_userName = FROM_UTF8( userName.c_str() );
1374
1375 // The canonical name will get reset back to the default for copper layer on the next
1376 // save. The user defined name is now a separate optional layer token from the canonical
1377 // name.
1378 if( aLayer->m_name != LSET::Name( static_cast<PCB_LAYER_ID>( aLayer->m_number ) ) )
1379 aLayer->m_userName = aLayer->m_name;
1380}
1381
1382
1384{
1385 T token;
1386 wxString name;
1387 int dielectric_idx = 1; // the index of dielectric layers
1389
1390 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1391 {
1392 if( CurTok() != T_LEFT )
1393 Expecting( T_LEFT );
1394
1395 token = NextTok();
1396
1397 if( token != T_layer )
1398 {
1399 switch( token )
1400 {
1401 case T_copper_finish:
1402 NeedSYMBOL();
1403 stackup.m_FinishType = FromUTF8();
1404 NeedRIGHT();
1405 break;
1406
1407 case T_edge_plating:
1408 token = NextTok();
1409 stackup.m_EdgePlating = token == T_yes;
1410 NeedRIGHT();
1411 break;
1412
1413 case T_dielectric_constraints:
1414 token = NextTok();
1415 stackup.m_HasDielectricConstrains = token == T_yes;
1416 NeedRIGHT();
1417 break;
1418
1419 case T_edge_connector:
1420 token = NextTok();
1422
1423 if( token == T_yes )
1425 else if( token == T_bevelled )
1427
1428 NeedRIGHT();
1429 break;
1430
1431 case T_castellated_pads:
1432 token = NextTok();
1433 stackup.m_CastellatedPads = token == T_yes;
1434 NeedRIGHT();
1435 break;
1436
1437 default:
1438 // Currently, skip this item if not defined, because the stackup def
1439 // is a moving target
1440 //Expecting( "copper_finish, edge_plating, dielectric_constrains, edge_connector, castellated_pads" );
1441 skipCurrent();
1442 break;
1443 }
1444
1445 continue;
1446 }
1447
1448 NeedSYMBOL();
1449 name = FromUTF8();
1450
1451 // init the layer id. For dielectric, layer id = UNDEFINED_LAYER
1452 PCB_LAYER_ID layerId = m_board->GetLayerID( name );
1453
1454 // Init the type
1456
1457 if( layerId == F_SilkS || layerId == B_SilkS )
1459 else if( layerId == F_Mask || layerId == B_Mask )
1461 else if( layerId == F_Paste || layerId == B_Paste )
1463 else if( layerId == UNDEFINED_LAYER )
1465 else if( layerId >= F_Cu && layerId <= B_Cu )
1466 type = BS_ITEM_TYPE_COPPER;
1467
1468 BOARD_STACKUP_ITEM* item = nullptr;
1469
1470 if( type != BS_ITEM_TYPE_UNDEFINED )
1471 {
1472 item = new BOARD_STACKUP_ITEM( type );
1473 item->SetBrdLayerId( layerId );
1474
1475 if( type == BS_ITEM_TYPE_DIELECTRIC )
1476 item->SetDielectricLayerId( dielectric_idx++ );
1477
1478 stackup.Add( item );
1479 }
1480 else
1481 {
1482 Expecting( "layer_name" );
1483 }
1484
1485 bool has_next_sublayer = true;
1486 int sublayer_idx = 0; // the index of dielectric sub layers
1487 // sublayer 0 is always existing (main sublayer)
1488
1489 while( has_next_sublayer )
1490 {
1491 has_next_sublayer = false;
1492
1493 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1494 {
1495 if( token == T_addsublayer )
1496 {
1497 has_next_sublayer = true;
1498 break;
1499 }
1500
1501 if( token == T_LEFT )
1502 {
1503 token = NextTok();
1504
1505 switch( token )
1506 {
1507 case T_type:
1508 NeedSYMBOL();
1509 item->SetTypeName( FromUTF8() );
1510 NeedRIGHT();
1511 break;
1512
1513 case T_thickness:
1514 item->SetThickness( parseBoardUnits( T_thickness ), sublayer_idx );
1515 token = NextTok();
1516
1517 if( token == T_LEFT )
1518 break;
1519
1520 if( token == T_locked )
1521 {
1522 // Dielectric thickness can be locked (for impedance controlled layers)
1523 if( type == BS_ITEM_TYPE_DIELECTRIC )
1524 item->SetThicknessLocked( true, sublayer_idx );
1525
1526 NeedRIGHT();
1527 }
1528
1529 break;
1530
1531 case T_material:
1532 NeedSYMBOL();
1533 item->SetMaterial( FromUTF8(), sublayer_idx );
1534 NeedRIGHT();
1535 break;
1536
1537 case T_epsilon_r:
1538 NextTok();
1539 item->SetEpsilonR( parseDouble(), sublayer_idx );
1540 NeedRIGHT();
1541 break;
1542
1543 case T_loss_tangent:
1544 NextTok();
1545 item->SetLossTangent( parseDouble(), sublayer_idx );
1546 NeedRIGHT();
1547 break;
1548
1549 case T_color:
1550 NeedSYMBOL();
1551 name = FromUTF8();
1552
1553 // Older versions didn't store opacity with custom colors
1554 if( name.StartsWith( wxT( "#" ) ) && m_requiredVersion < 20210824 )
1555 {
1557
1558 if( item->GetType() == BS_ITEM_TYPE_SOLDERMASK )
1559 color = color.WithAlpha( DEFAULT_SOLDERMASK_OPACITY );
1560 else
1561 color = color.WithAlpha( 1.0 );
1562
1563 wxColour wx_color = color.ToColour();
1564
1565 // Open-code wxColour::GetAsString() because 3.0 doesn't handle rgba
1566 name.Printf( wxT("#%02X%02X%02X%02X" ),
1567 wx_color.Red(),
1568 wx_color.Green(),
1569 wx_color.Blue(),
1570 wx_color.Alpha() );
1571 }
1572
1573 item->SetColor( name );
1574 NeedRIGHT();
1575 break;
1576
1577 default:
1578 // Currently, skip this item if not defined, because the stackup def
1579 // is a moving target
1580 //Expecting( "type, thickness, material, epsilon_r, loss_tangent, color" );
1581 skipCurrent();
1582 }
1583 }
1584 }
1585
1586 if( has_next_sublayer ) // Prepare reading the next sublayer description
1587 {
1588 sublayer_idx++;
1589 item->AddDielectricPrms( sublayer_idx );
1590 }
1591 }
1592 }
1593
1594 if( token != T_RIGHT )
1595 {
1596 Expecting( ")" );
1597 }
1598
1599 // Success:
1601}
1602
1603
1604void PCB_PARSER::createOldLayerMapping( std::unordered_map< std::string, std::string >& aMap )
1605{
1606 // N.B. This mapping only includes Italian, Polish and French as they were the only languages
1607 // that mapped the layer names as of cc2022b1ac739aa673d2a0b7a2047638aa7a47b3 (kicad-i18n)
1608 // when the bug was fixed in KiCad source.
1609
1610 // Italian
1611 aMap["Adesivo.Retro"] = "B.Adhes";
1612 aMap["Adesivo.Fronte"] = "F.Adhes";
1613 aMap["Pasta.Retro"] = "B.Paste";
1614 aMap["Pasta.Fronte"] = "F.Paste";
1615 aMap["Serigrafia.Retro"] = "B.SilkS";
1616 aMap["Serigrafia.Fronte"] = "F.SilkS";
1617 aMap["Maschera.Retro"] = "B.Mask";
1618 aMap["Maschera.Fronte"] = "F.Mask";
1619 aMap["Grafica"] = "Dwgs.User";
1620 aMap["Commenti"] = "Cmts.User";
1621 aMap["Eco1"] = "Eco1.User";
1622 aMap["Eco2"] = "Eco2.User";
1623 aMap["Contorno.scheda"] = "Edge.Cuts";
1624
1625 // Polish
1626 aMap["Kleju_Dolna"] = "B.Adhes";
1627 aMap["Kleju_Gorna"] = "F.Adhes";
1628 aMap["Pasty_Dolna"] = "B.Paste";
1629 aMap["Pasty_Gorna"] = "F.Paste";
1630 aMap["Opisowa_Dolna"] = "B.SilkS";
1631 aMap["Opisowa_Gorna"] = "F.SilkS";
1632 aMap["Maski_Dolna"] = "B.Mask";
1633 aMap["Maski_Gorna"] = "F.Mask";
1634 aMap["Rysunkowa"] = "Dwgs.User";
1635 aMap["Komentarzy"] = "Cmts.User";
1636 aMap["ECO1"] = "Eco1.User";
1637 aMap["ECO2"] = "Eco2.User";
1638 aMap["Krawedziowa"] = "Edge.Cuts";
1639
1640 // French
1641 aMap["Dessous.Adhes"] = "B.Adhes";
1642 aMap["Dessus.Adhes"] = "F.Adhes";
1643 aMap["Dessous.Pate"] = "B.Paste";
1644 aMap["Dessus.Pate"] = "F.Paste";
1645 aMap["Dessous.SilkS"] = "B.SilkS";
1646 aMap["Dessus.SilkS"] = "F.SilkS";
1647 aMap["Dessous.Masque"] = "B.Mask";
1648 aMap["Dessus.Masque"] = "F.Mask";
1649 aMap["Dessin.User"] = "Dwgs.User";
1650 aMap["Contours.Ci"] = "Edge.Cuts";
1651}
1652
1653
1655{
1656 wxCHECK_RET( CurTok() == T_layers,
1657 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as layers." ) );
1658
1659 T token;
1660 LSET visibleLayers;
1661 LSET enabledLayers;
1662 int copperLayerCount = 0;
1663 LAYER layer;
1664 bool anyHidden = false;
1665
1666 std::unordered_map< std::string, std::string > v3_layer_names;
1667 std::vector<LAYER> cu;
1668
1669 createOldLayerMapping( v3_layer_names );
1670
1671 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1672 {
1673 parseLayer( &layer );
1674
1675 if( layer.m_type == LT_UNDEFINED ) // it's a non-copper layer
1676 break;
1677
1678 cu.push_back( layer ); // it's copper
1679 }
1680
1681 // All Cu layers are parsed, but not the non-cu layers here.
1682
1683 // The original *.kicad_pcb file format and the inverted
1684 // Cu stack format both have all the Cu layers first, so use this
1685 // trick to handle either. The layer number in the (layers ..)
1686 // s-expression element are ignored.
1687 if( cu.size() )
1688 {
1689 // Rework the layer numbers, which changed when the Cu stack
1690 // was flipped. So we instead use position in the list.
1691 cu[cu.size()-1].m_number = B_Cu;
1692
1693 for( unsigned i=0; i < cu.size()-1; ++i )
1694 {
1695 cu[i].m_number = i;
1696 }
1697
1698 for( std::vector<LAYER>::const_iterator it = cu.begin(); it<cu.end(); ++it )
1699 {
1700 enabledLayers.set( it->m_number );
1701
1702 if( it->m_visible )
1703 visibleLayers.set( it->m_number );
1704 else
1705 anyHidden = true;
1706
1707 m_board->SetLayerDescr( PCB_LAYER_ID( it->m_number ), *it );
1708
1709 UTF8 name = it->m_name;
1710
1711 m_layerIndices[ name ] = PCB_LAYER_ID( it->m_number );
1712 m_layerMasks[ name ] = LSET( PCB_LAYER_ID( it->m_number ) );
1713 }
1714
1715 copperLayerCount = cu.size();
1716 }
1717
1718 // process non-copper layers
1719 while( token != T_RIGHT )
1720 {
1721 LAYER_ID_MAP::const_iterator it = m_layerIndices.find( UTF8( layer.m_name ) );
1722
1723 if( it == m_layerIndices.end() )
1724 {
1725 auto new_layer_it = v3_layer_names.find( layer.m_name.ToStdString() );
1726
1727 if( new_layer_it != v3_layer_names.end() )
1728 it = m_layerIndices.find( new_layer_it->second );
1729
1730 if( it == m_layerIndices.end() )
1731 {
1732 wxString error;
1733 error.Printf( _( "Layer '%s' in file '%s' at line %d is not in fixed layer hash." ),
1734 layer.m_name,
1735 CurSource(),
1736 CurLineNumber(),
1737 CurOffset() );
1738
1739 THROW_IO_ERROR( error );
1740 }
1741
1742 // If we are here, then we have found a translated layer name. Put it in the maps
1743 // so that items on this layer get the appropriate layer ID number.
1744 m_layerIndices[ UTF8( layer.m_name ) ] = it->second;
1745 m_layerMasks[ UTF8( layer.m_name ) ] = it->second;
1746 layer.m_name = it->first;
1747 }
1748
1749 layer.m_number = it->second;
1750 enabledLayers.set( layer.m_number );
1751
1752 if( layer.m_visible )
1753 visibleLayers.set( layer.m_number );
1754 else
1755 anyHidden = true;
1756
1757 m_board->SetLayerDescr( it->second, layer );
1758
1759 token = NextTok();
1760
1761 if( token != T_LEFT )
1762 break;
1763
1764 parseLayer( &layer );
1765 }
1766
1767 // We need at least 2 copper layers and there must be an even number of them.
1768 if( copperLayerCount < 2 || (copperLayerCount % 2) != 0 )
1769 {
1770 wxString err = wxString::Format( _( "%d is not a valid layer count" ), copperLayerCount );
1771
1772 THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
1773 }
1774
1775 m_board->SetCopperLayerCount( copperLayerCount );
1776 m_board->SetEnabledLayers( enabledLayers );
1777
1778 // Only set this if any layers were explicitly marked as hidden. Otherwise, we want to leave
1779 // this alone; default visibility will show everything
1780 if( anyHidden )
1781 m_board->m_LegacyVisibleLayers = visibleLayers;
1782}
1783
1784
1785template<class T, class M>
1786T PCB_PARSER::lookUpLayer( const M& aMap )
1787{
1788 // avoid constructing another std::string, use lexer's directly
1789 typename M::const_iterator it = aMap.find( curText );
1790
1791 if( it == aMap.end() )
1792 {
1793 m_undefinedLayers.insert( curText );
1794 return Rescue;
1795 }
1796
1797 // Some files may have saved items to the Rescue Layer due to an issue in v5
1798 if( it->second == Rescue )
1799 m_undefinedLayers.insert( curText );
1800
1801 return it->second;
1802}
1803
1804
1806{
1807 wxCHECK_MSG( CurTok() == T_layer, UNDEFINED_LAYER,
1808 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as layer." ) );
1809
1810 NextTok();
1811
1812 PCB_LAYER_ID layerIndex = lookUpLayer<PCB_LAYER_ID>( m_layerIndices );
1813
1814 // Handle closing ) in object parser.
1815
1816 return layerIndex;
1817}
1818
1819
1821{
1822 wxCHECK_MSG( CurTok() == T_layers, LSET(),
1823 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as item layers." ) );
1824
1825 LSET layerMask;
1826
1827 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
1828 {
1829 LSET mask = lookUpLayer<LSET>( m_layerMasks );
1830 layerMask |= mask;
1831 }
1832
1833 return layerMask;
1834}
1835
1836
1838{
1839 wxCHECK_RET( CurTok() == T_setup,
1840 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as setup." ) );
1841
1843 std::shared_ptr<NETCLASS>& defaultNetClass = bds.m_NetSettings->m_DefaultNetClass;
1844 ZONE_SETTINGS& zoneSettings = bds.GetDefaultZoneSettings();
1845
1846 // Missing soldermask min width value means that the user has set the value to 0 and
1847 // not the default value (0.25mm)
1848 bds.m_SolderMaskMinWidth = 0;
1849
1850 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
1851 {
1852 if( token != T_LEFT )
1853 Expecting( T_LEFT );
1854
1855 token = NextTok();
1856
1857 switch( token )
1858 {
1859 case T_stackup:
1861 break;
1862
1863 case T_last_trace_width: // not used now
1864 /* lastTraceWidth =*/ parseBoardUnits( T_last_trace_width );
1865 NeedRIGHT();
1866 break;
1867
1868 case T_user_trace_width:
1869 {
1870 // Make room for the netclass value
1871 if( bds.m_TrackWidthList.empty() )
1872 bds.m_TrackWidthList.emplace_back( 0 );
1873
1874 int trackWidth = parseBoardUnits( T_user_trace_width );
1875
1876 if( !m_appendToExisting || !alg::contains( bds.m_TrackWidthList, trackWidth ) )
1877 bds.m_TrackWidthList.push_back( trackWidth );
1878
1880 NeedRIGHT();
1881 break;
1882 }
1883
1884 case T_trace_clearance:
1885 defaultNetClass->SetClearance( parseBoardUnits( T_trace_clearance ) );
1887 NeedRIGHT();
1888 break;
1889
1890 case T_zone_clearance:
1891 zoneSettings.m_ZoneClearance = parseBoardUnits( T_zone_clearance );
1893 NeedRIGHT();
1894 break;
1895
1896 case T_zone_45_only: // legacy setting
1897 /* zoneSettings.m_Zone_45_Only = */ parseBool();
1899 NeedRIGHT();
1900 break;
1901
1902 case T_clearance_min:
1903 bds.m_MinClearance = parseBoardUnits( T_clearance_min );
1905 NeedRIGHT();
1906 break;
1907
1908 case T_trace_min:
1909 bds.m_TrackMinWidth = parseBoardUnits( T_trace_min );
1911 NeedRIGHT();
1912 break;
1913
1914 case T_via_size:
1915 defaultNetClass->SetViaDiameter( parseBoardUnits( T_via_size ) );
1917 NeedRIGHT();
1918 break;
1919
1920 case T_via_drill:
1921 defaultNetClass->SetViaDrill( parseBoardUnits( T_via_drill ) );
1923 NeedRIGHT();
1924 break;
1925
1926 case T_via_min_annulus:
1927 bds.m_ViasMinAnnularWidth = parseBoardUnits( T_via_min_annulus );
1929 NeedRIGHT();
1930 break;
1931
1932 case T_via_min_size:
1933 bds.m_ViasMinSize = parseBoardUnits( T_via_min_size );
1935 NeedRIGHT();
1936 break;
1937
1938 case T_through_hole_min:
1939 bds.m_MinThroughDrill = parseBoardUnits( T_through_hole_min );
1941 NeedRIGHT();
1942 break;
1943
1944 // Legacy token for T_through_hole_min
1945 case T_via_min_drill:
1946 bds.m_MinThroughDrill = parseBoardUnits( T_via_min_drill );
1948 NeedRIGHT();
1949 break;
1950
1951 case T_hole_to_hole_min:
1952 bds.m_HoleToHoleMin = parseBoardUnits( T_hole_to_hole_min );
1954 NeedRIGHT();
1955 break;
1956
1957 case T_user_via:
1958 {
1959 int viaSize = parseBoardUnits( "user via size" );
1960 int viaDrill = parseBoardUnits( "user via drill" );
1961 VIA_DIMENSION via( viaSize, viaDrill );
1962
1963 // Make room for the netclass value
1964 if( bds.m_ViasDimensionsList.empty() )
1965 bds.m_ViasDimensionsList.emplace_back( VIA_DIMENSION( 0, 0 ) );
1966
1968 bds.m_ViasDimensionsList.emplace_back( via );
1969
1971 NeedRIGHT();
1972 break;
1973 }
1974
1975 case T_uvia_size:
1976 defaultNetClass->SetuViaDiameter( parseBoardUnits( T_uvia_size ) );
1978 NeedRIGHT();
1979 break;
1980
1981 case T_uvia_drill:
1982 defaultNetClass->SetuViaDrill( parseBoardUnits( T_uvia_drill ) );
1984 NeedRIGHT();
1985 break;
1986
1987 case T_uvias_allowed:
1988 parseBool();
1990 NeedRIGHT();
1991 break;
1992
1993 case T_blind_buried_vias_allowed:
1994 parseBool();
1996 NeedRIGHT();
1997 break;
1998
1999 case T_uvia_min_size:
2000 bds.m_MicroViasMinSize = parseBoardUnits( T_uvia_min_size );
2002 NeedRIGHT();
2003 break;
2004
2005 case T_uvia_min_drill:
2006 bds.m_MicroViasMinDrill = parseBoardUnits( T_uvia_min_drill );
2008 NeedRIGHT();
2009 break;
2010
2011 case T_user_diff_pair:
2012 {
2013 int width = parseBoardUnits( "user diff-pair width" );
2014 int gap = parseBoardUnits( "user diff-pair gap" );
2015 int viaGap = parseBoardUnits( "user diff-pair via gap" );
2016 DIFF_PAIR_DIMENSION diffPair( width, gap, viaGap );
2017
2019 bds.m_DiffPairDimensionsList.emplace_back( diffPair );
2020
2022 NeedRIGHT();
2023 break;
2024 }
2025
2026 case T_segment_width: // note: legacy (pre-6.0) token
2027 bds.m_LineThickness[ LAYER_CLASS_COPPER ] = parseBoardUnits( T_segment_width );
2029 NeedRIGHT();
2030 break;
2031
2032 case T_edge_width: // note: legacy (pre-6.0) token
2033 bds.m_LineThickness[ LAYER_CLASS_EDGES ] = parseBoardUnits( T_edge_width );
2035 NeedRIGHT();
2036 break;
2037
2038 case T_mod_edge_width: // note: legacy (pre-6.0) token
2039 bds.m_LineThickness[ LAYER_CLASS_SILK ] = parseBoardUnits( T_mod_edge_width );
2041 NeedRIGHT();
2042 break;
2043
2044 case T_pcb_text_width: // note: legacy (pre-6.0) token
2045 bds.m_TextThickness[ LAYER_CLASS_COPPER ] = parseBoardUnits( T_pcb_text_width );
2047 NeedRIGHT();
2048 break;
2049
2050 case T_mod_text_width: // note: legacy (pre-6.0) token
2051 bds.m_TextThickness[ LAYER_CLASS_SILK ] = parseBoardUnits( T_mod_text_width );
2053 NeedRIGHT();
2054 break;
2055
2056 case T_pcb_text_size: // note: legacy (pre-6.0) token
2057 bds.m_TextSize[ LAYER_CLASS_COPPER ].x = parseBoardUnits( "pcb text width" );
2058 bds.m_TextSize[ LAYER_CLASS_COPPER ].y = parseBoardUnits( "pcb text height" );
2060 NeedRIGHT();
2061 break;
2062
2063 case T_mod_text_size: // note: legacy (pre-6.0) token
2064 bds.m_TextSize[ LAYER_CLASS_SILK ].x = parseBoardUnits( "footprint text width" );
2065 bds.m_TextSize[ LAYER_CLASS_SILK ].y = parseBoardUnits( "footprint text height" );
2067 NeedRIGHT();
2068 break;
2069
2070 case T_defaults:
2071 parseDefaults( bds );
2073 break;
2074
2075 case T_pad_size:
2076 {
2077 wxSize sz;
2078 sz.SetWidth( parseBoardUnits( "master pad width" ) );
2079 sz.SetHeight( parseBoardUnits( "master pad height" ) );
2080 bds.m_Pad_Master->SetSize( sz );
2082 NeedRIGHT();
2083 break;
2084 }
2085
2086 case T_pad_drill:
2087 {
2088 int drillSize = parseBoardUnits( T_pad_drill );
2089 bds.m_Pad_Master->SetDrillSize( wxSize( drillSize, drillSize ) );
2091 NeedRIGHT();
2092 break;
2093 }
2094
2095 case T_pad_to_mask_clearance:
2096 bds.m_SolderMaskExpansion = parseBoardUnits( T_pad_to_mask_clearance );
2097 NeedRIGHT();
2098 break;
2099
2100 case T_solder_mask_min_width:
2101 bds.m_SolderMaskMinWidth = parseBoardUnits( T_solder_mask_min_width );
2102 NeedRIGHT();
2103 break;
2104
2105 case T_pad_to_paste_clearance:
2106 bds.m_SolderPasteMargin = parseBoardUnits( T_pad_to_paste_clearance );
2107 NeedRIGHT();
2108 break;
2109
2110 case T_pad_to_paste_clearance_ratio:
2111 bds.m_SolderPasteMarginRatio = parseDouble( T_pad_to_paste_clearance_ratio );
2112 NeedRIGHT();
2113 break;
2114
2115 case T_allow_soldermask_bridges_in_footprints:
2117 NeedRIGHT();
2118 break;
2119
2120 case T_aux_axis_origin:
2121 {
2122 int x = parseBoardUnits( "auxiliary origin X" );
2123 int y = parseBoardUnits( "auxiliary origin Y" );
2124 bds.SetAuxOrigin( VECTOR2I( x, y ) );
2125
2126 // Aux origin still stored in board for the moment
2127 //m_board->m_LegacyDesignSettingsLoaded = true;
2128 NeedRIGHT();
2129 break;
2130 }
2131
2132 case T_grid_origin:
2133 {
2134 int x = parseBoardUnits( "grid origin X" );
2135 int y = parseBoardUnits( "grid origin Y" );
2136 bds.SetGridOrigin( VECTOR2I( x, y ) );
2137 // Grid origin still stored in board for the moment
2138 //m_board->m_LegacyDesignSettingsLoaded = true;
2139 NeedRIGHT();
2140 break;
2141 }
2142
2143 // Stored in board prior to 6.0
2144 case T_visible_elements:
2145 {
2146 // Make sure to start with DefaultVisible so all new layers are set
2148
2149 int visible = parseHex() | MIN_VISIBILITY_MASK;
2150
2151 for( size_t i = 0; i < sizeof( int ) * CHAR_BIT; i++ )
2152 m_board->m_LegacyVisibleItems.set( i, visible & ( 1u << i ) );
2153
2154 NeedRIGHT();
2155 break;
2156 }
2157
2158 case T_max_error:
2159 bds.m_MaxError = parseBoardUnits( T_max_error );
2161 NeedRIGHT();
2162 break;
2163
2164 case T_filled_areas_thickness:
2165 if( parseBool() )
2166 {
2168 {
2169 // Thick outline fill mode no longer supported. Make sure user is OK with
2170 // converting fills.
2171 KIDIALOG dlg( nullptr, _( "The legacy zone fill strategy is no longer "
2172 "supported.\nConvert zones to smoothed polygon "
2173 "fills?" ),
2174 _( "Legacy Zone Warning" ), wxYES_NO | wxICON_WARNING );
2175
2176 dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
2177
2178 if( dlg.ShowModal() == wxID_NO )
2179 THROW_IO_ERROR( wxT( "CANCEL" ) );
2180
2182 }
2183 }
2184
2185 NeedRIGHT();
2186 break;
2187
2188 case T_pcbplotparams:
2189 {
2190 PCB_PLOT_PARAMS plotParams;
2191 PCB_PLOT_PARAMS_PARSER parser( reader );
2192 // parser must share the same current line as our current PCB parser
2193 // synchronize it.
2194 parser.SyncLineReaderWith( *this );
2195
2196 plotParams.Parse( &parser );
2197 SyncLineReaderWith( parser );
2198
2199 m_board->SetPlotOptions( plotParams );
2200 break;
2201 }
2202
2203 default:
2204 Unexpected( CurText() );
2205 }
2206 }
2207}
2208
2209
2211{
2212 T token;
2213
2214 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2215 {
2216 if( token != T_LEFT )
2217 Expecting( T_LEFT );
2218
2219 token = NextTok();
2220
2221 switch( token )
2222 {
2223 case T_edge_clearance:
2224 designSettings.m_CopperEdgeClearance = parseBoardUnits( T_edge_clearance );
2226 NeedRIGHT();
2227 break;
2228
2229 case T_copper_line_width:
2230 designSettings.m_LineThickness[ LAYER_CLASS_COPPER ] = parseBoardUnits( token );
2231 NeedRIGHT();
2232 break;
2233
2234 case T_copper_text_dims:
2235 parseDefaultTextDims( designSettings, LAYER_CLASS_COPPER );
2236 break;
2237
2238 case T_courtyard_line_width:
2239 designSettings.m_LineThickness[ LAYER_CLASS_COURTYARD ] = parseBoardUnits( token );
2240 NeedRIGHT();
2241 break;
2242
2243 case T_edge_cuts_line_width:
2244 designSettings.m_LineThickness[ LAYER_CLASS_EDGES ] = parseBoardUnits( token );
2245 NeedRIGHT();
2246 break;
2247
2248 case T_silk_line_width:
2249 designSettings.m_LineThickness[ LAYER_CLASS_SILK ] = parseBoardUnits( token );
2250 NeedRIGHT();
2251 break;
2252
2253 case T_silk_text_dims:
2254 parseDefaultTextDims( designSettings, LAYER_CLASS_SILK );
2255 break;
2256
2257 case T_fab_layers_line_width:
2258 designSettings.m_LineThickness[ LAYER_CLASS_FAB ] = parseBoardUnits( token );
2259 NeedRIGHT();
2260 break;
2261
2262 case T_fab_layers_text_dims:
2263 parseDefaultTextDims( designSettings, LAYER_CLASS_FAB );
2264 break;
2265
2266 case T_other_layers_line_width:
2267 designSettings.m_LineThickness[ LAYER_CLASS_OTHERS ] = parseBoardUnits( token );
2268 NeedRIGHT();
2269 break;
2270
2271 case T_other_layers_text_dims:
2272 parseDefaultTextDims( designSettings, LAYER_CLASS_OTHERS );
2273 break;
2274
2275 case T_dimension_units:
2276 designSettings.m_DimensionUnitsMode =
2277 static_cast<DIM_UNITS_MODE>( parseInt( "dimension units" ) );
2278 NeedRIGHT();
2279 break;
2280
2281 case T_dimension_precision:
2282 designSettings.m_DimensionPrecision = parseInt( "dimension precision" );
2283 NeedRIGHT();
2284 break;
2285
2286 default:
2287 Unexpected( CurText() );
2288 }
2289 }
2290}
2291
2292
2294{
2295 T token;
2296
2297 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2298 {
2299 if( token == T_LEFT )
2300 token = NextTok();
2301
2302 switch( token )
2303 {
2304 case T_size:
2305 aSettings.m_TextSize[ aLayer ].x = parseBoardUnits( "default text size X" );
2306 aSettings.m_TextSize[ aLayer ].y = parseBoardUnits( "default text size Y" );
2307 NeedRIGHT();
2308 break;
2309
2310 case T_thickness:
2311 aSettings.m_TextThickness[ aLayer ] = parseBoardUnits( "default text width" );
2312 NeedRIGHT();
2313 break;
2314
2315 case T_italic:
2316 aSettings.m_TextItalic[ aLayer ] = true;
2317 break;
2318
2319 case T_keep_upright:
2320 aSettings.m_TextUpright[ aLayer ] = true;
2321 break;
2322
2323 default:
2324 Expecting( "size, thickness, italic or keep_upright" );
2325 }
2326 }
2327}
2328
2329
2331{
2332 wxCHECK_RET( CurTok() == T_net,
2333 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as net." ) );
2334
2335 int netCode = parseInt( "net number" );
2336
2337 NeedSYMBOLorNUMBER();
2338 wxString name = FromUTF8();
2339
2340 // Convert overbar syntax from `~...~` to `~{...}`. These were left out of the first merge
2341 // so the version is a bit later.
2342 if( m_requiredVersion < 20210606 )
2344
2345 NeedRIGHT();
2346
2347 // net 0 should be already in list, so store this net
2348 // if it is not the net 0, or if the net 0 does not exists.
2349 // (TODO: a better test.)
2351 {
2352 NETINFO_ITEM* net = new NETINFO_ITEM( m_board, name, netCode );
2353 m_board->Add( net, ADD_MODE::INSERT, true );
2354
2355 // Store the new code mapping
2356 pushValueIntoMap( netCode, net->GetNetCode() );
2357 }
2358}
2359
2360
2362{
2363 wxCHECK_RET( CurTok() == T_net_class,
2364 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as net class." ) );
2365
2366 T token;
2367
2368 std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( wxEmptyString );
2369
2370 // Read netclass name (can be a name or just a number like track width)
2371 NeedSYMBOLorNUMBER();
2372 nc->SetName( FromUTF8() );
2373 NeedSYMBOL();
2374 nc->SetDescription( FromUTF8() );
2375
2376 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2377 {
2378 if( token != T_LEFT )
2379 Expecting( T_LEFT );
2380
2381 token = NextTok();
2382
2383 switch( token )
2384 {
2385 case T_clearance:
2386 nc->SetClearance( parseBoardUnits( T_clearance ) );
2387 break;
2388
2389 case T_trace_width:
2390 nc->SetTrackWidth( parseBoardUnits( T_trace_width ) );
2391 break;
2392
2393 case T_via_dia:
2394 nc->SetViaDiameter( parseBoardUnits( T_via_dia ) );
2395 break;
2396
2397 case T_via_drill:
2398 nc->SetViaDrill( parseBoardUnits( T_via_drill ) );
2399 break;
2400
2401 case T_uvia_dia:
2402 nc->SetuViaDiameter( parseBoardUnits( T_uvia_dia ) );
2403 break;
2404
2405 case T_uvia_drill:
2406 nc->SetuViaDrill( parseBoardUnits( T_uvia_drill ) );
2407 break;
2408
2409 case T_diff_pair_width:
2410 nc->SetDiffPairWidth( parseBoardUnits( T_diff_pair_width ) );
2411 break;
2412
2413 case T_diff_pair_gap:
2414 nc->SetDiffPairGap( parseBoardUnits( T_diff_pair_gap ) );
2415 break;
2416
2417 case T_add_net:
2418 {
2419 NeedSYMBOLorNUMBER();
2420
2421 wxString netName = FromUTF8();
2422
2423 // Convert overbar syntax from `~...~` to `~{...}`. These were left out of the
2424 // first merge so the version is a bit later.
2425 if( m_requiredVersion < 20210606 )
2426 netName = ConvertToNewOverbarNotation( FromUTF8() );
2427
2428 m_board->GetDesignSettings().m_NetSettings->m_NetClassPatternAssignments.push_back(
2429 {
2430 std::make_unique<EDA_COMBINED_MATCHER>( netName, CTX_NETCLASS ),
2431 nc->GetName()
2432 } );
2433
2434 break;
2435 }
2436
2437 default:
2438 Expecting( "clearance, trace_width, via_dia, via_drill, uvia_dia, uvia_drill, "
2439 "diff_pair_width, diff_pair_gap or add_net" );
2440 }
2441
2442 NeedRIGHT();
2443 }
2444
2445 if( m_board->GetDesignSettings().m_NetSettings->m_NetClasses.count( nc->GetName() ) )
2446 {
2447 // Must have been a name conflict, this is a bad board file.
2448 // User may have done a hand edit to the file.
2449 wxString error;
2450 error.Printf( _( "Duplicate NETCLASS name '%s' in file '%s' at line %d, offset %d." ),
2451 nc->GetName().GetData(), CurSource().GetData(), CurLineNumber(),
2452 CurOffset() );
2453 THROW_IO_ERROR( error );
2454 }
2455 else
2456 {
2457 m_board->GetDesignSettings().m_NetSettings->m_NetClasses[ nc->GetName() ] = nc;
2458 }
2459}
2460
2461
2463{
2464 wxCHECK_MSG( CurTok() == T_gr_arc || CurTok() == T_gr_circle || CurTok() == T_gr_curve ||
2465 CurTok() == T_gr_rect || CurTok() == T_gr_bbox || CurTok() == T_gr_line ||
2466 CurTok() == T_gr_poly, nullptr,
2467 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_SHAPE." ) );
2468
2469 T token;
2470 VECTOR2I pt;
2472 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( nullptr );
2473
2474 switch( CurTok() )
2475 {
2476 case T_gr_arc:
2477 shape->SetShape( SHAPE_T::ARC );
2478 token = NextTok();
2479
2480 if( token == T_locked )
2481 {
2482 shape->SetLocked( true );
2483 token = NextTok();
2484 }
2485
2486 if( token != T_LEFT )
2487 Expecting( T_LEFT );
2488
2489 token = NextTok();
2490
2492 {
2493 // In legacy files the start keyword actually gives the arc center...
2494 if( token != T_start )
2495 Expecting( T_start );
2496
2497 pt.x = parseBoardUnits( "X coordinate" );
2498 pt.y = parseBoardUnits( "Y coordinate" );
2499 shape->SetCenter( pt );
2500 NeedRIGHT();
2501 NeedLEFT();
2502 token = NextTok();
2503
2504 // ... and the end keyword gives the start point of the arc
2505 if( token != T_end )
2506 Expecting( T_end );
2507
2508 pt.x = parseBoardUnits( "X coordinate" );
2509 pt.y = parseBoardUnits( "Y coordinate" );
2510 shape->SetStart( pt );
2511 NeedRIGHT();
2512 }
2513 else
2514 {
2515 VECTOR2I arc_start, arc_mid, arc_end;
2516
2517 if( token != T_start )
2518 Expecting( T_start );
2519
2520 arc_start.x = parseBoardUnits( "X coordinate" );
2521 arc_start.y = parseBoardUnits( "Y coordinate" );
2522 NeedRIGHT();
2523 NeedLEFT();
2524 token = NextTok();
2525
2526 if( token != T_mid )
2527 Expecting( T_mid );
2528
2529 arc_mid.x = parseBoardUnits( "X coordinate" );
2530 arc_mid.y = parseBoardUnits( "Y coordinate" );
2531 NeedRIGHT();
2532 NeedLEFT();
2533 token = NextTok();
2534
2535 if( token != T_end )
2536 Expecting( T_end );
2537
2538 arc_end.x = parseBoardUnits( "X coordinate" );
2539 arc_end.y = parseBoardUnits( "Y coordinate" );
2540 NeedRIGHT();
2541
2542 shape->SetArcGeometry( arc_start, arc_mid, arc_end );
2543 }
2544
2545 break;
2546
2547 case T_gr_circle:
2548 shape->SetShape( SHAPE_T::CIRCLE );
2549 token = NextTok();
2550
2551 if( token == T_locked )
2552 {
2553 shape->SetLocked( true );
2554 token = NextTok();
2555 }
2556
2557 if( token != T_LEFT )
2558 Expecting( T_LEFT );
2559
2560 token = NextTok();
2561
2562 if( token != T_center )
2563 Expecting( T_center );
2564
2565 pt.x = parseBoardUnits( "X coordinate" );
2566 pt.y = parseBoardUnits( "Y coordinate" );
2567 shape->SetStart( pt );
2568 NeedRIGHT();
2569 NeedLEFT();
2570
2571 token = NextTok();
2572
2573 if( token != T_end )
2574 Expecting( T_end );
2575
2576 pt.x = parseBoardUnits( "X coordinate" );
2577 pt.y = parseBoardUnits( "Y coordinate" );
2578 shape->SetEnd( pt );
2579 NeedRIGHT();
2580 break;
2581
2582 case T_gr_curve:
2583 shape->SetShape( SHAPE_T::BEZIER );
2584 token = NextTok();
2585
2586 if( token == T_locked )
2587 {
2588 shape->SetLocked( true );
2589 token = NextTok();
2590 }
2591
2592 if( token != T_LEFT )
2593 Expecting( T_LEFT );
2594
2595 token = NextTok();
2596
2597 if( token != T_pts )
2598 Expecting( T_pts );
2599
2600 shape->SetStart( parseXY() );
2601 shape->SetBezierC1( parseXY());
2602 shape->SetBezierC2( parseXY());
2603 shape->SetEnd( parseXY() );
2604 NeedRIGHT();
2605 break;
2606
2607 case T_gr_bbox:
2608 case T_gr_rect:
2609 shape->SetShape( SHAPE_T::RECT );
2610 token = NextTok();
2611
2612 if( token == T_locked )
2613 {
2614 shape->SetLocked( true );
2615 token = NextTok();
2616 }
2617
2618 if( token != T_LEFT )
2619 Expecting( T_LEFT );
2620
2621 token = NextTok();
2622
2623 if( token != T_start )
2624 Expecting( T_start );
2625
2626 pt.x = parseBoardUnits( "X coordinate" );
2627 pt.y = parseBoardUnits( "Y coordinate" );
2628 shape->SetStart( pt );
2629 NeedRIGHT();
2630 NeedLEFT();
2631 token = NextTok();
2632
2633 if( token != T_end )
2634 Expecting( T_end );
2635
2636 pt.x = parseBoardUnits( "X coordinate" );
2637 pt.y = parseBoardUnits( "Y coordinate" );
2638 shape->SetEnd( pt );
2639 shape->NormalizeRect();
2640 NeedRIGHT();
2641 break;
2642
2643 case T_gr_line:
2644 // Default PCB_SHAPE type is S_SEGMENT.
2645 token = NextTok();
2646
2647 if( token == T_locked )
2648 {
2649 shape->SetLocked( true );
2650 token = NextTok();
2651 }
2652
2653 if( token != T_LEFT )
2654 Expecting( T_LEFT );
2655
2656 token = NextTok();
2657
2658 if( token != T_start )
2659 Expecting( T_start );
2660
2661 pt.x = parseBoardUnits( "X coordinate" );
2662 pt.y = parseBoardUnits( "Y coordinate" );
2663 shape->SetStart( pt );
2664 NeedRIGHT();
2665 NeedLEFT();
2666 token = NextTok();
2667
2668 if( token != T_end )
2669 Expecting( T_end );
2670
2671 pt.x = parseBoardUnits( "X coordinate" );
2672 pt.y = parseBoardUnits( "Y coordinate" );
2673 shape->SetEnd( pt );
2674 NeedRIGHT();
2675 break;
2676
2677 case T_gr_poly:
2678 {
2679 shape->SetShape( SHAPE_T::POLY );
2680 shape->SetPolyPoints( {} );
2681
2682 SHAPE_LINE_CHAIN& outline = shape->GetPolyShape().Outline( 0 );
2683
2684 token = NextTok();
2685
2686 if( token == T_locked )
2687 {
2688 shape->SetLocked( true );
2689 token = NextTok();
2690 }
2691
2692 if( token != T_LEFT )
2693 Expecting( T_LEFT );
2694
2695 token = NextTok();
2696
2697 if( token != T_pts )
2698 Expecting( T_pts );
2699
2700 while( (token = NextTok() ) != T_RIGHT )
2701 {
2702 parseOutlinePoints( outline );
2703 }
2704
2705 break;
2706 }
2707
2708 default:
2709 Expecting( "gr_arc, gr_circle, gr_curve, gr_line, gr_poly, gr_rect or gr_bbox" );
2710 }
2711
2712 bool foundFill = false;
2713
2714 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2715 {
2716 if( token != T_LEFT )
2717 Expecting( T_LEFT );
2718
2719 token = NextTok();
2720
2721 switch( token )
2722 {
2723 case T_angle:
2725 {
2726 EDA_ANGLE angle( parseDouble( "arc angle" ), DEGREES_T );
2727
2728 if( shape->GetShape() == SHAPE_T::ARC )
2729 shape->SetArcAngleAndEnd( angle, true );
2730
2731 NeedRIGHT();
2732 }
2733 else
2734 {
2735 Unexpected( T_angle );
2736 }
2737
2738 break;
2739
2740 case T_layer:
2741 shape->SetLayer( parseBoardItemLayer() );
2742 NeedRIGHT();
2743 break;
2744
2745 case T_width: // legacy token
2746 stroke.SetWidth( parseBoardUnits( T_width ) );
2747 NeedRIGHT();
2748 break;
2749
2750 case T_stroke:
2751 {
2752 STROKE_PARAMS_PARSER strokeParser( reader, pcbIUScale.IU_PER_MM );
2753 strokeParser.SyncLineReaderWith( *this );
2754
2755 strokeParser.ParseStroke( stroke );
2756 SyncLineReaderWith( strokeParser );
2757 break;
2758 }
2759
2760 case T_tstamp:
2761 NextTok();
2762 const_cast<KIID&>( shape->m_Uuid ) = CurStrToKIID();
2763 NeedRIGHT();
2764 break;
2765
2766 case T_fill:
2767 foundFill = true;
2768
2769 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2770 {
2771 if( token == T_LEFT )
2772 token = NextTok();
2773
2774 switch( token )
2775 {
2776 // T_yes was used to indicate filling when first introduced,
2777 // so treat it like a solid fill since that was the only fill available
2778 case T_yes:
2779 case T_solid:
2780 shape->SetFilled( true );
2781 break;
2782
2783 case T_none:
2784 shape->SetFilled( false );
2785 break;
2786
2787 default:
2788 Expecting( "yes, none, solid" );
2789 }
2790 }
2791
2792 break;
2793
2794 // We continue to parse the status field but it is no longer written
2795 case T_status:
2796 shape->SetStatus( static_cast<EDA_ITEM_FLAGS>( parseHex() ) );
2797 NeedRIGHT();
2798 break;
2799
2800 // Continue to process "(locked)" format which was output during 5.99 development
2801 case T_locked:
2802 shape->SetLocked( true );
2803 NeedRIGHT();
2804 break;
2805
2806 default:
2807 Expecting( "layer, width, fill, tstamp, locked or status" );
2808 }
2809 }
2810
2811 if( !foundFill )
2812 {
2813 // Legacy versions didn't have a filled flag but allowed some shapes to indicate they
2814 // should be filled by specifying a 0 stroke-width.
2815 if( stroke.GetWidth() == 0
2816 && ( shape->GetShape() == SHAPE_T::RECT || shape->GetShape() == SHAPE_T::CIRCLE ) )
2817 {
2818 shape->SetFilled( true );
2819 }
2820 else if( shape->GetShape() == SHAPE_T::POLY && shape->GetLayer() != Edge_Cuts )
2821 {
2822 // Polygons on non-Edge_Cuts layers were always filled.
2823 shape->SetFilled( true );
2824 }
2825 }
2826
2827 // Only filled shapes may have a zero line-width. This is not permitted in KiCad but some
2828 // external tools can generate invalid files.
2829 if( stroke.GetWidth() <= 0 && !shape->IsFilled() )
2830 {
2832 }
2833
2834 shape->SetStroke( stroke );
2835
2836 return shape.release();
2837}
2838
2839
2841{
2842 wxCHECK_MSG( CurTok() == T_image, nullptr,
2843 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an image." ) );
2844
2845 T token;
2846 std::unique_ptr<PCB_BITMAP> bitmap = std::make_unique<PCB_BITMAP>( aParent );
2847
2848 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2849 {
2850 if( token != T_LEFT )
2851 Expecting( T_LEFT );
2852
2853 token = NextTok();
2854
2855 switch( token )
2856 {
2857 case T_at:
2858 {
2859 VECTOR2I pos;
2860 pos.x = parseBoardUnits( "X coordinate" );
2861 pos.y = parseBoardUnits( "Y coordinate" );
2862 bitmap->SetPosition( pos );
2863 NeedRIGHT();
2864 break;
2865 }
2866
2867 case T_layer:
2868 bitmap->SetLayer( parseBoardItemLayer() );
2869 NeedRIGHT();
2870 break;
2871
2872 case T_scale:
2873 bitmap->GetImage()->SetScale( parseDouble( "image scale factor" ) );
2874
2875 if( !std::isnormal( bitmap->GetImage()->GetScale() ) )
2876 bitmap->GetImage()->SetScale( 1.0 );
2877
2878 NeedRIGHT();
2879 break;
2880
2881 case T_data:
2882 {
2883 token = NextTok();
2884
2885 wxString data;
2886
2887 // Reserve 128K because most image files are going to be larger than the default
2888 // 1K that wxString reserves.
2889 data.reserve( 1 << 17 );
2890
2891 while( token != T_RIGHT )
2892 {
2893 if( !IsSymbol( token ) )
2894 Expecting( "base64 image data" );
2895
2896 data += FromUTF8();
2897 token = NextTok();
2898 }
2899
2900 wxMemoryBuffer buffer = wxBase64Decode( data );
2901 wxMemoryOutputStream stream( buffer.GetData(), buffer.GetBufSize() );
2902 wxImage* image = new wxImage();
2903 wxMemoryInputStream istream( stream );
2904 image->LoadFile( istream, wxBITMAP_TYPE_PNG );
2905 bitmap->GetImage()->SetImage( image );
2906 bitmap->GetImage()->SetBitmap( new wxBitmap( *image ) );
2907 break;
2908 }
2909
2910 default:
2911 Expecting( "at, layer, scale, data" );
2912 }
2913 }
2914
2915 return bitmap.release();
2916}
2917
2918
2920{
2921 wxCHECK_MSG( CurTok() == T_gr_text, nullptr,
2922 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TEXT." ) );
2923
2924 std::unique_ptr<PCB_TEXT> text = std::make_unique<PCB_TEXT>( m_board );
2925
2926 T token = NextTok();
2927
2928 if( token == T_locked )
2929 {
2930 text->SetLocked( true );
2931 token = NextTok();
2932 }
2933
2934 if( !IsSymbol( token ) && (int) token != DSN_NUMBER )
2935 Expecting( "text value" );
2936
2937 text->SetText( FromUTF8() );
2938
2939 NeedLEFT();
2940 token = NextTok();
2941
2942 if( token != T_at )
2943 Expecting( T_at );
2944
2945 VECTOR2I pt;
2946
2947 pt.x = parseBoardUnits( "X coordinate" );
2948 pt.y = parseBoardUnits( "Y coordinate" );
2949 text->SetTextPos( pt );
2950
2951 // If there is no orientation defined, then it is the default value of 0 degrees.
2952 token = NextTok();
2953
2954 if( token == T_NUMBER )
2955 {
2956 text->SetTextAngle( EDA_ANGLE( parseDouble(), DEGREES_T ) );
2957 NeedRIGHT();
2958 }
2959 else if( token != T_RIGHT )
2960 {
2961 Unexpected( CurText() );
2962 }
2963
2964 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2965 {
2966 if( token != T_LEFT )
2967 Expecting( T_LEFT );
2968
2969 token = NextTok();
2970
2971 switch( token )
2972 {
2973 case T_layer:
2974 text->SetLayer( parseBoardItemLayer() );
2975
2976 token = NextTok();
2977
2978 if( token == T_knockout )
2979 {
2980 text->SetIsKnockout( true );
2981 token = NextTok();
2982 }
2983
2984 if( (int) token != DSN_RIGHT )
2985 Expecting( DSN_RIGHT );
2986
2987 break;
2988
2989 case T_tstamp:
2990 NextTok();
2991 const_cast<KIID&>( text->m_Uuid ) = CurStrToKIID();
2992 NeedRIGHT();
2993 break;
2994
2995 case T_effects:
2996 parseEDA_TEXT( static_cast<EDA_TEXT*>( text.get() ) );
2997 break;
2998
2999 case T_render_cache:
3000 parseRenderCache( static_cast<EDA_TEXT*>( text.get() ) );
3001 break;
3002
3003 default:
3004 Expecting( "layer, effects, locked, render_cache or tstamp" );
3005 }
3006 }
3007
3008 return text.release();
3009}
3010
3011
3013{
3014 wxCHECK_MSG( CurTok() == T_gr_text_box, nullptr,
3015 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TEXTBOX." ) );
3016
3017 std::unique_ptr<PCB_TEXTBOX> textbox = std::make_unique<PCB_TEXTBOX>( m_board );
3018
3020 T token = NextTok();
3021
3022 if( token == T_locked )
3023 {
3024 textbox->SetLocked( true );
3025 token = NextTok();
3026 }
3027
3028 if( !IsSymbol( token ) && (int) token != DSN_NUMBER )
3029 Expecting( "text value" );
3030
3031 textbox->SetText( FromUTF8() );
3032
3033 NeedLEFT();
3034 token = NextTok();
3035
3036 if( token == T_start )
3037 {
3038 int x = parseBoardUnits( "X coordinate" );
3039 int y = parseBoardUnits( "Y coordinate" );
3040 textbox->SetStart( VECTOR2I( x, y ) );
3041 NeedRIGHT();
3042
3043 NeedLEFT();
3044 token = NextTok();
3045
3046 if( token != T_end )
3047 Expecting( T_end );
3048
3049 x = parseBoardUnits( "X coordinate" );
3050 y = parseBoardUnits( "Y coordinate" );
3051 textbox->SetEnd( VECTOR2I( x, y ) );
3052 NeedRIGHT();
3053 }
3054 else if( token == T_pts )
3055 {
3056 textbox->SetShape( SHAPE_T::POLY );
3057 textbox->GetPolyShape().RemoveAllContours();
3058 textbox->GetPolyShape().NewOutline();
3059
3060 while( (token = NextTok() ) != T_RIGHT )
3061 parseOutlinePoints( textbox->GetPolyShape().Outline( 0 ) );
3062 }
3063 else
3064 {
3065 Expecting( "start or pts" );
3066 }
3067
3068 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3069 {
3070 if( token != T_LEFT )
3071 Expecting( T_LEFT );
3072
3073 token = NextTok();
3074
3075 switch( token )
3076 {
3077 case T_angle:
3078 textbox->SetTextAngle( EDA_ANGLE( parseDouble( "text box angle" ), DEGREES_T ) );
3079 NeedRIGHT();
3080 break;
3081
3082 case T_stroke:
3083 {
3084 STROKE_PARAMS_PARSER strokeParser( reader, pcbIUScale.IU_PER_MM );
3085 strokeParser.SyncLineReaderWith( *this );
3086
3087 strokeParser.ParseStroke( stroke );
3088 SyncLineReaderWith( strokeParser );
3089 break;
3090 }
3091
3092 case T_layer:
3093 textbox->SetLayer( parseBoardItemLayer() );
3094 NeedRIGHT();
3095 break;
3096
3097 case T_tstamp:
3098 NextTok();
3099 const_cast<KIID&>( textbox->m_Uuid ) = CurStrToKIID();
3100 NeedRIGHT();
3101 break;
3102
3103 case T_effects:
3104 parseEDA_TEXT( static_cast<EDA_TEXT*>( textbox.get() ) );
3105 break;
3106
3107 case T_render_cache:
3108 parseRenderCache( static_cast<EDA_TEXT*>( textbox.get() ) );
3109 break;
3110
3111 default:
3112 Expecting( "angle, width, layer, effects, render_cache or tstamp" );
3113 }
3114 }
3115
3116 textbox->SetStroke( stroke );
3117
3118 return textbox.release();
3119}
3120
3121
3123{
3124 wxCHECK_MSG( CurTok() == T_dimension, nullptr,
3125 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as DIMENSION." ) );
3126
3127 T token;
3128 bool locked = false;
3129 std::unique_ptr<PCB_DIMENSION_BASE> dim;
3130
3131 token = NextTok();
3132
3133 if( token == T_locked )
3134 {
3135 locked = true;
3136 token = NextTok();
3137 }
3138
3139 // skip value that used to be saved
3140 if( token != T_LEFT )
3141 NeedLEFT();
3142
3143 token = NextTok();
3144
3145 bool isLegacyDimension = false;
3146
3147 // Old format
3148 if( token == T_width )
3149 {
3150 isLegacyDimension = true;
3151 dim = std::make_unique<PCB_DIM_ALIGNED>( aParent, aInFP ? PCB_FP_DIM_ALIGNED_T
3153 dim->SetLineThickness( parseBoardUnits( "dim width value" ) );
3154 NeedRIGHT();
3155 }
3156 else
3157 {
3158 if( token != T_type )
3159 Expecting( T_type );
3160
3161 switch( NextTok() )
3162 {
3163 case T_aligned:
3164 dim = std::make_unique<PCB_DIM_ALIGNED>( aParent, aInFP ? PCB_FP_DIM_ALIGNED_T
3166 break;
3167
3168 case T_orthogonal:
3169 dim = std::make_unique<PCB_DIM_ORTHOGONAL>( aParent, aInFP );
3170 break;
3171
3172 case T_leader:
3173 dim = std::make_unique<PCB_DIM_LEADER>( aParent, aInFP );
3174 break;
3175
3176 case T_center:
3177 dim = std::make_unique<PCB_DIM_CENTER>( aParent, aInFP );
3178 break;
3179
3180 case T_radial:
3181 dim = std::make_unique<PCB_DIM_RADIAL>( aParent, aInFP );
3182 break;
3183
3184 default:
3185 wxFAIL_MSG( wxT( "Cannot parse unknown dim type %s" ) + GetTokenString( CurTok() ) );
3186 }
3187
3188 NeedRIGHT();
3189 }
3190
3191 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3192 {
3193 if( token != T_LEFT )
3194 Expecting( T_LEFT );
3195
3196 token = NextTok();
3197
3198 switch( token )
3199 {
3200 case T_layer:
3201 dim->SetLayer( parseBoardItemLayer() );
3202 NeedRIGHT();
3203 break;
3204
3205 case T_tstamp:
3206 NextTok();
3207 const_cast<KIID&>( dim->m_Uuid ) = CurStrToKIID();
3208 NeedRIGHT();
3209 break;
3210
3211 case T_gr_text:
3212 {
3214 dim->Text() = *text;
3215
3216 // The text is part of the dim and shares its uuid
3217 const_cast<KIID&>( dim->Text().m_Uuid ) = dim->m_Uuid;
3218
3219 // Fetch other dim properties out of the text item
3220 dim->Text().SetTextPos( text->GetTextPos() );
3221
3222 if( isLegacyDimension )
3223 {
3225 EDA_UNIT_UTILS::FetchUnitsFromString( text->GetText(), units );
3226 dim->SetUnits( units );
3227 }
3228
3229 delete text;
3230 break;
3231 }
3232
3233 // New format: feature points
3234 case T_pts:
3235 {
3236 VECTOR2I point;
3237
3238 parseXY( &point.x, &point.y );
3239 dim->SetStart( point );
3240 parseXY( &point.x, &point.y );
3241 dim->SetEnd( point );
3242
3243 NeedRIGHT();
3244 break;
3245 }
3246
3247 case T_height:
3248 {
3249 int height = parseBoardUnits( "dim height value" );
3250 NeedRIGHT();
3251
3252 if( dim->Type() == PCB_DIM_ORTHOGONAL_T || dim->Type() == PCB_FP_DIM_ORTHOGONAL_T
3253 || dim->Type() == PCB_DIM_ALIGNED_T || dim->Type() == PCB_FP_DIM_ALIGNED_T )
3254 {
3255 PCB_DIM_ALIGNED* aligned = static_cast<PCB_DIM_ALIGNED*>( dim.get() );
3256 aligned->SetHeight( height );
3257 }
3258
3259 break;
3260 }
3261
3262 case T_leader_length:
3263 {
3264 int length = parseBoardUnits( "dim leader length value" );
3265 NeedRIGHT();
3266
3267 if( dim->Type() == PCB_DIM_RADIAL_T || dim->Type() == PCB_FP_DIM_RADIAL_T )
3268 {
3269 PCB_DIM_RADIAL* radial = static_cast<PCB_DIM_RADIAL*>( dim.get() );
3270 radial->SetLeaderLength( length );
3271 }
3272
3273 break;
3274 }
3275
3276 case T_orientation:
3277 {
3278 int orientation = parseInt( "orthogonal dim orientation" );
3279 NeedRIGHT();
3280
3281 if( dim->Type() == PCB_DIM_ORTHOGONAL_T || dim->Type() == PCB_FP_DIM_ORTHOGONAL_T )
3282 {
3283 PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dim.get() );
3284 orientation = std::max( 0, std::min( 1, orientation ) );
3285 ortho->SetOrientation( static_cast<PCB_DIM_ORTHOGONAL::DIR>( orientation ) );
3286 }
3287
3288 break;
3289 }
3290
3291 case T_format:
3292 {
3293 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3294 {
3295 switch( token )
3296 {
3297 case T_LEFT:
3298 continue;
3299
3300 case T_prefix:
3301 NeedSYMBOLorNUMBER();
3302 dim->SetPrefix( FromUTF8() );
3303 NeedRIGHT();
3304 break;
3305
3306 case T_suffix:
3307 NeedSYMBOLorNUMBER();
3308 dim->SetSuffix( FromUTF8() );
3309 NeedRIGHT();
3310 break;
3311
3312 case T_units:
3313 {
3314 int mode = parseInt( "dim units mode" );
3315 mode = std::max( 0, std::min( 4, mode ) );
3316 dim->SetUnitsMode( static_cast<DIM_UNITS_MODE>( mode ) );
3317 NeedRIGHT();
3318 break;
3319 }
3320
3321 case T_units_format:
3322 {
3323 int format = parseInt( "dim units format" );
3324 format = std::max( 0, std::min( 3, format ) );
3325 dim->SetUnitsFormat( static_cast<DIM_UNITS_FORMAT>( format ) );
3326 NeedRIGHT();
3327 break;
3328 }
3329
3330 case T_precision:
3331 dim->SetPrecision( parseInt( "dim precision" ) );
3332 NeedRIGHT();
3333 break;
3334
3335 case T_override_value:
3336 NeedSYMBOLorNUMBER();
3337 dim->SetOverrideTextEnabled( true );
3338 dim->SetOverrideText( FromUTF8() );
3339 NeedRIGHT();
3340 break;
3341
3342 case T_suppress_zeroes:
3343 dim->SetSuppressZeroes( true );
3344 break;
3345
3346 default:
3347 Expecting( "prefix, suffix, units, units_format, precision, override_value, "
3348 "suppress_zeroes" );
3349 }
3350 }
3351 break;
3352 }
3353
3354 case T_style:
3355 {
3356 // new format: default to keep text aligned off unless token is present
3357 dim->SetKeepTextAligned( false );
3358
3359 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3360 {
3361 switch( token )
3362 {
3363 case T_LEFT:
3364 continue;
3365
3366 case T_thickness:
3367 dim->SetLineThickness( parseBoardUnits( "extension line thickness" ) );
3368 NeedRIGHT();
3369 break;
3370
3371 case T_arrow_length:
3372 dim->SetArrowLength( parseBoardUnits( "arrow length" ) );
3373 NeedRIGHT();
3374 break;
3375
3376 case T_text_position_mode:
3377 {
3378 int mode = parseInt( "dim text position mode" );
3379 mode = std::max( 0, std::min( 3, mode ) );
3380 dim->SetTextPositionMode( static_cast<DIM_TEXT_POSITION>( mode ) );
3381 NeedRIGHT();
3382 break;
3383 }
3384
3385 case T_extension_height:
3386 {
3387 PCB_DIM_ALIGNED* aligned = dynamic_cast<PCB_DIM_ALIGNED*>( dim.get() );
3388 wxCHECK_MSG( aligned, nullptr, wxT( "Invalid extension_height token" ) );
3389 aligned->SetExtensionHeight( parseBoardUnits( "extension height" ) );
3390 NeedRIGHT();
3391 break;
3392 }
3393
3394 case T_extension_offset:
3395 dim->SetExtensionOffset( parseBoardUnits( "extension offset" ) );
3396 NeedRIGHT();
3397 break;
3398
3399 case T_keep_text_aligned:
3400 dim->SetKeepTextAligned( true );
3401 break;
3402
3403 case T_text_frame:
3404 {
3405 KICAD_T expected_type = aInFP ? PCB_FP_DIM_LEADER_T : PCB_DIM_LEADER_T;
3406 wxCHECK_MSG( dim->Type() == expected_type, nullptr,
3407 wxT( "Invalid text_frame token" ) );
3408
3409 PCB_DIM_LEADER* leader = static_cast<PCB_DIM_LEADER*>( dim.get() );
3410
3411 int textFrame = parseInt( "dim text frame mode" );
3412 textFrame = std::max( 0, std::min( 3, textFrame ) );
3413 leader->SetTextBorder( static_cast<DIM_TEXT_BORDER>( textFrame ));
3414 NeedRIGHT();
3415 break;
3416 }
3417
3418 default:
3419 Expecting( "thickness, arrow_length, text_position_mode, extension_height, "
3420 "extension_offset" );
3421 }
3422 }
3423
3424 break;
3425 }
3426
3427 // Old format: feature1 stores a feature line. We only care about the origin.
3428 case T_feature1:
3429 {
3430 NeedLEFT();
3431 token = NextTok();
3432
3433 if( token != T_pts )
3434 Expecting( T_pts );
3435
3436 VECTOR2I point;
3437
3438 parseXY( &point.x, &point.y );
3439 dim->SetStart( point );
3440
3441 parseXY( nullptr, nullptr ); // Ignore second point
3442 NeedRIGHT();
3443 NeedRIGHT();
3444 break;
3445 }
3446
3447 // Old format: feature2 stores a feature line. We only care about the end point.
3448 case T_feature2:
3449 {
3450 NeedLEFT();
3451 token = NextTok();
3452
3453 if( token != T_pts )
3454 Expecting( T_pts );
3455
3456 VECTOR2I point;
3457
3458 parseXY( &point.x, &point.y );
3459 dim->SetEnd( point );
3460
3461 parseXY( nullptr, nullptr ); // Ignore second point
3462
3463 NeedRIGHT();
3464 NeedRIGHT();
3465 break;
3466 }
3467
3468 case T_crossbar:
3469 {
3470 NeedLEFT();
3471 token = NextTok();
3472
3473 if( token == T_pts )
3474 {
3475 // If we have a crossbar, we know we're an old aligned dim
3476 PCB_DIM_ALIGNED* aligned = static_cast<PCB_DIM_ALIGNED*>( dim.get() );
3477
3478 // Old style: calculate height from crossbar
3479 VECTOR2I point1, point2;
3480 parseXY( &point1.x, &point1.y );
3481 parseXY( &point2.x, &point2.y );
3482 aligned->UpdateHeight( point2, point1 ); // Yes, backwards intentionally
3483 NeedRIGHT();
3484 }
3485
3486 NeedRIGHT();
3487 break;
3488 }
3489
3490 // Arrow: no longer saved; no-op
3491 case T_arrow1a:
3492 NeedLEFT();
3493 token = NextTok();
3494
3495 if( token != T_pts )
3496 Expecting( T_pts );
3497
3498 parseXY( nullptr, nullptr );
3499 parseXY( nullptr, nullptr );
3500 NeedRIGHT();
3501 NeedRIGHT();
3502 break;
3503
3504 // Arrow: no longer saved; no-op
3505 case T_arrow1b:
3506 NeedLEFT();
3507 token = NextTok();
3508
3509 if( token != T_pts )
3510 Expecting( T_pts );
3511
3512 parseXY( nullptr, nullptr );
3513 parseXY( nullptr, nullptr );
3514 NeedRIGHT();
3515 NeedRIGHT();
3516 break;
3517
3518 // Arrow: no longer saved; no-op
3519 case T_arrow2a:
3520 NeedLEFT();
3521 token = NextTok();
3522
3523 if( token != T_pts )
3524 Expecting( T_pts );
3525
3526 parseXY( nullptr, nullptr );
3527 parseXY( nullptr, nullptr );
3528 NeedRIGHT();
3529 NeedRIGHT();
3530 break;
3531
3532 // Arrow: no longer saved; no-op
3533 case T_arrow2b:
3534 NeedLEFT();
3535 token = NextTok();
3536
3537 if( token != T_pts )
3538 Expecting( T_pts );
3539
3540 parseXY( nullptr, nullptr );
3541 parseXY( nullptr, nullptr );
3542 NeedRIGHT();
3543 NeedRIGHT();
3544 break;
3545
3546 default:
3547 Expecting( "layer, tstamp, gr_text, feature1, feature2, crossbar, arrow1a, "
3548 "arrow1b, arrow2a, or arrow2b" );
3549 }
3550 }
3551
3552 if( locked )
3553 dim->SetLocked( true );
3554
3555 dim->Update();
3556
3557 return dim.release();
3558}
3559
3560
3561FOOTPRINT* PCB_PARSER::parseFOOTPRINT( wxArrayString* aInitialComments )
3562{
3563 try
3564 {
3565 return parseFOOTPRINT_unchecked( aInitialComments );
3566 }
3567 catch( const PARSE_ERROR& parse_error )
3568 {
3569 if( m_tooRecent )
3570 throw FUTURE_FORMAT_ERROR( parse_error, GetRequiredVersion() );
3571 else
3572 throw;
3573 }
3574}
3575
3576
3577FOOTPRINT* PCB_PARSER::parseFOOTPRINT_unchecked( wxArrayString* aInitialComments )
3578{
3579 wxCHECK_MSG( CurTok() == T_module || CurTok() == T_footprint, nullptr,
3580 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as FOOTPRINT." ) );
3581
3582 wxString name;
3583 VECTOR2I pt;
3584 T token;
3585 LIB_ID fpid;
3586 int attributes = 0;
3587
3588 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
3589
3590 std::map<wxString, wxString> properties;
3591
3592 footprint->SetInitialComments( aInitialComments );
3593
3594 token = NextTok();
3595
3596 if( !IsSymbol( token ) && token != T_NUMBER )
3597 Expecting( "symbol|number" );
3598
3599 name = FromUTF8();
3600
3601 if( !name.IsEmpty() && fpid.Parse( name, true ) >= 0 )
3602 {
3603 THROW_IO_ERROR( wxString::Format( _( "Invalid footprint ID in\nfile: %s\nline: %d\n"
3604 "offset: %d." ),
3605 CurSource(), CurLineNumber(), CurOffset() ) );
3606 }
3607
3608 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3609 {
3610 if( token == T_LEFT )
3611 token = NextTok();
3612
3613 switch( token )
3614 {
3615 case T_version:
3616 {
3617 // Theoretically a footprint nested in a PCB could declare its own version, though
3618 // as of writing this comment we don't do that. Just in case, take the greater
3619 // version.
3620 int this_version = parseInt( FromUTF8().mb_str( wxConvUTF8 ) );
3621 NeedRIGHT();
3622 m_requiredVersion = std::max( m_requiredVersion, this_version );
3624 break;
3625 }
3626
3627 case T_generator:
3628 // We currently ignore the generator when parsing. It is included in the file for manual
3629 // indication of where the footprint came from.
3630 NeedSYMBOL();
3631 NeedRIGHT();
3632 break;
3633
3634 case T_locked:
3635 footprint->SetLocked( true );
3636 break;
3637
3638 case T_placed:
3639 footprint->SetIsPlaced( true );
3640 break;
3641
3642 case T_layer:
3643 {
3644 // Footprints can be only on the front side or the back side.
3645 // but because we can find some stupid layer in file, ensure a
3646 // acceptable layer is set for the footprint
3648 footprint->SetLayer( layer == B_Cu ? B_Cu : F_Cu );
3649 NeedRIGHT();
3650 break;
3651 }
3652
3653 case T_tedit:
3654 parseHex();
3655 NeedRIGHT();
3656 break;
3657
3658 case T_tstamp:
3659 NextTok();
3660 const_cast<KIID&>( footprint->m_Uuid ) = CurStrToKIID();
3661 NeedRIGHT();
3662 break;
3663
3664 case T_at:
3665 pt.x = parseBoardUnits( "X coordinate" );
3666 pt.y = parseBoardUnits( "Y coordinate" );
3667 footprint->SetPosition( pt );
3668 token = NextTok();
3669
3670 if( token == T_NUMBER )
3671 {
3672 footprint->SetOrientation( EDA_ANGLE( parseDouble(), DEGREES_T ) );
3673 NeedRIGHT();
3674 }
3675 else if( token != T_RIGHT )
3676 {
3677 Expecting( T_RIGHT );
3678 }
3679
3680 break;
3681
3682 case T_descr:
3683 NeedSYMBOLorNUMBER(); // some symbols can be 0508, so a number is also a symbol here
3684 footprint->SetDescription( FromUTF8() );
3685 NeedRIGHT();
3686 break;
3687
3688 case T_tags:
3689 NeedSYMBOLorNUMBER(); // some symbols can be 0508, so a number is also a symbol here
3690 footprint->SetKeywords( FromUTF8() );
3691 NeedRIGHT();
3692 break;
3693
3694 case T_property:
3695 properties.insert( parseProperty() );
3696 break;
3697
3698 case T_path:
3699 NeedSYMBOLorNUMBER(); // Paths can be numerical so a number is also a symbol here
3700 footprint->SetPath( KIID_PATH( FromUTF8() ) );
3701 NeedRIGHT();
3702 break;
3703
3704 case T_autoplace_cost90:
3705 case T_autoplace_cost180:
3706 parseInt( "legacy auto-place cost" );
3707 NeedRIGHT();
3708 break;
3709
3710 case T_private_layers:
3711 {
3712 LSET privateLayers;
3713
3714 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3715 {
3716 auto it = m_layerIndices.find( CurStr() );
3717
3718 if( it != m_layerIndices.end() )
3719 privateLayers.set( it->second );
3720 else
3721 Expecting( "layer name" );
3722 }
3723
3724 if( m_requiredVersion < 20220427 )
3725 {
3726 privateLayers.set( Edge_Cuts, false );
3727 privateLayers.set( Margin, false );
3728 }
3729
3730 footprint->SetPrivateLayers( privateLayers );
3731 break;
3732 }
3733
3734 case T_net_tie_pad_groups:
3735 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3736 footprint->AddNetTiePadGroup( CurStr() );
3737
3738 break;
3739
3740 case T_solder_mask_margin:
3741 footprint->SetLocalSolderMaskMargin( parseBoardUnits( "local solder mask margin "
3742 "value" ) );
3743 NeedRIGHT();
3744 break;
3745
3746 case T_solder_paste_margin:
3747 footprint->SetLocalSolderPasteMargin( parseBoardUnits( "local solder paste margin "
3748 "value" ) );
3749 NeedRIGHT();
3750 break;
3751
3752 case T_solder_paste_ratio:
3753 footprint->SetLocalSolderPasteMarginRatio( parseDouble( "local solder paste margin "
3754 "ratio value" ) );
3755 NeedRIGHT();
3756 break;
3757
3758 case T_clearance:
3759 footprint->SetLocalClearance( parseBoardUnits( "local clearance value" ) );
3760 NeedRIGHT();
3761 break;
3762
3763 case T_zone_connect:
3764 footprint->SetZoneConnection((ZONE_CONNECTION) parseInt( "zone connection value" ) );
3765 NeedRIGHT();
3766 break;
3767
3768 case T_thermal_width:
3769 case T_thermal_gap:
3770 // Interestingly, these have never been exposed in the GUI
3771 parseBoardUnits( token );
3772 NeedRIGHT();
3773 break;
3774
3775 case T_attr:
3776 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3777 {
3778 switch( token )
3779 {
3780 case T_virtual: // legacy token prior to version 20200826
3782 break;
3783
3784 case T_through_hole:
3785 attributes |= FP_THROUGH_HOLE;
3786 break;
3787
3788 case T_smd:
3789 attributes |= FP_SMD;
3790 break;
3791
3792 case T_board_only:
3793 attributes |= FP_BOARD_ONLY;
3794 break;
3795
3796 case T_exclude_from_pos_files:
3797 attributes |= FP_EXCLUDE_FROM_POS_FILES;
3798 break;
3799
3800 case T_exclude_from_bom:
3801 attributes |= FP_EXCLUDE_FROM_BOM;
3802 break;
3803
3804 case T_allow_missing_courtyard:
3805 attributes |= FP_ALLOW_MISSING_COURTYARD;
3806 break;
3807
3808 case T_allow_soldermask_bridges:
3809 attributes |= FP_ALLOW_SOLDERMASK_BRIDGES;
3810 break;
3811
3812 default:
3813 Expecting( "through_hole, smd, virtual, board_only, exclude_from_pos_files, "
3814 "exclude_from_bom or allow_solder_mask_bridges" );
3815 }
3816 }
3817
3818 break;
3819
3820 case T_fp_text:
3821 {
3823 text->SetParent( footprint.get() );
3824 EDA_ANGLE orientation = text->GetTextAngle();
3825 orientation -= footprint->GetOrientation();
3826 text->SetTextAngle( orientation );
3827 text->SetDrawCoord();
3828
3829 switch( text->GetType() )
3830 {
3832 footprint->Reference() = *text;
3833 const_cast<KIID&>( footprint->Reference().m_Uuid ) = text->m_Uuid;
3834 delete text;
3835 break;
3836
3838 footprint->Value() = *text;
3839 const_cast<KIID&>( footprint->Value().m_Uuid ) = text->m_Uuid;
3840 delete text;
3841 break;
3842
3843 default:
3844 footprint->Add( text, ADD_MODE::APPEND, true );
3845 }
3846
3847 break;
3848 }
3849
3850 case T_fp_text_box:
3851 {
3852 FP_TEXTBOX* textbox = parseFP_TEXTBOX();
3853 textbox->SetParent( footprint.get() );
3854 textbox->SetDrawCoord();
3855 footprint->Add( textbox, ADD_MODE::APPEND, true );
3856 break;
3857 }
3858
3859 case T_fp_arc:
3860 case T_fp_circle:
3861 case T_fp_curve:
3862 case T_fp_rect:
3863 case T_fp_line:
3864 case T_fp_poly:
3865 {
3866 FP_SHAPE* shape = parseFP_SHAPE();
3867 shape->SetParent( footprint.get() );
3868 shape->SetDrawCoord();
3869 footprint->Add( shape, ADD_MODE::APPEND, true );
3870 break;
3871 }
3872
3873 case T_image:
3874 {
3875 PCB_BITMAP* image = parsePCB_BITMAP( footprint.get() );
3876 footprint->Add( image, ADD_MODE::APPEND, true );
3877 break;
3878 }
3879
3880 case T_dimension:
3881 {
3882 PCB_DIMENSION_BASE* dimension = parseDIMENSION( footprint.get(), true );
3883 footprint->Add( dimension, ADD_MODE::APPEND, true );
3884 break;
3885 }
3886
3887 case T_pad:
3888 {
3889 if( PAD* pad = parsePAD( footprint.get() ) )
3890 {
3891 pt = pad->GetPos0();
3892 RotatePoint( pt, footprint->GetOrientation() );
3893 pad->SetPosition( pt + footprint->GetPosition() );
3894 footprint->Add( pad, ADD_MODE::APPEND, true );
3895 }
3896
3897 break;
3898 }
3899
3900 case T_model:
3901 {
3902 FP_3DMODEL* model = parse3DModel();
3903 footprint->Add3DModel( model );
3904 delete model;
3905 break;
3906 }
3907
3908 case T_zone:
3909 {
3910 ZONE* zone = parseZONE( footprint.get() );
3911 footprint->Add( zone, ADD_MODE::APPEND, true );
3912 break;
3913 }
3914
3915 case T_group:
3916 parseGROUP( footprint.get() );
3917 break;
3918
3919 default:
3920 Expecting( "locked, placed, tedit, tstamp, at, descr, tags, path, "
3921 "autoplace_cost90, autoplace_cost180, solder_mask_margin, "
3922 "solder_paste_margin, solder_paste_ratio, clearance, "
3923 "zone_connect, thermal_gap, attr, fp_text, "
3924 "fp_arc, fp_circle, fp_curve, fp_line, fp_poly, fp_rect, pad, "
3925 "zone, group, generator, version or model" );
3926 }
3927 }
3928
3929 // In legacy files the lack of attributes indicated a through-hole component which was by
3930 // default excluded from pos files. However there was a hack to look for SMD pads and
3931 // consider those "mislabeled through-hole components" and therefore include them in place
3932 // files. We probably don't want to get into that game so we'll just include them by
3933 // default and let the user change it if required.
3934 if( m_requiredVersion < 20200826 && attributes == 0 )
3935 attributes |= FP_THROUGH_HOLE;
3936
3938 {
3939 if( footprint->GetKeywords().StartsWith( wxT( "net tie" ) ) )
3940 {
3941 wxString padGroup;
3942
3943 for( PAD* pad : footprint->Pads() )
3944 {
3945 if( !padGroup.IsEmpty() )
3946 padGroup += wxS( ", " );
3947
3948 padGroup += pad->GetNumber();
3949 }
3950
3951 if( !padGroup.IsEmpty() )
3952 footprint->AddNetTiePadGroup( padGroup );
3953 }
3954 }
3955
3956 footprint->SetAttributes( attributes );
3957
3958 footprint->SetFPID( fpid );
3959 footprint->SetProperties( properties );
3960
3961 return footprint.release();
3962}
3963
3964
3966{
3967 wxCHECK_MSG( CurTok() == T_fp_text, nullptr,
3968 wxString::Format( wxT( "Cannot parse %s as FP_TEXT at line %d, offset %d." ),
3969 GetTokenString( CurTok() ), CurLineNumber(), CurOffset() ) );
3970
3971 T token = NextTok();
3972
3973 std::unique_ptr<FP_TEXT> text = std::make_unique<FP_TEXT>( nullptr );
3974
3975 switch( token )
3976 {
3977 case T_reference:
3978 text->SetType( FP_TEXT::TEXT_is_REFERENCE );
3979 break;
3980
3981 case T_value:
3982 text->SetType( FP_TEXT::TEXT_is_VALUE );
3983 break;
3984
3985 case T_user:
3986 break; // Default type is user text.
3987
3988 default:
3989 THROW_IO_ERROR( wxString::Format( _( "Cannot handle footprint text type %s" ),
3990 FromUTF8() ) );
3991 }
3992
3993 token = NextTok();
3994
3995 if( token == T_locked )
3996 {
3997 text->SetLocked( true );
3998 token = NextTok();
3999 }
4000
4001 if( !IsSymbol( token ) && (int) token != DSN_NUMBER )
4002 Expecting( "text value" );
4003
4004 wxString value = FromUTF8();
4005 value.Replace( wxT( "%V" ), wxT( "${VALUE}" ) );
4006 value.Replace( wxT( "%R" ), wxT( "${REFERENCE}" ) );
4007 text->SetText( value );
4008 NeedLEFT();
4009 token = NextTok();
4010
4011 if( token != T_at )
4012 Expecting( T_at );
4013
4014 VECTOR2I pt;
4015
4016 pt.x = parseBoardUnits( "X coordinate" );
4017 pt.y = parseBoardUnits( "Y coordinate" );
4018 text->SetPos0( pt );
4019
4020 NextTok();
4021
4022 if( CurTok() == T_NUMBER )
4023 {
4024 text->SetTextAngle( EDA_ANGLE( parseDouble(), DEGREES_T ) );
4025 NextTok();
4026 }
4027
4028 if( CurTok() == T_unlocked )
4029 {
4030 text->SetKeepUpright( false );
4031 NextTok();
4032 }
4033
4034 if( CurTok() != T_RIGHT )
4035 {
4036 Unexpected( CurText() );
4037 }
4038
4039 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4040 {
4041 if( token == T_LEFT )
4042 token = NextTok();
4043
4044 switch( token )
4045 {
4046 case T_layer:
4047 text->SetLayer( parseBoardItemLayer() );
4048
4049 token = NextTok();
4050
4051 if( token == T_knockout )
4052 {
4053 text->SetIsKnockout( true );
4054 token = NextTok();
4055 }
4056
4057 if( (int) token != DSN_RIGHT )
4058 Expecting( DSN_RIGHT );
4059
4060 break;
4061
4062 case T_hide:
4063 text->SetVisible( false );
4064 break;
4065
4066 case T_effects:
4067 parseEDA_TEXT( static_cast<EDA_TEXT*>( text.get() ) );
4068 break;
4069
4070 case T_render_cache:
4071 parseRenderCache( static_cast<EDA_TEXT*>( text.get() ) );
4072 break;
4073
4074 case T_tstamp:
4075 NextTok();
4076 const_cast<KIID&>( text->m_Uuid ) = CurStrToKIID();
4077 NeedRIGHT();
4078 break;
4079
4080 default:
4081 Expecting( "layer, hide, effects, render_cache or tstamp" );
4082 }
4083 }
4084
4085 return text.release();
4086}
4087
4088
4090{
4091 wxCHECK_MSG( CurTok() == T_fp_text_box, nullptr,
4092 wxString::Format( wxT( "Cannot parse %s as FP_TEXTBOX at line %d, offset %d." ),
4093 GetTokenString( CurTok() ), CurLineNumber(), CurOffset() ) );
4094
4095 std::unique_ptr<FP_TEXTBOX> textbox = std::make_unique<FP_TEXTBOX>( nullptr );
4096
4098 T token = NextTok();
4099
4100 if( token == T_locked )
4101 {
4102 textbox->SetLocked( true );
4103 token = NextTok();
4104 }
4105
4106 if( !IsSymbol( token ) && (int) token != DSN_NUMBER )
4107 Expecting( "text value" );
4108
4109 textbox->SetText( FromUTF8() );
4110
4111 NeedLEFT();
4112 token = NextTok();
4113
4114 if( token == T_start )
4115 {
4116 int x = parseBoardUnits( "X coordinate" );
4117 int y = parseBoardUnits( "Y coordinate" );
4118 textbox->SetStart0( VECTOR2I( x, y ) );
4119 NeedRIGHT();
4120
4121 NeedLEFT();
4122 token = NextTok();
4123
4124 if( token != T_end )
4125 Expecting( T_end );
4126
4127 x = parseBoardUnits( "X coordinate" );
4128 y = parseBoardUnits( "Y coordinate" );
4129 textbox->SetEnd0( VECTOR2I( x, y ) );
4130 NeedRIGHT();
4131 }
4132 else if( token == T_pts )
4133 {
4134 textbox->SetShape( SHAPE_T::POLY );
4135 textbox->GetPolyShape().RemoveAllContours();
4136 textbox->GetPolyShape().NewOutline();
4137
4138 while( (token = NextTok() ) != T_RIGHT )
4139 parseOutlinePoints( textbox->GetPolyShape().Outline( 0 ) );
4140 }
4141 else
4142 {
4143 Expecting( "start or pts" );
4144 }
4145
4146 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4147 {
4148 if( token == T_LEFT )
4149 token = NextTok();
4150
4151 switch( token )
4152 {
4153 case T_angle:
4154 textbox->SetTextAngle( EDA_ANGLE( parseDouble( "text box angle" ), DEGREES_T ) );
4155 NeedRIGHT();
4156 break;
4157
4158 case T_stroke:
4159 {
4160 STROKE_PARAMS_PARSER strokeParser( reader, pcbIUScale.IU_PER_MM );
4161 strokeParser.SyncLineReaderWith( *this );
4162
4163 strokeParser.ParseStroke( stroke );
4164 SyncLineReaderWith( strokeParser );
4165 break;
4166 }
4167
4168 case T_layer:
4169 textbox->SetLayer( parseBoardItemLayer() );
4170 NeedRIGHT();
4171 break;
4172
4173 case T_effects:
4174 parseEDA_TEXT( static_cast<EDA_TEXT*>( textbox.get() ) );
4175 break;
4176
4177 case T_render_cache:
4178 parseRenderCache( static_cast<EDA_TEXT*>( textbox.get() ) );
4179 break;
4180
4181 case T_tstamp:
4182 NextTok();
4183 const_cast<KIID&>( textbox->m_Uuid ) = CurStrToKIID();
4184 NeedRIGHT();
4185 break;
4186
4187 default:
4188 Expecting( "angle, width, layer, effects, render_cache or tstamp" );
4189 }
4190 }
4191
4192 textbox->SetStroke( stroke );
4193
4194 return textbox.release();
4195}
4196
4197
4199{
4200 wxCHECK_MSG( CurTok() == T_fp_arc || CurTok() == T_fp_circle || CurTok() == T_fp_curve ||
4201 CurTok() == T_fp_rect || CurTok() == T_fp_line || CurTok() == T_fp_poly, nullptr,
4202 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as FP_SHAPE." ) );
4203
4204 VECTOR2I pt;
4206 T token;
4207
4208 std::unique_ptr<FP_SHAPE> shape = std::make_unique<FP_SHAPE>( nullptr );
4209
4210 switch( CurTok() )
4211 {
4212 case T_fp_arc:
4213 shape->SetShape( SHAPE_T::ARC );
4214 token = NextTok();
4215
4216 if( token == T_locked )
4217 {
4218 shape->SetLocked( true );
4219 token = NextTok();
4220 }
4221
4222 if( token != T_LEFT )
4223 Expecting( T_LEFT );
4224
4225 token = NextTok();
4226
4228 {
4229 // In legacy files the start keyword actually gives the arc center...
4230 if( token != T_start )
4231 Expecting( T_start );
4232
4233 pt.x = parseBoardUnits( "X coordinate" );
4234 pt.y = parseBoardUnits( "Y coordinate" );
4235 shape->SetCenter0( pt );
4236 NeedRIGHT();
4237 NeedLEFT();
4238 token = NextTok();
4239
4240 // ... and the end keyword gives the start point of the arc
4241 if( token != T_end )
4242 Expecting( T_end );
4243
4244 pt.x = parseBoardUnits( "X coordinate" );
4245 pt.y = parseBoardUnits( "Y coordinate" );
4246 shape->SetStart0( pt );
4247 NeedRIGHT();
4248 NeedLEFT();
4249 token = NextTok();
4250
4251 if( token != T_angle )
4252 Expecting( T_angle );
4253
4254 shape->SetArcAngleAndEnd0( EDA_ANGLE( parseDouble( "arc angle" ), DEGREES_T ), true );
4255 NeedRIGHT();
4256 }
4257 else
4258 {
4259 VECTOR2I arc_start, arc_mid, arc_end;
4260
4261 if( token != T_start )
4262 Expecting( T_start );
4263
4264 arc_start.x = parseBoardUnits( "X coordinate" );
4265 arc_start.y = parseBoardUnits( "Y coordinate" );
4266 NeedRIGHT();
4267 NeedLEFT();
4268 token = NextTok();
4269
4270 if( token != T_mid )
4271 Expecting( T_mid );
4272
4273 arc_mid.x = parseBoardUnits( "X coordinate" );
4274 arc_mid.y = parseBoardUnits( "Y coordinate" );
4275 NeedRIGHT();
4276 NeedLEFT();
4277 token = NextTok();
4278
4279 if( token != T_end )
4280 Expecting( T_end );
4281
4282 arc_end.x = parseBoardUnits( "X coordinate" );
4283 arc_end.y = parseBoardUnits( "Y coordinate" );
4284 NeedRIGHT();
4285
4286 shape->SetArcGeometry0( arc_start, arc_mid, arc_end );
4287 }
4288
4289 break;
4290
4291 case T_fp_circle:
4292 shape->SetShape( SHAPE_T::CIRCLE );
4293 token = NextTok();
4294
4295 if( token == T_locked )
4296 {
4297 shape->SetLocked( true );
4298 token = NextTok();
4299 }
4300
4301 if( token != T_LEFT )
4302 Expecting( T_LEFT );
4303
4304 token = NextTok();
4305
4306 if( token != T_center )
4307 Expecting( T_center );
4308
4309 pt.x = parseBoardUnits( "X coordinate" );
4310 pt.y = parseBoardUnits( "Y coordinate" );
4311 shape->SetStart0( pt );
4312 NeedRIGHT();
4313 NeedLEFT();
4314 token = NextTok();
4315
4316 if( token != T_end )
4317 Expecting( T_end );
4318
4319 pt.x = parseBoardUnits( "X coordinate" );
4320 pt.y = parseBoardUnits( "Y coordinate" );
4321 shape->SetEnd0( pt );
4322 NeedRIGHT();
4323 break;
4324
4325 case T_fp_curve:
4326 shape->SetShape( SHAPE_T::BEZIER );
4327 token = NextTok();
4328
4329 if( token == T_locked )
4330 {
4331 shape->SetLocked( true );
4332 token = NextTok();
4333 }
4334
4335 if( token != T_LEFT )
4336 Expecting( T_LEFT );
4337
4338 token = NextTok();
4339
4340 if( token != T_pts )
4341 Expecting( T_pts );
4342
4343 shape->SetStart0( parseXY() );
4344 shape->SetBezierC1_0( parseXY() );
4345 shape->SetBezierC2_0( parseXY() );
4346 shape->SetEnd0( parseXY() );
4347 NeedRIGHT();
4348 break;
4349
4350 case T_fp_rect:
4351 shape->SetShape( SHAPE_T::RECT );
4352 token = NextTok();
4353
4354 if( token == T_locked )
4355 {
4356 shape->SetLocked( true );
4357 token = NextTok();
4358 }
4359
4360 if( token != T_LEFT )
4361 Expecting( T_LEFT );
4362
4363 token = NextTok();
4364
4365 if( token != T_start )
4366 Expecting( T_start );
4367
4368 pt.x = parseBoardUnits( "X coordinate" );
4369 pt.y = parseBoardUnits( "Y coordinate" );
4370 shape->SetStart0( pt );
4371
4372 NeedRIGHT();
4373 NeedLEFT();
4374 token = NextTok();
4375
4376 if( token != T_end )
4377 Expecting( T_end );
4378
4379 pt.x = parseBoardUnits( "X coordinate" );
4380 pt.y = parseBoardUnits( "Y coordinate" );
4381 shape->SetEnd0( pt );
4382 NeedRIGHT();
4383 break;
4384
4385 case T_fp_line:
4386 // Default PCB_SHAPE type is S_SEGMENT.
4387 token = NextTok();
4388
4389 if( token == T_locked )
4390 {
4391 shape->SetLocked( true );
4392 token = NextTok();
4393 }
4394
4395 if( token != T_LEFT )
4396 Expecting( T_LEFT );
4397
4398 token = NextTok();
4399
4400 if( token != T_start )
4401 Expecting( T_start );
4402
4403 pt.x = parseBoardUnits( "X coordinate" );
4404 pt.y = parseBoardUnits( "Y coordinate" );
4405 shape->SetStart0( pt );
4406
4407 NeedRIGHT();
4408 NeedLEFT();
4409 token = NextTok();
4410
4411 if( token != T_end )
4412 Expecting( T_end );
4413
4414 pt.x = parseBoardUnits( "X coordinate" );
4415 pt.y = parseBoardUnits( "Y coordinate" );
4416 shape->SetEnd0( pt );
4417 NeedRIGHT();
4418 break;
4419
4420 case T_fp_poly:
4421 {
4422 shape->SetShape( SHAPE_T::POLY );
4423 shape->SetPolyPoints( {} );
4424 SHAPE_LINE_CHAIN& outline = shape->GetPolyShape().Outline( 0 );
4425
4426 token = NextTok();
4427
4428 if( token == T_locked )
4429 {
4430 shape->SetLocked( true );
4431 token = NextTok();
4432 }
4433
4434 if( token != T_LEFT )
4435 Expecting( T_LEFT );
4436
4437 token = NextTok();
4438
4439 if( token != T_pts )
4440 Expecting( T_pts );
4441
4442 while( (token = NextTok() ) != T_RIGHT )
4443 parseOutlinePoints( outline );
4444
4445 break;
4446 }
4447
4448 default:
4449 Expecting( "fp_arc, fp_circle, fp_curve, fp_line, fp_poly, or fp_rect" );
4450 }
4451
4452 bool foundFill = false;
4453
4454 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4455 {
4456 if( token != T_LEFT )
4457 Expecting( T_LEFT );
4458
4459 token = NextTok();
4460
4461 switch( token )
4462 {
4463 case T_layer:
4464 shape->SetLayer( parseBoardItemLayer() );
4465 NeedRIGHT();
4466 break;
4467
4468 case T_width: // legacy token
4469 stroke.SetWidth( parseBoardUnits( T_width ) );
4470 NeedRIGHT();
4471 break;
4472
4473 case T_stroke:
4474 {
4475 STROKE_PARAMS_PARSER strokeParser( reader, pcbIUScale.IU_PER_MM );
4476 strokeParser.SyncLineReaderWith( *this );
4477
4478 strokeParser.ParseStroke( stroke );
4479 SyncLineReaderWith( strokeParser );
4480 break;
4481 }
4482
4483 case T_tstamp:
4484 NextTok();
4485 const_cast<KIID&>( shape->m_Uuid ) = CurStrToKIID();
4486 NeedRIGHT();
4487 break;
4488
4489 case T_fill:
4490 foundFill = true;
4491
4492 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4493 {
4494 if( token == T_LEFT )
4495 token = NextTok();
4496
4497 switch( token )
4498 {
4499 // T_yes was used to indicate filling when first introduced,
4500 // so treat it like a solid fill since that was the only fill available
4501 case T_yes:
4502 case T_solid:
4503 shape->SetFilled( true );
4504 break;
4505
4506 case T_none:
4507 shape->SetFilled( false );
4508 break;
4509
4510 default:
4511 Expecting( "yes, none, solid" );
4512 }
4513 }
4514
4515 break;
4516
4517 // We continue to parse the status field but it is no longer written
4518 case T_status:
4519 shape->SetStatus( static_cast<EDA_ITEM_FLAGS>( parseHex() ) );
4520 NeedRIGHT();
4521 break;
4522
4523 // Continue to process "(locked)" format which was output during 5.99 development
4524 case T_locked:
4525 shape->SetLocked( true );
4526 NeedRIGHT();
4527 break;
4528
4529 default:
4530 Expecting( "layer, width, fill, tstamp, locked, or status" );
4531 }
4532 }
4533
4534 if( !foundFill )
4535 {
4536 // Legacy versions didn't have a filled flag but allowed some shapes to indicate they
4537 // should be filled by specifying a 0 stroke-width.
4538 if( stroke.GetWidth() == 0
4539 && ( shape->GetShape() == SHAPE_T::RECT || shape->GetShape() == SHAPE_T::CIRCLE ) )
4540 {
4541 shape->SetFilled( true );
4542 }
4543 // Polygons on non-Edge_Cuts layers were always filled
4544 else if( shape->GetShape() == SHAPE_T::POLY && shape->GetLayer() != Edge_Cuts )
4545 {
4546 shape->SetFilled( true );
4547 }
4548 }
4549
4550 // Only filled shapes may have a zero line-width. This is not permitted in KiCad but some
4551 // external tools can generate invalid files.
4552 if( stroke.GetWidth() <= 0 && !shape->IsFilled() )
4553 {
4555 }
4556
4557 shape->SetStroke( stroke );
4558
4559 return shape.release();
4560}
4561
4562
4564{
4565 wxCHECK_MSG( CurTok() == T_pad, nullptr,
4566 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PAD." ) );
4567
4568 VECTOR2I sz;
4569 VECTOR2I pt;
4570
4571 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aParent );
4572
4573 // File only contains a token if KeepTopBottom is true
4574 pad->SetKeepTopBottom( false );
4575
4576 NeedSYMBOLorNUMBER();
4577 pad->SetNumber( FromUTF8() );
4578
4579 T token = NextTok();
4580
4581 switch( token )
4582 {
4583 case T_thru_hole:
4584 pad->SetAttribute( PAD_ATTRIB::PTH );
4585 break;
4586
4587 case T_smd:
4588 pad->SetAttribute( PAD_ATTRIB::SMD );
4589
4590 // Default PAD object is thru hole with drill.
4591 // SMD pads have no hole
4592 pad->SetDrillSize( VECTOR2I( 0, 0 ) );
4593 break;
4594
4595 case T_connect:
4596 pad->SetAttribute( PAD_ATTRIB::CONN );
4597
4598 // Default PAD object is thru hole with drill.
4599 // CONN pads have no hole
4600 pad->SetDrillSize( VECTOR2I( 0, 0 ) );
4601 break;
4602
4603 case T_np_thru_hole:
4604 pad->SetAttribute( PAD_ATTRIB::NPTH );
4605 break;
4606
4607 default:
4608 Expecting( "thru_hole, smd, connect, or np_thru_hole" );
4609 }
4610
4611 token = NextTok();
4612
4613 switch( token )
4614 {
4615 case T_circle:
4616 pad->SetShape( PAD_SHAPE::CIRCLE );
4617 break;
4618
4619 case T_rect:
4620 pad->SetShape( PAD_SHAPE::RECT );
4621 break;
4622
4623 case T_oval:
4624 pad->SetShape( PAD_SHAPE::OVAL );
4625 break;
4626
4627 case T_trapezoid:
4628 pad->SetShape( PAD_SHAPE::TRAPEZOID );
4629 break;
4630
4631 case T_roundrect:
4632 // Note: the shape can be PAD_SHAPE::ROUNDRECT or PAD_SHAPE::CHAMFERED_RECT
4633 // (if chamfer parameters are found later in pad descr.)
4634 pad->SetShape( PAD_SHAPE::ROUNDRECT );
4635 break;
4636
4637 case T_custom:
4638 pad->SetShape( PAD_SHAPE::CUSTOM );
4639 break;
4640
4641 default:
4642 Expecting( "circle, rectangle, roundrect, oval, trapezoid or custom" );
4643 }
4644
4645 if( pad->GetShape() == PAD_SHAPE::CIRCLE )
4646 pad->SetThermalSpokeAngle( ANGLE_45 );
4647 else if( pad->GetShape() == PAD_SHAPE::CUSTOM && pad->GetAnchorPadShape() == PAD_SHAPE::CIRCLE )
4648 pad->SetThermalSpokeAngle( ANGLE_45 );
4649 else
4650 pad->SetThermalSpokeAngle( ANGLE_90 );
4651
4652 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4653 {
4654 if( token == T_locked )
4655 {
4656 // Pad locking is now a session preference
4657 token = NextTok();
4658 }
4659
4660 if( token != T_LEFT )
4661 Expecting( T_LEFT );
4662
4663 token = NextTok();
4664
4665 switch( token )
4666 {
4667 case T_size:
4668 sz.x = parseBoardUnits( "width value" );
4669 sz.y = parseBoardUnits( "height value" );
4670 pad->SetSize( sz );
4671 NeedRIGHT();
4672 break;
4673
4674 case T_at:
4675 pt.x = parseBoardUnits( "X coordinate" );
4676 pt.y = parseBoardUnits( "Y coordinate" );
4677 pad->SetPos0( pt );
4678 token = NextTok();
4679
4680 if( token == T_NUMBER )
4681 {
4682 pad->SetOrientation( EDA_ANGLE( parseDouble(), DEGREES_T ) );
4683 NeedRIGHT();
4684 }
4685 else if( token != T_RIGHT )
4686 {
4687 Expecting( ") or angle value" );
4688 }
4689
4690 break;
4691
4692 case T_rect_delta:
4693 {
4694 wxSize delta;
4695 delta.SetWidth( parseBoardUnits( "rectangle delta width" ) );
4696 delta.SetHeight( parseBoardUnits( "rectangle delta height" ) );
4697 pad->SetDelta( delta );
4698 NeedRIGHT();
4699 break;
4700 }
4701
4702 case T_drill:
4703 {
4704 bool haveWidth = false;
4705 VECTOR2I drillSize = pad->GetDrillSize();
4706
4707 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4708 {
4709 if( token == T_LEFT )
4710 token = NextTok();
4711
4712 switch( token )
4713 {
4714 case T_oval: pad->SetDrillShape( PAD_DRILL_SHAPE_OBLONG ); break;
4715
4716 case T_NUMBER:
4717 {
4718 if( !haveWidth )
4719 {
4720 drillSize.x = parseBoardUnits();
4721
4722 // If height is not defined the width and height are the same.
4723 drillSize.y = drillSize.x;
4724 haveWidth = true;
4725 }
4726 else
4727 {
4728 drillSize.y = parseBoardUnits();
4729 }
4730 }
4731
4732 break;
4733
4734 case T_offset:
4735 pt.x = parseBoardUnits( "drill offset x" );
4736 pt.y = parseBoardUnits( "drill offset y" );
4737 pad->SetOffset( pt );
4738 NeedRIGHT();
4739 break;
4740
4741 default:
4742 Expecting( "oval, size, or offset" );
4743 }
4744 }
4745
4746 // This fixes a bug caused by setting the default PAD drill size to a value other
4747 // than 0 used to fix a bunch of debug assertions even though it is defined as a
4748 // through hole pad. Wouldn't a though hole pad with no drill be a surface mount
4749 // pad (or a conn pad which is a smd pad with no solder paste)?
4750 if( ( pad->GetAttribute() != PAD_ATTRIB::SMD )
4751 && ( pad->GetAttribute() != PAD_ATTRIB::CONN ) )
4752 pad->SetDrillSize( drillSize );
4753 else
4754 pad->SetDrillSize( wxSize( 0, 0 ) );
4755
4756 break;
4757 }
4758
4759 case T_layers:
4760 {
4761 LSET layerMask = parseBoardItemLayersAsMask();
4762 pad->SetLayerSet( layerMask );
4763 break;
4764 }
4765
4766 case T_net:
4767 if( ! pad->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
4768 {
4769 wxLogError( _( "Invalid net ID in\nfile: %s\nline: %d offset: %d" ),
4770 CurSource(), CurLineNumber(), CurOffset() );
4771 }
4772
4773 NeedSYMBOLorNUMBER();
4774
4775 // Test validity of the netname in file for netcodes expected having a net name
4776 if( m_board && pad->GetNetCode() > 0 )
4777 {
4778 wxString netName( FromUTF8() );
4779
4780 // Convert overbar syntax from `~...~` to `~{...}`. These were left out of the
4781 // first merge so the version is a bit later.
4782 if( m_requiredVersion < 20210606 )
4783 netName = ConvertToNewOverbarNotation( netName );
4784
4785 if( netName != m_board->FindNet( pad->GetNetCode() )->GetNetname() )
4786 {
4787 pad->SetNetCode( NETINFO_LIST::ORPHANED, /* aNoAssert */ true );
4788 wxLogError( _( "Net name doesn't match ID in\nfile: %s\nline: %d offset: %d" ),
4789 CurSource(), CurLineNumber(), CurOffset() );
4790 }
4791 }
4792
4793 NeedRIGHT();
4794 break;
4795
4796 case T_pinfunction:
4797 NeedSYMBOLorNUMBER();
4798 pad->SetPinFunction( FromUTF8() );
4799 NeedRIGHT();
4800 break;
4801
4802 case T_pintype:
4803 NeedSYMBOLorNUMBER();
4804 pad->SetPinType( FromUTF8() );
4805 NeedRIGHT();
4806 break;
4807
4808 case T_die_length:
4809 pad->SetPadToDieLength( parseBoardUnits( T_die_length ) );
4810 NeedRIGHT();
4811 break;
4812
4813 case T_solder_mask_margin:
4814 pad->SetLocalSolderMaskMargin( parseBoardUnits( T_solder_mask_margin ) );
4815 NeedRIGHT();
4816 break;
4817
4818 case T_solder_paste_margin:
4819 pad->SetLocalSolderPasteMargin( parseBoardUnits( T_solder_paste_margin ) );
4820 NeedRIGHT();
4821 break;
4822
4823 case T_solder_paste_margin_ratio:
4824 pad->SetLocalSolderPasteMarginRatio(
4825 parseDouble( "pad local solder paste margin ratio value" ) );
4826 NeedRIGHT();
4827 break;
4828
4829 case T_clearance:
4830 pad->SetLocalClearance( parseBoardUnits( "local clearance value" ) );
4831 NeedRIGHT();
4832 break;
4833
4834 case T_zone_connect:
4835 pad->SetZoneConnection( (ZONE_CONNECTION) parseInt( "zone connection value" ) );
4836 NeedRIGHT();
4837 break;
4838
4839 case T_thermal_width: // legacy token
4840 case T_thermal_bridge_width:
4841 pad->SetThermalSpokeWidth( parseBoardUnits( token ) );
4842 NeedRIGHT();
4843 break;
4844
4845 case T_thermal_bridge_angle:
4846 pad->SetThermalSpokeAngle( EDA_ANGLE( parseDouble( "thermal spoke angle" ), DEGREES_T ) );
4847 NeedRIGHT();
4848 break;
4849
4850
4851 case T_thermal_gap:
4852 pad->SetThermalGap( parseBoardUnits( "thermal relief gap value" ) );
4853 NeedRIGHT();
4854 break;
4855
4856 case T_roundrect_rratio:
4857 pad->SetRoundRectRadiusRatio( parseDouble( "roundrect radius ratio" ) );
4858 NeedRIGHT();
4859 break;
4860
4861 case T_chamfer_ratio:
4862 pad->SetChamferRectRatio( parseDouble( "chamfer ratio" ) );
4863
4864 if( pad->GetChamferRectRatio() > 0 )
4865 pad->SetShape( PAD_SHAPE::CHAMFERED_RECT );
4866
4867 NeedRIGHT();
4868 break;
4869
4870 case T_chamfer:
4871 {
4872 int chamfers = 0;
4873 bool end_list = false;
4874
4875 while( !end_list )
4876 {
4877 token = NextTok();
4878
4879 switch( token )
4880 {
4881 case T_top_left:
4882 chamfers |= RECT_CHAMFER_TOP_LEFT;
4883 break;
4884
4885 case T_top_right:
4886 chamfers |= RECT_CHAMFER_TOP_RIGHT;
4887 break;
4888
4889 case T_bottom_left:
4890 chamfers |= RECT_CHAMFER_BOTTOM_LEFT;
4891 break;
4892
4893 case T_bottom_right:
4894 chamfers |= RECT_CHAMFER_BOTTOM_RIGHT;
4895 break;
4896
4897 case T_RIGHT:
4898 pad->SetChamferPositions( chamfers );
4899 end_list = true;
4900 break;
4901
4902 default:
4903 Expecting( "chamfer_top_left chamfer_top_right chamfer_bottom_left or "
4904 "chamfer_bottom_right" );
4905 }
4906 }
4907
4908 if( pad->GetChamferPositions() != RECT_NO_CHAMFER )
4909 pad->SetShape( PAD_SHAPE::CHAMFERED_RECT );
4910
4911 break;
4912 }
4913
4914 case T_property:
4915 while( token != T_RIGHT )
4916 {
4917 token = NextTok();
4918
4919 switch( token )
4920 {
4921 case T_pad_prop_bga: pad->SetProperty( PAD_PROP::BGA ); break;
4922 case T_pad_prop_fiducial_glob: pad->SetProperty( PAD_PROP::FIDUCIAL_GLBL ); break;
4923 case T_pad_prop_fiducial_loc: pad->SetProperty( PAD_PROP::FIDUCIAL_LOCAL ); break;
4924 case T_pad_prop_testpoint: pad->SetProperty( PAD_PROP::TESTPOINT ); break;
4925 case T_pad_prop_castellated: pad->SetProperty( PAD_PROP::CASTELLATED ); break;
4926 case T_pad_prop_heatsink: pad->SetProperty( PAD_PROP::HEATSINK ); break;
4927 case T_none: pad->SetProperty( PAD_PROP::NONE ); break;
4928 case T_RIGHT: break;
4929
4930 default:
4931#if 0 // Currently: skip unknown property
4932 Expecting( "pad_prop_bga pad_prop_fiducial_glob pad_prop_fiducial_loc"
4933 " pad_prop_heatsink or pad_prop_castellated" );
4934#endif
4935 break;
4936 }
4937 }
4938
4939 break;
4940
4941 case T_options:
4942 parsePAD_option( pad.get() );
4943 break;
4944
4945 case T_primitives:
4946 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4947 {
4948 if( token == T_LEFT )
4949 token = NextTok();
4950
4951 // Currently, I am using parsePCB_SHAPE() to read basic shapes parameters,
4952 // because they are the same as a PCB_SHAPE.
4953 // However it could be better to write a specific parser, to avoid possible issues
4954 // if the PCB_SHAPE parser is modified.
4955 PCB_SHAPE* dummyShape = nullptr;
4956
4957 switch( token )
4958 {
4959 case T_gr_arc:
4960 dummyShape = parsePCB_SHAPE();
4961 pad->AddPrimitiveArc( dummyShape->GetCenter(), dummyShape->GetStart(),
4962 dummyShape->GetArcAngle(), dummyShape->GetWidth() );
4963 break;
4964
4965 case T_gr_line:
4966 dummyShape = parsePCB_SHAPE();
4967 pad->AddPrimitiveSegment( dummyShape->GetStart(), dummyShape->GetEnd(),
4968 dummyShape->GetWidth() );
4969 break;
4970
4971 case T_gr_circle:
4972 dummyShape = parsePCB_SHAPE();
4973 pad->AddPrimitiveCircle( dummyShape->GetCenter(), dummyShape->GetRadius(),
4974 dummyShape->GetWidth(), dummyShape->IsFilled() );
4975 break;
4976
4977 case T_gr_rect:
4978 dummyShape = parsePCB_SHAPE();
4979 pad->AddPrimitiveRect( dummyShape->GetStart(), dummyShape->GetEnd(),
4980 dummyShape->GetWidth(), dummyShape->IsFilled() );
4981 break;
4982
4983 case T_gr_bbox:
4984 dummyShape = parsePCB_SHAPE();
4985 pad->AddPrimitiveAnnotationBox( dummyShape->GetStart(), dummyShape->GetEnd() );
4986 break;
4987
4988 case T_gr_poly:
4989 dummyShape = parsePCB_SHAPE();
4990 pad->AddPrimitivePoly( dummyShape->GetPolyShape(), dummyShape->GetWidth(),
4991 dummyShape->IsFilled() );
4992 break;
4993
4994 case T_gr_curve:
4995 dummyShape = parsePCB_SHAPE();
4996 pad->AddPrimitiveCurve( dummyShape->GetStart(), dummyShape->GetEnd(),
4997 dummyShape->GetBezierC1(), dummyShape->GetBezierC2(),
4998 dummyShape->GetWidth() );
4999 break;
5000
5001 default:
5002 Expecting( "gr_line, gr_arc, gr_circle, gr_curve, gr_rect, gr_bbox or gr_poly" );
5003 break;
5004 }
5005
5006 delete dummyShape;
5007 }
5008
5009 break;
5010
5011 case T_remove_unused_layers:
5012 pad->SetRemoveUnconnected( true );
5013 NeedRIGHT();
5014 break;
5015
5016 case T_keep_end_layers:
5017 pad->SetKeepTopBottom( true );
5018 NeedRIGHT();
5019 break;
5020
5021 case T_zone_layer_connections:
5022 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5023 {
5024 PCB_LAYER_ID layer = lookUpLayer<PCB_LAYER_ID>( m_layerIndices );
5025
5026 if( layer < F_Cu || layer > B_Cu )
5027 Expecting( "copper layer name" );
5028
5029 pad->ZoneConnectionCache( layer ) = ZLC_CONNECTED;
5030 }
5031
5032 break;
5033
5034 // Continue to process "(locked)" format which was output during 5.99 development
5035 case T_locked:
5036 // Pad locking is now a session preference
5037 NeedRIGHT();
5038 break;
5039
5040 case T_tstamp:
5041 NextTok();
5042 const_cast<KIID&>( pad->m_Uuid ) = CurStrToKIID();
5043 NeedRIGHT();
5044 break;
5045
5046 default:
5047 Expecting( "at, locked, drill, layers, net, die_length, roundrect_rratio, "
5048 "solder_mask_margin, solder_paste_margin, solder_paste_margin_ratio, "
5049 "clearance, tstamp, primitives, remove_unused_layers, keep_end_layers, "
5050 "pinfunction, pintype, zone_connect, thermal_width, or thermal_gap" );
5051 }
5052 }
5053
5054 if( !pad->CanHaveNumber() )
5055 {
5056 // At some point it was possible to assign a number to aperture pads so we need to clean
5057 // those out here.
5058 pad->SetNumber( wxEmptyString );
5059 }
5060
5061 // Zero-sized pads are likely algorithmically unsafe.
5062 if( pad->GetSizeX() <= 0 || pad->GetSizeY() <= 0 )
5063 {
5064 pad->SetSize( VECTOR2I( pcbIUScale.mmToIU( 0.001 ), pcbIUScale.mmToIU( 0.001 ) ) );
5065
5066 wxLogWarning( _( "Invalid zero-sized pad pinned to %s in\nfile: %s\nline: %d\noffset: %d" ),
5067 wxT( "1┬Ám" ), CurSource(), CurLineNumber(), CurOffset() );
5068 }
5069
5070 return pad.release();
5071}
5072
5073
5075{
5076 // Parse only the (option ...) inside a pad description
5077 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
5078 {
5079 if( token != T_LEFT )
5080 Expecting( T_LEFT );
5081
5082 token = NextTok();
5083
5084 switch( token )
5085 {
5086 case T_anchor:
5087 token = NextTok();
5088 // Custom shaped pads have a "anchor pad", which is the reference
5089 // for connection calculations.
5090 // Because this is an anchor, only the 2 very basic shapes are managed:
5091 // circle and rect. The default is circle
5092 switch( token )
5093 {
5094 case T_circle: // default
5095 break;
5096
5097 case T_rect:
5099 break;
5100
5101 default:
5102 // Currently, because pad options is a moving target
5103 // just skip unknown keywords
5104 break;
5105 }
5106 NeedRIGHT();
5107 break;
5108
5109 case T_clearance:
5110 token = NextTok();
5111 // Custom shaped pads have a clearance area that is the pad shape
5112 // (like usual pads) or the convex hull of the pad shape.
5113 switch( token )
5114 {
5115 case T_outline:
5117 break;
5118
5119 case T_convexhull:
5121 break;
5122
5123 default:
5124 // Currently, because pad options is a moving target
5125 // just skip unknown keywords
5126 break;
5127 }
5128
5129 NeedRIGHT();
5130 break;
5131
5132 default:
5133 // Currently, because pad options is a moving target
5134 // just skip unknown keywords
5135 while( (token = NextTok() ) != T_RIGHT )
5136 {}
5137
5138 break;
5139 }
5140 }
5141
5142 return true;
5143}
5144
5145
5147{
5148 wxCHECK_RET( CurTok() == T_group,
5149 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_GROUP." ) );
5150
5151 VECTOR2I pt;
5152 T token;
5153
5154 m_groupInfos.push_back( GROUP_INFO() );
5155 GROUP_INFO& groupInfo = m_groupInfos.back();
5156 groupInfo.parent = aParent;
5157
5158 while( ( token = NextTok() ) != T_LEFT )
5159 {
5160 if( token == T_STRING )
5161 groupInfo.name = FromUTF8();
5162 else if( token == T_locked )
5163 groupInfo.locked = true;
5164 else
5165 Expecting( "group name or locked" );
5166 }
5167
5168 token = NextTok();
5169
5170 if( token != T_id )
5171 Expecting( T_id );
5172
5173 NextTok();
5174 groupInfo.uuid = CurStrToKIID();
5175 NeedRIGHT();
5176
5177 NeedLEFT();
5178 token = NextTok();
5179
5180 if( token != T_members )
5181 Expecting( T_members );
5182
5183 while( ( token = NextTok() ) != T_RIGHT )
5184 {
5185 // This token is the Uuid of the item in the group.
5186 // Since groups are serialized at the end of the file/footprint, the Uuid should already
5187 // have been seen and exist in the board.
5188 KIID uuid( CurStr() );
5189 groupInfo.memberUuids.push_back( uuid );
5190 }
5191
5192 NeedRIGHT();
5193}
5194
5195
5197{
5198 wxCHECK_MSG( CurTok() == T_arc, nullptr,
5199 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as ARC." ) );
5200
5201 VECTOR2I pt;
5202 T token;
5203
5204 std::unique_ptr<PCB_ARC> arc = std::make_unique<PCB_ARC>( m_board );
5205
5206 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5207 {
5208 if( token == T_locked )
5209 {
5210 arc->SetLocked( true );
5211 token = NextTok();
5212 }
5213
5214 if( token != T_LEFT )
5215 Expecting( T_LEFT );
5216
5217 token = NextTok();
5218
5219 switch( token )
5220 {
5221 case T_start:
5222 pt.x = parseBoardUnits( "start x" );
5223 pt.y = parseBoardUnits( "start y" );
5224 arc->SetStart( pt );
5225 break;
5226
5227 case T_mid:
5228 pt.x = parseBoardUnits( "mid x" );
5229 pt.y = parseBoardUnits( "mid y" );
5230 arc->SetMid( pt );
5231 break;
5232
5233 case T_end:
5234 pt.x = parseBoardUnits( "end x" );
5235 pt.y = parseBoardUnits( "end y" );
5236 arc->SetEnd( pt );
5237 break;
5238
5239 case T_width:
5240 arc->SetWidth( parseBoardUnits( "width" ) );
5241 break;
5242
5243 case T_layer:
5244 arc->SetLayer( parseBoardItemLayer() );
5245 break;
5246
5247 case T_net:
5248 if( !arc->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
5249 {
5250 wxLogError( _( "Invalid net ID in\nfile: %s\nline: %d\noffset: %d." ),
5251 CurSource(), CurLineNumber(), CurOffset() );
5252 }
5253 break;
5254
5255 case T_tstamp:
5256 NextTok();
5257 const_cast<KIID&>( arc->m_Uuid ) = CurStrToKIID();
5258 break;
5259
5260 // We continue to parse the status field but it is no longer written
5261 case T_status:
5262 arc->SetStatus( static_cast<EDA_ITEM_FLAGS>( parseHex() ) );
5263 break;
5264
5265 // Continue to process "(locked)" format which was output during 5.99 development
5266 case T_locked:
5267 arc->SetLocked( true );
5268 break;
5269
5270 default:
5271 Expecting( "start, mid, end, width, layer, net, tstamp, or status" );
5272 }
5273
5274 NeedRIGHT();
5275 }
5276
5277 return arc.release();
5278}
5279
5280
5282{
5283 wxCHECK_MSG( CurTok() == T_segment, nullptr,
5284 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TRACK." ) );
5285
5286 VECTOR2I pt;
5287 T token;
5288
5289 std::unique_ptr<PCB_TRACK> track = std::make_unique<PCB_TRACK>( m_board );
5290
5291 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5292 {
5293 if( token == T_locked )
5294 {
5295 track->SetLocked( true );
5296 token = NextTok();
5297 }
5298
5299 if( token != T_LEFT )
5300 Expecting( T_LEFT );
5301
5302 token = NextTok();
5303
5304 switch( token )
5305 {
5306 case T_start:
5307 pt.x = parseBoardUnits( "start x" );
5308 pt.y = parseBoardUnits( "start y" );
5309 track->SetStart( pt );
5310 break;
5311
5312 case T_end:
5313 pt.x = parseBoardUnits( "end x" );
5314 pt.y = parseBoardUnits( "end y" );
5315 track->SetEnd( pt );
5316 break;
5317
5318 case T_width:
5319 track->SetWidth( parseBoardUnits( "width" ) );
5320 break;
5321
5322 case T_layer:
5323 track->SetLayer( parseBoardItemLayer() );
5324 break;
5325
5326 case T_net:
5327 if( !track->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
5328 {
5329 wxLogError( _( "Invalid net ID in\nfile: '%s'\nline: %d\noffset: %d." ),
5330 CurSource(), CurLineNumber(), CurOffset() );
5331 }
5332 break;
5333
5334 case T_tstamp:
5335 NextTok();
5336 const_cast<KIID&>( track->m_Uuid ) = CurStrToKIID();
5337 break;
5338
5339 // We continue to parse the status field but it is no longer written
5340 case T_status:
5341 track->SetStatus( static_cast<EDA_ITEM_FLAGS>( parseHex() ) );
5342 break;
5343
5344 // Continue to process "(locked)" format which was output during 5.99 development
5345 case T_locked:
5346 track->SetLocked( true );
5347 break;
5348
5349 default:
5350 Expecting( "start, end, width, layer, net, tstamp, or locked" );
5351 }
5352
5353 NeedRIGHT();
5354 }
5355
5356 return track.release();
5357}
5358
5359
5361{
5362 wxCHECK_MSG( CurTok() == T_via, nullptr,
5363 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_VIA." ) );
5364
5365 VECTOR2I pt;
5366 T token;
5367
5368 std::unique_ptr<PCB_VIA> via = std::make_unique<PCB_VIA>( m_board );
5369
5370 // File format default is no-token == no-feature.
5371 via->SetRemoveUnconnected( false );
5372 via->SetKeepTopBottom( false );
5373
5374 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5375 {
5376 if( token == T_locked )
5377 {
5378 via->SetLocked( true );
5379 token = NextTok();
5380 }
5381
5382 if( token == T_LEFT )
5383 token = NextTok();
5384
5385 switch( token )
5386 {
5387 case T_blind:
5388 via->SetViaType( VIATYPE::BLIND_BURIED );
5389 break;
5390
5391 case T_micro:
5392 via->SetViaType( VIATYPE::MICROVIA );
5393 break;
5394
5395 case T_at:
5396 pt.x = parseBoardUnits( "start x" );
5397 pt.y = parseBoardUnits( "start y" );
5398 via->SetStart( pt );
5399 via->SetEnd( pt );
5400 NeedRIGHT();
5401 break;
5402
5403 case T_size:
5404 via->SetWidth( parseBoardUnits( "via width" ) );
5405 NeedRIGHT();
5406 break;
5407
5408 case T_drill:
5409 via->SetDrill( parseBoardUnits( "drill diameter" ) );
5410 NeedRIGHT();
5411 break;
5412
5413 case T_layers:
5414 {
5415 PCB_LAYER_ID layer1, layer2;
5416 NextTok();
5417 layer1 = lookUpLayer<PCB_LAYER_ID>( m_layerIndices );
5418 NextTok();
5419 layer2 = lookUpLayer<PCB_LAYER_ID>( m_layerIndices );
5420 via->SetLayerPair( layer1, layer2 );
5421 NeedRIGHT();
5422 break;
5423 }
5424
5425 case T_net:
5426 if( !via->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
5427 {
5428 wxLogError( _( "Invalid net ID in\nfile: %s\nline: %d\noffset: %d" ),
5429 CurSource(), CurLineNumber(), CurOffset() );
5430 }
5431
5432 NeedRIGHT();
5433 break;
5434
5435 case T_remove_unused_layers:
5436 via->SetRemoveUnconnected( true );
5437 NeedRIGHT();
5438 break;
5439
5440 case T_keep_end_layers:
5441 via->SetKeepTopBottom( true );
5442 NeedRIGHT();
5443 break;
5444
5445 case T_zone_layer_connections:
5446 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5447 {
5448 PCB_LAYER_ID layer = lookUpLayer<PCB_LAYER_ID>( m_layerIndices );
5449
5450 if( layer < F_Cu || layer > B_Cu )
5451 Expecting( "copper layer name" );
5452
5453 via->ZoneConnectionCache( layer ) = ZLC_CONNECTED;
5454 }
5455
5456 break;
5457
5458 case T_tstamp:
5459 NextTok();
5460 const_cast<KIID&>( via->m_Uuid ) = CurStrToKIID();
5461 NeedRIGHT();
5462 break;
5463
5464 // We continue to parse the status field but it is no longer written
5465 case T_status:
5466 via->SetStatus( static_cast<EDA_ITEM_FLAGS>( parseHex() ) );
5467 NeedRIGHT();
5468 break;
5469
5470 // Continue to process "(locked)" format which was output during 5.99 development
5471 case T_locked:
5472 via->SetLocked( true );
5473 NeedRIGHT();
5474 break;
5475
5476 case T_free:
5477 via->SetIsFree();
5478 NeedRIGHT();
5479 break;
5480
5481 default:
5482 Expecting( "blind, micro, at, size, drill, layers, net, free, tstamp, or status" );
5483 }
5484 }
5485
5486 return via.release();
5487}
5488
5489
5491{
5492 wxCHECK_MSG( CurTok() == T_zone, nullptr,
5493 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as ZONE." ) );
5494
5496
5497 int hatchPitch = ZONE::GetDefaultHatchPitch();
5498 VECTOR2I pt;
5499 T token;
5500 int tmp;
5501 wxString netnameFromfile; // the zone net name find in file
5502
5503 // bigger scope since each filled_polygon is concatenated in here
5504 std::map<PCB_LAYER_ID, SHAPE_POLY_SET> pts;
5505 bool inFootprint = false;
5506 PCB_LAYER_ID filledLayer;
5507 bool addedFilledPolygons = false;
5508 bool dropFilledPolygons = false;
5509
5510 if( dynamic_cast<FOOTPRINT*>( aParent ) ) // The zone belongs a footprint
5511 inFootprint = true;
5512
5513 std::unique_ptr<ZONE> zone;
5514
5515 if( inFootprint )
5516 zone = std::make_unique<FP_ZONE>( aParent );
5517 else
5518 zone = std::make_unique<ZONE>( aParent );
5519
5520 zone->SetAssignedPriority( 0 );
5521
5522 // This is the default for board files:
5523 zone->SetIslandRemovalMode( ISLAND_REMOVAL_MODE::ALWAYS );
5524
5525 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5526 {
5527 if( token == T_locked )
5528 {
5529 zone->SetLocked( true );
5530 token = NextTok();
5531 }
5532
5533 if( token == T_LEFT )
5534 token = NextTok();
5535
5536 switch( token )
5537 {
5538 case T_net:
5539 // Init the net code only, not the netname, to be sure
5540 // the zone net name is the name read in file.
5541 // (When mismatch, the user will be prompted in DRC, to fix the actual name)
5542 tmp = getNetCode( parseInt( "net number" ) );
5543
5544 if( tmp < 0 )
5545 tmp = 0;
5546
5547 if( !zone->SetNetCode( tmp, /* aNoAssert */ true ) )
5548 {
5549 wxLogError( _( "Invalid net ID in\nfile: %s;\nline: %d\noffset: %d." ),
5550 CurSource(), CurLineNumber(), CurOffset() );
5551 }
5552
5553 NeedRIGHT();
5554 break;
5555
5556 case T_net_name:
5557 NeedSYMBOLorNUMBER();
5558 netnameFromfile = FromUTF8();
5559 NeedRIGHT();
5560 break;
5561
5562 case T_layer: // keyword for zones that are on only one layer
5563 zone->SetLayer( parseBoardItemLayer() );
5564 NeedRIGHT();
5565 break;
5566
5567 case T_layers: // keyword for zones that can live on a set of layers
5568 zone->SetLayerSet( parseBoardItemLayersAsMask() );
5569 break;
5570
5571 case T_tstamp:
5572 NextTok();
5573 const_cast<KIID&>( zone->m_Uuid ) = CurStrToKIID();
5574 NeedRIGHT();
5575 break;
5576
5577 case T_hatch:
5578 token = NextTok();
5579
5580 if( token != T_none && token != T_edge && token != T_full )
5581 Expecting( "none, edge, or full" );
5582
5583 switch( token )
5584 {
5585 default:
5586 case T_none: hatchStyle = ZONE_BORDER_DISPLAY_STYLE::NO_HATCH; break;
5587 case T_edge: hatchStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE; break;
5588 case T_full: hatchStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL; break;
5589 }
5590
5591 hatchPitch = parseBoardUnits( "hatch pitch" );
5592 NeedRIGHT();
5593 break;
5594
5595 case T_priority:
5596 zone->SetAssignedPriority( parseInt( "zone priority" ) );
5597 NeedRIGHT();
5598 break;
5599
5600 case T_connect_pads:
5601 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5602 {
5603 if( token == T_LEFT )
5604 token = NextTok();
5605
5606 switch( token )
5607 {
5608 case T_yes:
5609 zone->SetPadConnection( ZONE_CONNECTION::FULL );
5610 break;
5611
5612 case T_no:
5613 zone->SetPadConnection( ZONE_CONNECTION::NONE );
5614 break;
5615
5616 case T_thru_hole_only:
5617 zone->SetPadConnection( ZONE_CONNECTION::THT_THERMAL );
5618 break;
5619
5620 case T_clearance:
5621 zone->SetLocalClearance( parseBoardUnits( "zone clearance" ) );
5622 NeedRIGHT();
5623 break;
5624
5625 default:
5626 Expecting( "yes, no, or clearance" );
5627 }
5628 }
5629
5630 break;
5631
5632 case T_min_thickness:
5633 zone->SetMinThickness( parseBoardUnits( T_min_thickness ) );
5634 NeedRIGHT();
5635 break;
5636
5637 case T_filled_areas_thickness:
5638 if( parseBool() )
5639 {
5641 {
5642 if( !(*m_queryUserCallback)(
5643 _( "Legacy Zone Warning" ), wxICON_WARNING,
5644 _( "The legacy zone fill strategy is no longer supported.\n"
5645 "Convert zones to smoothed polygon fills?" ),
5646 _( "Convert" ) ) )
5647 {
5648 THROW_IO_ERROR( wxT( "CANCEL" ) );
5649 }
5650 }
5651
5653 zone->SetFlags( CANDIDATE );
5654 dropFilledPolygons = true;
5655 }
5656
5657 NeedRIGHT();
5658 break;
5659
5660 case T_fill:
5661 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5662 {
5663 if( token == T_LEFT )
5664 token = NextTok();
5665
5666 switch( token )
5667 {
5668 case T_yes:
5669 zone->SetIsFilled( true );
5670 break;
5671
5672 case T_mode:
5673 token = NextTok();
5674
5675 if( token != T_segment && token != T_hatch && token != T_polygon )
5676 Expecting( "segment, hatch or polygon" );
5677
5678 if( token == T_segment ) // deprecated
5679 {
5681 {
5682 if( !(*m_queryUserCallback)(
5683 _( "Legacy Zone Warning" ), wxICON_WARNING,
5684 _( "The segment zone fill mode is no longer supported.\n"
5685 "Convert zones to smoothed polygon fills?" ),
5686 _( "Convert" ) ) )
5687 {
5688 THROW_IO_ERROR( wxT( "CANCEL" ) );
5689 }
5690 }
5691
5693 zone->SetFlags( CANDIDATE );
5694 zone->SetFillMode( ZONE_FILL_MODE::POLYGONS );
5696 }
5697 else if( token == T_hatch )
5698 {
5699 zone->SetFillMode( ZONE_FILL_MODE::HATCH_PATTERN );
5700 }
5701 else
5702 {
5703 zone->SetFillMode( ZONE_FILL_MODE::POLYGONS );
5704 }
5705
5706 NeedRIGHT();
5707 break;
5708
5709 case T_hatch_thickness:
5710 zone->SetHatchThickness( parseBoardUnits( T_hatch_thickness ) );
5711 NeedRIGHT();
5712 break;
5713
5714 case T_hatch_gap:
5715 zone->SetHatchGap( parseBoardUnits( T_hatch_gap ) );
5716 NeedRIGHT();
5717 break;
5718
5719 case T_hatch_orientation:
5720 {
5721 EDA_ANGLE orientation( parseDouble( T_hatch_orientation ), DEGREES_T );
5722 zone->SetHatchOrientation( orientation );
5723 NeedRIGHT();
5724 break;
5725 }
5726
5727 case T_hatch_smoothing_level:
5728 zone->SetHatchSmoothingLevel( parseDouble( T_hatch_smoothing_level ) );
5729 NeedRIGHT();
5730 break;
5731
5732 case T_hatch_smoothing_value:
5733 zone->SetHatchSmoothingValue( parseDouble( T_hatch_smoothing_value ) );
5734 NeedRIGHT();
5735 break;
5736
5737 case T_hatch_border_algorithm:
5738 token = NextTok();
5739
5740 if( token != T_hatch_thickness && token != T_min_thickness )
5741 Expecting( "hatch_thickness or min_thickness" );
5742
5743 zone->SetHatchBorderAlgorithm( token == T_hatch_thickness ? 1 : 0 );
5744 NeedRIGHT();
5745 break;
5746
5747 case T_hatch_min_hole_area:
5748 zone->SetHatchHoleMinArea( parseDouble( T_hatch_min_hole_area ) );
5749 NeedRIGHT();
5750 break;
5751
5752 case T_arc_segments:
5753 ignore_unused( parseInt( "arc segment count" ) );
5754 NeedRIGHT();
5755 break;
5756
5757 case T_thermal_gap:
5758 zone->SetThermalReliefGap( parseBoardUnits( T_thermal_gap ) );
5759 NeedRIGHT();
5760 break;
5761
5762 case T_thermal_bridge_width:
5763 zone->SetThermalReliefSpokeWidth( parseBoardUnits( T_thermal_bridge_width ) );
5764 NeedRIGHT();
5765 break;
5766
5767 case T_smoothing:
5768 switch( NextTok() )
5769 {
5770 case T_none:
5771 zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_NONE );
5772 break;
5773
5774 case T_chamfer:
5775 if( !zone->GetIsRuleArea() ) // smoothing has meaning only for filled zones
5776 zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_CHAMFER );
5777
5778 break;
5779
5780 case T_fillet:
5781 if( !zone->GetIsRuleArea() ) // smoothing has meaning only for filled zones
5782 zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_FILLET );
5783
5784 break;
5785
5786 default:
5787 Expecting( "none, chamfer, or fillet" );
5788 }
5789
5790 NeedRIGHT();
5791 break;
5792
5793 case T_radius:
5794 tmp = parseBoardUnits( "corner radius" );
5795 if( !zone->GetIsRuleArea() ) // smoothing has meaning only for filled zones
5796 zone->SetCornerRadius( tmp );
5797 NeedRIGHT();
5798 break;
5799
5800 case T_island_removal_mode:
5801 tmp = parseInt( "island_removal_mode" );
5802
5803 if( tmp >= 0 && tmp <= 2 )
5804 zone->SetIslandRemovalMode( static_cast<ISLAND_REMOVAL_MODE>( tmp ) );
5805
5806 NeedRIGHT();
5807 break;
5808
5809 case T_island_area_min:
5810 {
5811 int area = parseBoardUnits( T_island_area_min );
5812 zone->SetMinIslandArea( area * pcbIUScale.IU_PER_MM );
5813 NeedRIGHT();
5814 break;
5815 }
5816
5817 default:
5818 Expecting( "mode, arc_segments, thermal_gap, thermal_bridge_width, "
5819 "hatch_thickness, hatch_gap, hatch_orientation, "
5820 "hatch_smoothing_level, hatch_smoothing_value, "
5821 "hatch_border_algorithm, hatch_min_hole_area, smoothing, radius, "
5822 "island_removal_mode, or island_area_min" );
5823 }
5824 }
5825
5826 break;
5827
5828 case T_keepout:
5829 // "keepout" now means rule area, but the file token stays the same
5830 zone->SetIsRuleArea( true );
5831
5832 // Initialize these two because their tokens won't appear in older files:
5833 zone->SetDoNotAllowPads( false );
5834 zone->SetDoNotAllowFootprints( false );
5835
5836 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5837 {
5838 if( token == T_LEFT )
5839 token = NextTok();
5840
5841 switch( token )
5842 {
5843 case T_tracks:
5844 token = NextTok();
5845
5846 if( token != T_allowed && token != T_not_allowed )
5847 Expecting( "allowed or not_allowed" );
5848 zone->SetDoNotAllowTracks( token == T_not_allowed );
5849 break;
5850
5851 case T_vias:
5852 token = NextTok();
5853
5854 if( token != T_allowed && token != T_not_allowed )
5855 Expecting( "allowed or not_allowed" );
5856 zone->SetDoNotAllowVias( token == T_not_allowed );
5857 break;
5858
5859 case T_copperpour:
5860 token = NextTok();
5861
5862 if( token != T_allowed && token != T_not_allowed )
5863 Expecting( "allowed or not_allowed" );
5864 zone->SetDoNotAllowCopperPour( token == T_not_allowed );
5865 break;
5866
5867 case T_pads:
5868 token = NextTok();
5869
5870 if( token != T_allowed && token != T_not_allowed )
5871 Expecting( "allowed or not_allowed" );
5872 zone->SetDoNotAllowPads( token == T_not_allowed );
5873 break;
5874
5875 case T_footprints:
5876 token = NextTok();
5877
5878 if( token != T_allowed && token != T_not_allowed )
5879 Expecting( "allowed or not_allowed" );
5880 zone->SetDoNotAllowFootprints( token == T_not_allowed );
5881 break;
5882
5883 default:
5884 Expecting( "tracks, vias or copperpour" );
5885 }
5886
5887 NeedRIGHT();
5888 }
5889
5890 break;
5891
5892 case T_polygon:
5893 {
5894 SHAPE_LINE_CHAIN outline;
5895
5896 NeedLEFT();
5897 token = NextTok();
5898
5899 if( token != T_pts )
5900 Expecting( T_pts );
5901
5902 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5903 parseOutlinePoints( outline );
5904
5905 NeedRIGHT();
5906
5907 outline.SetClosed( true );
5908
5909 // Remark: The first polygon is the main outline.
5910 // Others are holes inside the main outline.
5911 zone->AddPolygon( outline );
5912 break;
5913 }
5914
5915 case T_filled_polygon:
5916 {
5917 // "(filled_polygon (pts"
5918 NeedLEFT();
5919 token = NextTok();
5920
5921 if( token == T_layer )
5922 {
5923 filledLayer = parseBoardItemLayer();
5924 NeedRIGHT();
5925 token = NextTok();
5926
5927 if( token != T_LEFT )
5928 Expecting( T_LEFT );
5929
5930 token = NextTok();
5931 }
5932 else
5933 {
5934 // for legacy, single-layer zones
5935 filledLayer = zone->GetFirstLayer();
5936 }
5937
5938 bool island = false;
5939
5940 if( token == T_island )
5941 {
5942 island = true;
5943 NeedRIGHT();
5944 NeedLEFT();
5945 token = NextTok();
5946 }
5947
5948 if( token != T_pts )
5949 Expecting( T_pts );
5950
5951 if( !pts.count( filledLayer ) )
5952 pts[filledLayer] = SHAPE_POLY_SET();
5953
5954 SHAPE_POLY_SET& poly = pts.at( filledLayer );
5955
5956 int idx = poly.NewOutline();
5957 SHAPE_LINE_CHAIN& chain = poly.Outline( idx );
5958
5959 if( island )
5960 zone->SetIsIsland( filledLayer, idx );
5961
5962 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5963 parseOutlinePoints( chain );
5964
5965 NeedRIGHT();
5966
5967 addedFilledPolygons |= !poly.IsEmpty();
5968 }
5969
5970 break;
5971
5972 case T_fill_segments:
5973 {
5974 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5975 {
5976 if( token != T_LEFT )
5977 Expecting( T_LEFT );
5978
5979 token = NextTok();
5980
5981 if( token == T_layer )
5982 {
5983 filledLayer = parseBoardItemLayer();
5984 NeedRIGHT();
5985 token = NextTok();
5986
5987 if( token != T_LEFT )
5988 Expecting( T_LEFT );
5989
5990 token = NextTok();
5991 }
5992 else
5993 {
5994 filledLayer = zone->GetLayer();
5995 }
5996
5997 if( token != T_pts )
5998 Expecting( T_pts );
5999
6002 NeedRIGHT();
6003 }
6004
6005 break;
6006 }
6007
6008 case T_name:
6009 NextTok();
6010 zone->SetZoneName( FromUTF8() );
6011
6012 // TODO: remove this hack in next future, now keywords are added for teadrop attribute
6013 // If a zone name starts by "$teardrop_", set its teardrop property flag
6014 if( zone->GetZoneName().StartsWith( "$teardrop_p" ) )
6015 zone->SetTeardropAreaType( TEARDROP_TYPE::TD_VIAPAD );
6016 else if( zone->GetZoneName().StartsWith( "$teardrop_t" ) )
6017 zone->SetTeardropAreaType( TEARDROP_TYPE::TD_TRACKEND );
6018
6019 NeedRIGHT();
6020 break;
6021
6022 case T_attr:
6023 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6024 {
6025 if( token == T_LEFT )
6026 token = NextTok();
6027
6028 switch( token )
6029 {
6030 case T_teardrop:
6031 token = NextTok();
6032
6033 // Expected teardrop data (type padvia) or (type track_end)
6034 if( token == T_LEFT )
6035 {
6036 token = NextTok();
6037
6038 if( token == T_type )
6039 {
6040 token = NextTok();
6041
6042 if( token == T_padvia )
6043 {
6044 zone->SetTeardropAreaType( TEARDROP_TYPE::TD_VIAPAD );
6045 NeedRIGHT();
6046 }
6047 else if( token == T_track_end )
6048 {
6049 zone->SetTeardropAreaType( TEARDROP_TYPE::TD_TRACKEND );
6050 NeedRIGHT();
6051 }
6052 else
6053 Expecting( "padvia or track_end" );
6054
6055 NeedRIGHT();
6056 }
6057 else
6058 Expecting( "type" );
6059 }
6060 else
6061 Expecting( "(" );
6062
6063 break;
6064
6065 default:
6066 Expecting( "teardrop" );
6067 }
6068 }
6069 break;
6070
6071 default:
6072 Expecting( "net, layer/layers, tstamp, hatch, priority, connect_pads, min_thickness, "
6073 "fill, polygon, filled_polygon, fill_segments, attr, or name" );
6074 }
6075 }
6076
6077 if( zone->GetNumCorners() > 2 )
6078 {
6079 if( !zone->IsOnCopperLayer() )
6080 {
6081 //zone->SetFillMode( ZONE_FILL_MODE::POLYGONS );
6082 zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
6083 }
6084
6085 // Set hatch here, after outlines corners are read
6086 zone->SetBorderDisplayStyle( hatchStyle, hatchPitch, true );
6087 }
6088
6089 if( addedFilledPolygons && !dropFilledPolygons )
6090 {
6091 for( auto& pair : pts )
6092 zone->SetFilledPolysList( pair.first, pair.second );
6093
6094 zone->CalculateFilledArea();
6095 }
6096
6097 // Ensure keepout and non copper zones do not have a net
6098 // (which have no sense for these zones)
6099 // the netcode 0 is used for these zones
6100 bool zone_has_net = zone->IsOnCopperLayer() && !zone->GetIsRuleArea();
6101
6102 if( !zone_has_net )
6103 zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
6104
6105 // Ensure the zone net name is valid, and matches the net code, for copper zones
6106 if( zone_has_net && ( zone->GetNet()->GetNetname() != netnameFromfile ) )
6107 {
6108 // Can happens which old boards, with nonexistent nets ...
6109 // or after being edited by hand
6110 // We try to fix the mismatch.
6111 NETINFO_ITEM* net = m_board->FindNet( netnameFromfile );
6112
6113 if( net ) // An existing net has the same net name. use it for the zone
6114 {
6115 zone->SetNetCode( net->GetNetCode() );
6116 }
6117 else // Not existing net: add a new net to keep trace of the zone netname
6118 {
6119 int newnetcode = m_board->GetNetCount();
6120 net = new NETINFO_ITEM( m_board, netnameFromfile, newnetcode );
6121 m_board->Add( net, ADD_MODE::INSERT, true );
6122
6123 // Store the new code mapping
6124 pushValueIntoMap( newnetcode, net->GetNetCode() );
6125
6126 // and update the zone netcode
6127 zone->SetNetCode( net->GetNetCode() );
6128 }
6129 }
6130
6131 // Clear flags used in zone edition:
6132 zone->SetNeedRefill( false );
6133
6134 return zone.release();
6135}
6136
6137
6139{
6140 wxCHECK_MSG( CurTok() == T_target, nullptr,
6141 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TARGET." ) );
6142
6143 VECTOR2I pt;
6144 T token;
6145
6146 std::unique_ptr<PCB_TARGET> target = std::make_unique<PCB_TARGET>( nullptr );
6147
6148 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6149 {
6150 if( token == T_LEFT )
6151 token = NextTok();
6152
6153 switch( token )
6154 {
6155 case T_x:
6156 target->SetShape( 1 );
6157 break;
6158
6159 case T_plus:
6160 target->SetShape( 0 );
6161 break;
6162
6163 case T_at:
6164 pt.x = parseBoardUnits( "target x position" );
6165 pt.y = parseBoardUnits( "target y position" );
6166 target->SetPosition( pt );
6167 NeedRIGHT();
6168 break;
6169
6170 case T_size:
6171 target->SetSize( parseBoardUnits( "target size" ) );
6172 NeedRIGHT();
6173 break;
6174
6175 case T_width:
6176 target->SetWidth( parseBoardUnits( "target thickness" ) );
6177 NeedRIGHT();
6178 break;
6179
6180 case T_layer:
6181 target->SetLayer( parseBoardItemLayer() );
6182 NeedRIGHT();
6183 break;
6184
6185 case T_tstamp:
6186 NextTok();
6187 const_cast<KIID&>( target->m_Uuid ) = CurStrToKIID();
6188 NeedRIGHT();
6189 break;
6190
6191 default:
6192 Expecting( "x, plus, at, size, width, layer or tstamp" );
6193 }
6194 }
6195
6196 return target.release();
6197}
6198
6199
6201{
6202 KIID aId;
6203
6204 if( m_appendToExisting )
6205 {
6206 aId = KIID();
6207 m_resetKIIDMap.insert( std::make_pair( CurStr(), aId ) );
6208 }
6209 else
6210 {
6211 aId = KIID( CurStr() );
6212 }
6213
6214 return aId;
6215}
int color
Definition: DXF_plotter.cpp:57
const char * name
Definition: DXF_plotter.cpp:56
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
@ LT_UNDEFINED
Definition: board.h:144
@ LAYER_CLASS_OTHERS
@ LAYER_CLASS_FAB
@ LAYER_CLASS_COURTYARD
@ LAYER_CLASS_SILK
@ LAYER_CLASS_COPPER
@ LAYER_CLASS_EDGES
#define DEFAULT_LINE_WIDTH
@ ZLC_CONNECTED
Definition: board_item.h:47
@ BS_EDGE_CONNECTOR_BEVELLED
Definition: board_stackup.h:57
@ BS_EDGE_CONNECTOR_NONE
Definition: board_stackup.h:55
@ BS_EDGE_CONNECTOR_IN_USE
Definition: board_stackup.h:56
BOARD_STACKUP_ITEM_TYPE
Definition: board_stackup.h:41
@ BS_ITEM_TYPE_UNDEFINED
Definition: board_stackup.h:42
@ BS_ITEM_TYPE_COPPER
Definition: board_stackup.h:43
@ BS_ITEM_TYPE_SILKSCREEN
Definition: board_stackup.h:49
@ BS_ITEM_TYPE_DIELECTRIC
Definition: board_stackup.h:44
@ BS_ITEM_TYPE_SOLDERPASTE
Definition: board_stackup.h:46
@ BS_ITEM_TYPE_SOLDERMASK
Definition: board_stackup.h:47
Container for design settings for a BOARD object.
std::shared_ptr< NET_SETTINGS > m_NetSettings
void SetGridOrigin(const VECTOR2I &aOrigin)
bool m_TextUpright[LAYER_CLASS_COUNT]
std::vector< DIFF_PAIR_DIMENSION > m_DiffPairDimensionsList
std::unique_ptr< PAD > m_Pad_Master
void SetAuxOrigin(const VECTOR2I &aOrigin)
int m_DimensionPrecision
Number of digits after the decimal.
BOARD_STACKUP & GetStackupDescriptor()
int m_TextThickness[LAYER_CLASS_COUNT]
std::vector< int > m_TrackWidthList
int m_LineThickness[LAYER_CLASS_COUNT]
void SetBoardThickness(int aThickness)
ZONE_SETTINGS & GetDefaultZoneSettings()
wxSize m_TextSize[LAYER_CLASS_COUNT]
bool m_TextItalic[LAYER_CLASS_COUNT]
DIM_UNITS_MODE m_DimensionUnitsMode
std::vector< VIA_DIMENSION > m_ViasDimensionsList
Abstract interface for BOARD_ITEMs capable of storing other items inside.
virtual void Delete(BOARD_ITEM *aItem)
Removes an item from the container and deletes it.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:58
virtual void SetLocked(bool aLocked)
Definition: board_item.h:254
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:163
Manage one layer needed to make a physical board.
Definition: board_stackup.h:90
void AddDielectricPrms(int aDielectricPrmsIdx)
Add (insert) a DIELECTRIC_PRMS item to m_DielectricPrmsList all values are set to default.
void SetDielectricLayerId(int aLayerId)
void SetThickness(int aThickness, int aDielectricSubLayer=0)
void SetThicknessLocked(bool aLocked, int aDielectricSubLayer=0)
void SetMaterial(const wxString &aName, int aDielectricSubLayer=0)
void SetLossTangent(double aTg, int aDielectricSubLayer=0)
BOARD_STACKUP_ITEM_TYPE GetType() const
void SetBrdLayerId(PCB_LAYER_ID aBrdLayerId)
void SetTypeName(const wxString &aName)
void SetEpsilonR(double aEpsilon, int aDielectricSubLayer=0)
void SetColor(const wxString &aColorName)
Manage layers needed to make a physical board.
bool m_CastellatedPads
True if castellated pads exist.
bool m_HasDielectricConstrains
True if some layers have impedance controlled tracks or have specific constrains for micro-wave appli...
void Add(BOARD_STACKUP_ITEM *aItem)
Add a new item in stackup layer.
bool m_EdgePlating
True if the edge board is plated.
BS_EDGE_CONNECTOR_CONSTRAINTS m_EdgeConnectorConstraints
If the board has edge connector cards, some constrains can be specified in job file: BS_EDGE_CONNECTO...
wxString m_FinishType
The name of external copper finish.
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:265
void SetPlotOptions(const PCB_PLOT_PARAMS &aOptions)
Definition: board.h:628
bool m_LegacyDesignSettingsLoaded
True if the legacy board design settings were loaded from a file.
Definition: board.h:342
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: board.cpp:696
GAL_SET m_LegacyVisibleItems
Definition: board.h:339
void SetEnabledLayers(LSET aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:531
void SetProperties(const std::map< wxString, wxString > &aProps)
Definition: board.h:333
ZONES & Zones()
Definition: board.h:313
bool SetLayerDescr(PCB_LAYER_ID aIndex, const LAYER &aLayer)
Return the type of the copper layer given by aLayer.
Definition: board.cpp:378
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:1402
FOOTPRINTS & Footprints()
Definition: board.h:307
LSET m_LegacyVisibleLayers
Visibility settings stored in board prior to 6.0, only used for loading legacy files.
Definition: board.h:338
const PCB_LAYER_ID GetLayerID(const wxString &aLayerName) const
Return the ID of a layer.
Definition: board.cpp:390
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition: board.h:625
TRACKS & Tracks()
Definition: board.h:304
bool m_LegacyNetclassesLoaded
True if netclasses were loaded from the file.
Definition: board.h:346
void SetCopperLayerCount(int aCount)
Definition: board.cpp:505
DRAWINGS & Drawings()
Definition: board.h:310
void FinalizeBulkAdd(std::vector< BOARD_ITEM * > &aNewItems)
Must be used if Add() is used using a BULK_x ADD_MODE to generate a change event for listeners.
Definition: board.cpp:793
wxString GroupsSanityCheck(bool repair=false)
Consistency check of internal m_groups structure.
Definition: board.cpp:2035
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:628