KiCad PCB EDA Suite
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
pcb_io_kicad_sexpr_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 The 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 "layer_ids.h"
31#include <cerrno>
32#include <charconv>
33#include <confirm.h>
34#include <macros.h>
35#include <fmt/format.h>
36#include <title_block.h>
37#include <trigo.h>
38
39#include <board.h>
42#include <font/fontconfig.h>
43#include <magic_enum.hpp>
44#include <pcb_dimension.h>
45#include <pcb_shape.h>
46#include <pcb_reference_image.h>
47#include <pcb_group.h>
48#include <pcb_generator.h>
49#include <pcb_target.h>
50#include <pcb_track.h>
51#include <pcb_textbox.h>
52#include <pcb_table.h>
53#include <pad.h>
54#include <generators_mgr.h>
55#include <zone.h>
56#include <footprint.h>
58#include <font/font.h>
59#include <core/ignore.h>
60#include <netclass.h>
63#include <pcb_plot_params.h>
64#include <locale_io.h>
65#include <zones.h>
67#include <convert_basic_shapes_to_polygon.h> // for RECT_CHAMFER_POSITIONS definition
68#include <math/util.h> // KiROUND, Clamp
69#include <string_utils.h>
71#include <wx/log.h>
72#include <progress_reporter.h>
74#include <pgm_base.h>
75
76// For some reason wxWidgets is built with wxUSE_BASE64 unset so expose the wxWidgets
77// base64 code. Needed for PCB_REFERENCE_IMAGE
78#define wxUSE_BASE64 1
79#include <wx/base64.h>
80#include <wx/log.h>
81#include <wx/mstream.h>
82
83// We currently represent board units as integers. Any values that are
84// larger or smaller than those board units represent undefined behavior for
85// the system. We limit values to the largest usable
86// i.e. std::numeric_limits<int>::max().
87// However to avoid issues in comparisons, use a slightly smaller value
88// Note also the usable limits are much smaller to avoid overflows in intermediate
89// calculations.
90constexpr double INT_LIMIT = std::numeric_limits<int>::max() - 10;
91
92using namespace PCB_KEYS_T;
93
94
96{
99 m_tooRecent = false;
101 m_layerIndices.clear();
102 m_layerMasks.clear();
103 m_resetKIIDMap.clear();
104
105 // Add untranslated default (i.e. English) layernames.
106 // Some may be overridden later if parsing a board rather than a footprint.
107 // The English name will survive if parsing only a footprint.
108 for( int layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
109 {
110 std::string untranslated = TO_UTF8( LSET::Name( PCB_LAYER_ID( layer ) ) );
111
112 m_layerIndices[untranslated] = PCB_LAYER_ID( layer );
113 m_layerMasks[untranslated] = LSET( { PCB_LAYER_ID( layer ) } );
114 }
115
116 m_layerMasks[ "*.Cu" ] = LSET::AllCuMask();
117 m_layerMasks[ "*In.Cu" ] = LSET::InternalCuMask();
118 m_layerMasks[ "F&B.Cu" ] = LSET( { F_Cu, B_Cu } );
119 m_layerMasks[ "*.Adhes" ] = LSET( { B_Adhes, F_Adhes } );
120 m_layerMasks[ "*.Paste" ] = LSET( { B_Paste, F_Paste } );
121 m_layerMasks[ "*.Mask" ] = LSET( { B_Mask, F_Mask } );
122 m_layerMasks[ "*.SilkS" ] = LSET( { B_SilkS, F_SilkS } );
123 m_layerMasks[ "*.Fab" ] = LSET( { B_Fab, F_Fab } );
124 m_layerMasks[ "*.CrtYd" ] = LSET( { B_CrtYd, F_CrtYd } );
125
126 // This is for the first pretty & *.kicad_pcb formats, which had
127 // Inner1_Cu - Inner14_Cu with the numbering sequence
128 // reversed from the subsequent format's In1_Cu - In30_Cu numbering scheme.
129 // The newer format brought in an additional 16 Cu layers and flipped the cu stack but
130 // kept the gap between one of the outside layers and the last cu internal.
131
132 for( int i=1; i<=14; ++i )
133 {
134 std::string key = StrPrintf( "Inner%d.Cu", i );
135
136 m_layerMasks[key] = LSET( { PCB_LAYER_ID( In15_Cu - 2 * i ) } );
137 }
138}
139
140
142{
144 {
145 TIME_PT curTime = CLOCK::now();
146 unsigned curLine = reader->LineNumber();
147 auto delta = std::chrono::duration_cast<TIMEOUT>( curTime - m_lastProgressTime );
148
149 if( delta > std::chrono::milliseconds( 250 ) )
150 {
151 m_progressReporter->SetCurrentProgress( ( (double) curLine )
152 / std::max( 1U, m_lineCount ) );
153
155 THROW_IO_ERROR( _( "Open cancelled by user." ) );
156
157 m_lastProgressTime = curTime;
158 }
159 }
160}
161
162
164{
165 int curr_level = 0;
166 T token;
167
168 while( ( token = NextTok() ) != T_EOF )
169 {
170 if( token == T_LEFT )
171 curr_level--;
172
173 if( token == T_RIGHT )
174 {
175 curr_level++;
176
177 if( curr_level > 0 )
178 return;
179 }
180 }
181}
182
183
185{
186 // Add aValue in netcode mapping (m_netCodes) at index aNetCode
187 // ensure there is room in m_netCodes for that, and add room if needed.
188
189 if( (int)m_netCodes.size() <= aIndex )
190 m_netCodes.resize( static_cast<std::size_t>( aIndex ) + 1 );
191
192 m_netCodes[aIndex] = aValue;
193}
194
195
197{
198 // There should be no major rounding issues here, since the values in
199 // the file are in mm and get converted to nano-meters.
200 // See test program tools/test-nm-biu-to-ascii-mm-round-tripping.cpp
201 // to confirm or experiment. Use a similar strategy in both places, here
202 // and in the test program. Make that program with:
203 // $ make test-nm-biu-to-ascii-mm-round-tripping
204 auto retval = parseDouble() * pcbIUScale.IU_PER_MM;
205
206 // N.B. we currently represent board units as integers. Any values that are
207 // larger or smaller than those board units represent undefined behavior for
208 // the system. We limit values to the largest that is visible on the screen
209 return KiROUND( std::clamp( retval, -INT_LIMIT, INT_LIMIT ) );
210}
211
212
214{
215 auto retval = parseDouble( aExpected ) * pcbIUScale.IU_PER_MM;
216
217 // N.B. we currently represent board units as integers. Any values that are
218 // larger or smaller than those board units represent undefined behavior for
219 // the system. We limit values to the largest that is visible on the screen
220 return KiROUND( std::clamp( retval, -INT_LIMIT, INT_LIMIT ) );
221}
222
223
225{
226 T token = NextTok();
227
228 if( token == T_yes )
229 return true;
230 else if( token == T_no )
231 return false;
232 else
233 Expecting( "yes or no" );
234
235 return false;
236}
237
238
240{
241 T token = NextTok();
242
243 if( token == T_yes )
244 return true;
245 else if( token == T_no )
246 return false;
247 else if( token == T_none )
248 return std::nullopt;
249 else
250 Expecting( "yes, no or none" );
251
252 return false;
253}
254
255
256/*
257 * e.g. "hide", "hide)", "(hide yes)"
258 */
260{
261 bool ret = aDefaultValue;
262
263 if( PrevTok() == T_LEFT )
264 {
265 T token = NextTok();
266
267 // "hide)"
268 if( static_cast<int>( token ) == DSN_RIGHT )
269 return aDefaultValue;
270
271 if( token == T_yes || token == T_true )
272 ret = true;
273 else if( token == T_no || token == T_false )
274 ret = false;
275 else
276 Expecting( "yes or no" );
277
278 NeedRIGHT();
279 }
280 else
281 {
282 // "hide"
283 return aDefaultValue;
284 }
285
286 return ret;
287}
288
289
291{
292 int year, month, day;
293
294 year = m_requiredVersion / 10000;
295 month = ( m_requiredVersion / 100 ) - ( year * 100 );
296 day = m_requiredVersion - ( year * 10000 ) - ( month * 100 );
297
298 // wx throws an assertion, not a catchable exception, when the date is invalid.
299 // User input shouldn't give wx asserts, so check manually and throw a proper
300 // error instead
301 if( day <= 0 || month <= 0 || month > 12 ||
302 day > wxDateTime::GetNumberOfDays( (wxDateTime::Month)( month - 1 ), year ) )
303 {
304 wxString err;
305 err.Printf( _( "Cannot interpret date code %d" ), m_requiredVersion );
306 THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
307 }
308
309 wxDateTime date( day, (wxDateTime::Month)( month - 1 ), year, 0, 0, 0, 0 );
310 return date.FormatDate();
311}
312
313
315{
316 if( CurTok() != T_LEFT )
317 NeedLEFT();
318
319 VECTOR2I pt;
320 T token = NextTok();
321
322 if( token != T_xy )
323 Expecting( T_xy );
324
325 pt.x = parseBoardUnits( "X coordinate" );
326 pt.y = parseBoardUnits( "Y coordinate" );
327
328 NeedRIGHT();
329
330 return pt;
331}
332
333
335{
336 if( CurTok() != T_LEFT )
337 NeedLEFT();
338
339 T token = NextTok();
340
341 switch( token )
342 {
343 case T_xy:
344 {
345 int x = parseBoardUnits( "X coordinate" );
346 int y = parseBoardUnits( "Y coordinate" );
347
348 NeedRIGHT();
349
350 aPoly.Append( x, y );
351 break;
352 }
353 case T_arc:
354 {
355 bool has_start = false;
356 bool has_mid = false;
357 bool has_end = false;
358
359 VECTOR2I arc_start, arc_mid, arc_end;
360
361 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
362 {
363 if( token != T_LEFT )
364 Expecting( T_LEFT );
365
366 token = NextTok();
367
368 switch( token )
369 {
370 case T_start:
371 arc_start.x = parseBoardUnits( "start x" );
372 arc_start.y = parseBoardUnits( "start y" );
373 has_start = true;
374 break;
375
376 case T_mid:
377 arc_mid.x = parseBoardUnits( "mid x" );
378 arc_mid.y = parseBoardUnits( "mid y" );
379 has_mid = true;
380 break;
381
382 case T_end:
383 arc_end.x = parseBoardUnits( "end x" );
384 arc_end.y = parseBoardUnits( "end y" );
385 has_end = true;
386 break;
387
388 default:
389 Expecting( "start, mid or end" );
390 }
391
392 NeedRIGHT();
393 }
394
395 if( !has_start )
396 Expecting( "start" );
397
398 if( !has_mid )
399 Expecting( "mid" );
400
401 if( !has_end )
402 Expecting( "end" );
403
404 SHAPE_ARC arc( arc_start, arc_mid, arc_end, 0 );
405
406 aPoly.Append( arc );
407
408 if( token != T_RIGHT )
409 Expecting( T_RIGHT );
410
411 break;
412 }
413 default:
414 Expecting( "xy or arc" );
415 }
416}
417
418
420{
421 VECTOR2I pt = parseXY();
422
423 if( aX )
424 *aX = pt.x;
425
426 if( aY )
427 *aY = pt.y;
428}
429
430
431void PCB_IO_KICAD_SEXPR_PARSER::parseMargins( int& aLeft, int& aTop, int& aRight, int& aBottom )
432{
433 aLeft = parseBoardUnits( "left margin" );
434 aTop = parseBoardUnits( "top margin" );
435 aRight = parseBoardUnits( "right margin" );
436 aBottom = parseBoardUnits( "bottom margin" );
437}
438
439
441{
442 wxString pName;
443 wxString pValue;
444
445 NeedSYMBOL();
446 pName = FromUTF8();
447 NeedSYMBOL();
448 pValue = FromUTF8();
449 NeedRIGHT();
450
451 return { pName, pValue };
452}
453
454
456{
457 tdParams->m_Enabled = false;
458 tdParams->m_AllowUseTwoTracks = false;
459 tdParams->m_TdOnPadsInZones = true;
460
461 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
462 {
463 if( token == T_LEFT )
464 token = NextTok();
465
466 switch( token )
467 {
468 case T_enabled:
469 tdParams->m_Enabled = parseMaybeAbsentBool( true );
470 break;
471
472 case T_allow_two_segments:
473 tdParams->m_AllowUseTwoTracks = parseMaybeAbsentBool( true );
474 break;
475
476 case T_prefer_zone_connections:
477 tdParams->m_TdOnPadsInZones = !parseMaybeAbsentBool( false );
478 break;
479
480 case T_best_length_ratio:
481 tdParams->m_BestLengthRatio = parseDouble( "teardrop best length ratio" );
482 NeedRIGHT();
483 break;
484
485 case T_max_length:
486 tdParams->m_TdMaxLen = parseBoardUnits( "teardrop max length" );
487 NeedRIGHT();
488 break;
489
490 case T_best_width_ratio:
491 tdParams->m_BestWidthRatio = parseDouble( "teardrop best width ratio" );
492 NeedRIGHT();
493 break;
494
495 case T_max_width:
496 tdParams->m_TdMaxWidth = parseBoardUnits( "teardrop max width" );
497 NeedRIGHT();
498 break;
499
500 // Legacy token
501 case T_curve_points:
502 tdParams->m_CurvedEdges = parseInt( "teardrop curve points count" ) > 0;
503 NeedRIGHT();
504 break;
505
506 case T_curved_edges:
507 tdParams->m_CurvedEdges = parseMaybeAbsentBool( true );
508 break;
509
510 case T_filter_ratio:
511 tdParams->m_WidthtoSizeFilterRatio = parseDouble( "teardrop filter ratio" );
512 NeedRIGHT();
513 break;
514
515 default:
516 Expecting( "enabled, allow_two_segments, prefer_zone_connections, best_length_ratio, "
517 "max_length, best_width_ratio, max_width, curve_points or filter_ratio" );
518 }
519 }
520}
521
522
524{
525 wxCHECK_RET( CurTok() == T_effects,
526 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as EDA_TEXT." ) );
527
528 // These are not written out if center/center and/or no mirror,
529 // so we have to make sure we start that way.
530 // (these parameters will be set in T_justify section, when existing)
533 aText->SetMirrored( false );
534
535 // In version 20210606 the notation for overbars was changed from `~...~` to `~{...}`.
536 // We need to convert the old syntax to the new one.
537 if( m_requiredVersion < 20210606 )
538 aText->SetText( ConvertToNewOverbarNotation( aText->GetText() ) );
539
540 T token;
541
542 // Prior to v5.0 text size was omitted from file format if equal to 60mils
543 // Now, it is always explicitly written to file
544 bool foundTextSize = false;
545
546 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
547 {
548 if( token == T_LEFT )
549 token = NextTok();
550
551 switch( token )
552 {
553 case T_font:
554 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
555 {
556 if( token == T_LEFT )
557 continue;
558
559 switch( token )
560 {
561 case T_face:
562 NeedSYMBOL();
563 aText->SetUnresolvedFontName( FromUTF8() );
564 NeedRIGHT();
565 break;
566
567 case T_size:
568 {
569 VECTOR2I sz;
570 sz.y = parseBoardUnits( "text height" );
571 sz.x = parseBoardUnits( "text width" );
572 aText->SetTextSize( sz );
573 NeedRIGHT();
574
575 foundTextSize = true;
576 break;
577 }
578
579 case T_line_spacing:
580 aText->SetLineSpacing( parseDouble( "line spacing" ) );
581 NeedRIGHT();
582 break;
583
584 case T_thickness:
585 aText->SetTextThickness( parseBoardUnits( "text thickness" ) );
586 NeedRIGHT();
587 break;
588
589 case T_bold:
590 {
591 bool value = parseMaybeAbsentBool( true );
592 aText->SetBoldFlag( value );
593 }
594 break;
595
596 case T_italic:
597 {
598 bool value = parseMaybeAbsentBool( true );
599 aText->SetItalicFlag( value );
600 }
601 break;
602
603 default:
604 Expecting( "face, size, line_spacing, thickness, bold, or italic" );
605 }
606 }
607
608 break;
609
610 case T_justify:
611 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
612 {
613 if( token == T_LEFT )
614 continue;
615
616 switch( token )
617 {
618 case T_left:
620 break;
621
622 case T_right:
624 break;
625
626 case T_top:
628 break;
629
630 case T_bottom:
632 break;
633
634 case T_mirror:
635 aText->SetMirrored( true );
636 break;
637
638 default:
639 Expecting( "left, right, top, bottom, or mirror" );
640 }
641
642 }
643
644 break;
645
646 case T_hide:
647 {
648 // In older files, the hide token appears bare, and indicates hide==true.
649 // In newer files, it will be an explicit bool in a list like (hide yes)
650 bool hide = parseMaybeAbsentBool( true );
651 aText->SetVisible( !hide );
652 break;
653 }
654
655 default:
656 Expecting( "font, justify, or hide" );
657 }
658 }
659
660 // Text size was not specified in file, force legacy default units
661 // 60mils is 1.524mm
662 if( !foundTextSize )
663 {
664 const double defaultTextSize = 1.524 * pcbIUScale.IU_PER_MM;
665
666 aText->SetTextSize( VECTOR2I( defaultTextSize, defaultTextSize ) );
667 }
668}
669
670
672{
673 T token;
674
675 NeedSYMBOLorNUMBER();
676 wxString cacheText = From_UTF8( CurText() );
677 EDA_ANGLE cacheAngle( parseDouble( "render cache angle" ), DEGREES_T );
678
679 text->SetupRenderCache( cacheText, text->GetFont(), cacheAngle, { 0, 0 } );
680
681 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
682 {
683 if( token != T_LEFT )
684 Expecting( T_LEFT );
685
686 token = NextTok();
687
688 if( token != T_polygon )
689 Expecting( T_polygon );
690
691 SHAPE_POLY_SET poly;
692
693 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
694 {
695 if( token != T_LEFT )
696 Expecting( T_LEFT );
697
698 token = NextTok();
699
700 if( token != T_pts )
701 Expecting( T_pts );
702
703 SHAPE_LINE_CHAIN lineChain;
704
705 while( (token = NextTok() ) != T_RIGHT )
706 parseOutlinePoints( lineChain );
707
708 lineChain.SetClosed( true );
709
710 if( poly.OutlineCount() == 0 )
711 poly.AddOutline( lineChain );
712 else
713 poly.AddHole( lineChain );
714 }
715
716 text->AddRenderCacheGlyph( poly );
717 }
718}
719
720
722{
723 wxCHECK_MSG( CurTok() == T_model, nullptr,
724 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as FP_3DMODEL." ) );
725
726 T token;
727
728 FP_3DMODEL* n3D = new FP_3DMODEL;
729 NeedSYMBOLorNUMBER();
730 n3D->m_Filename = FromUTF8();
731
732 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
733 {
734 if( token == T_LEFT )
735 token = NextTok();
736
737 switch( token )
738 {
739 case T_at:
740 NeedLEFT();
741 token = NextTok();
742
743 if( token != T_xyz )
744 Expecting( T_xyz );
745
746 /* Note:
747 * Prior to KiCad v5, model offset was designated by "at",
748 * and the units were in inches.
749 * Now we use mm, but support reading of legacy files
750 */
751
752 n3D->m_Offset.x = parseDouble( "x value" ) * 25.4f;
753 n3D->m_Offset.y = parseDouble( "y value" ) * 25.4f;
754 n3D->m_Offset.z = parseDouble( "z value" ) * 25.4f;
755
756 NeedRIGHT(); // xyz
757 NeedRIGHT(); // at
758 break;
759
760 case T_hide:
761 {
762 // In older files, the hide token appears bare, and indicates hide==true.
763 // In newer files, it will be an explicit bool in a list like (hide yes)
764 bool hide = parseMaybeAbsentBool( true );
765 n3D->m_Show = !hide;
766 break;
767 }
768
769 case T_opacity:
770 n3D->m_Opacity = parseDouble( "opacity value" );
771 NeedRIGHT();
772 break;
773
774 case T_offset:
775 NeedLEFT();
776 token = NextTok();
777
778 if( token != T_xyz )
779 Expecting( T_xyz );
780
781 /*
782 * 3D model offset is in mm
783 */
784 n3D->m_Offset.x = parseDouble( "x value" );
785 n3D->m_Offset.y = parseDouble( "y value" );
786 n3D->m_Offset.z = parseDouble( "z value" );
787
788 NeedRIGHT(); // xyz
789 NeedRIGHT(); // offset
790 break;
791
792 case T_scale:
793 NeedLEFT();
794 token = NextTok();
795
796 if( token != T_xyz )
797 Expecting( T_xyz );
798
799 n3D->m_Scale.x = parseDouble( "x value" );
800 n3D->m_Scale.y = parseDouble( "y value" );
801 n3D->m_Scale.z = parseDouble( "z value" );
802
803 NeedRIGHT(); // xyz
804 NeedRIGHT(); // scale
805 break;
806
807 case T_rotate:
808 NeedLEFT();
809 token = NextTok();
810
811 if( token != T_xyz )
812 Expecting( T_xyz );
813
814 n3D->m_Rotation.x = parseDouble( "x value" );
815 n3D->m_Rotation.y = parseDouble( "y value" );
816 n3D->m_Rotation.z = parseDouble( "z value" );
817
818 NeedRIGHT(); // xyz
819 NeedRIGHT(); // rotate
820 break;
821
822 default:
823 Expecting( "at, hide, opacity, offset, scale, or rotate" );
824 }
825
826 }
827
828 return n3D;
829}
830
831
833{
834 LOCALE_IO toggle;
835
836 m_groupInfos.clear();
837
838 // See Parse() - FOOTPRINTS can be prefixed with an initial block of single line comments,
839 // eventually BOARD might be the same
840 ReadCommentLines();
841
842 if( CurTok() != T_LEFT )
843 return false;
844
845 if( NextTok() != T_kicad_pcb)
846 return false;
847
848 return true;
849}
850
851
853{
854 T token;
855 BOARD_ITEM* item;
856 LOCALE_IO toggle;
857
858 m_groupInfos.clear();
859
860 // FOOTPRINTS can be prefixed with an initial block of single line comments and these are
861 // kept for Format() so they round trip in s-expression form. BOARDs might eventually do
862 // the same, but currently do not.
863 std::unique_ptr<wxArrayString> initial_comments( ReadCommentLines() );
864
865 token = CurTok();
866
867 if( token == -1 ) // EOF
868 Unexpected( token );
869
870 if( token != T_LEFT )
871 Expecting( T_LEFT );
872
873 switch( NextTok() )
874 {
875 case T_kicad_pcb:
876 if( m_board == nullptr )
877 m_board = new BOARD();
878
879 item = (BOARD_ITEM*) parseBOARD();
880 break;
881
882 case T_module: // legacy token
883 case T_footprint:
884 item = (BOARD_ITEM*) parseFOOTPRINT( initial_comments.release() );
885
886 // Locking a footprint has no meaning outside of a board.
887 item->SetLocked( false );
888 break;
889
890 default:
891 wxString err;
892 err.Printf( _( "Unknown token '%s'" ), FromUTF8() );
893 THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
894 }
895
896 const std::vector<wxString>* embeddedFonts = item->GetEmbeddedFiles()->UpdateFontFiles();
897
898 item->RunOnChildren(
899 [&]( BOARD_ITEM* aChild )
900 {
901 if( EDA_TEXT* textItem = dynamic_cast<EDA_TEXT*>( aChild ) )
902 textItem->ResolveFont( embeddedFonts );
903 },
904 RECURSE_MODE::RECURSE );
905
906 resolveGroups( item );
907
908 return item;
909}
910
911
913{
914 try
915 {
916 return parseBOARD_unchecked();
917 }
918 catch( const PARSE_ERROR& parse_error )
919 {
920 if( m_tooRecent )
921 throw FUTURE_FORMAT_ERROR( parse_error, GetRequiredVersion() );
922 else
923 throw;
924 }
925}
926
927
929{
930 T token;
931 std::map<wxString, wxString> properties;
932
933 parseHeader();
934
935 auto checkVersion =
936 [&]()
937 {
939 {
940 throw FUTURE_FORMAT_ERROR( fmt::format( "{}", m_requiredVersion ),
942 }
943 };
944
945 std::vector<BOARD_ITEM*> bulkAddedItems;
946 BOARD_ITEM* item = nullptr;
947
948 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
949 {
950 checkpoint();
951
952 if( token != T_LEFT )
953 Expecting( T_LEFT );
954
955 token = NextTok();
956
957 if( token == T_page && m_requiredVersion <= 20200119 )
958 token = T_paper;
959
960 switch( token )
961 {
962 case T_host: // legacy token
963 NeedSYMBOL();
964 m_board->SetGenerator( FromUTF8() );
965
966 // Older formats included build data
968 NeedSYMBOL();
969
970 NeedRIGHT();
971 break;
972
973 case T_generator:
974 NeedSYMBOL();
975 m_board->SetGenerator( FromUTF8() );
976 NeedRIGHT();
977 break;
978
979 case T_generator_version:
980 {
981 NeedSYMBOL();
982 m_generatorVersion = FromUTF8();
983 NeedRIGHT();
984
985 // If the format includes a generator version, by this point we have enough info to
986 // do the version check here
987 checkVersion();
988
989 break;
990 }
991
992 case T_general:
993 // Do another version check here, for older files that do not include generator_version
994 checkVersion();
995
997 break;
998
999 case T_paper:
1001 break;
1002
1003 case T_title_block:
1005 break;
1006
1007 case T_layers:
1008 parseLayers();
1009 break;
1010
1011 case T_setup:
1012 parseSetup();
1013 break;
1014
1015 case T_property:
1016 properties.insert( parseBoardProperty() );
1017 break;
1018
1019 case T_net:
1021 break;
1022
1023 case T_net_class:
1024 parseNETCLASS();
1026 break;
1027
1028 case T_gr_arc:
1029 case T_gr_curve:
1030 case T_gr_line:
1031 case T_gr_poly:
1032 case T_gr_circle:
1033 case T_gr_rect:
1034 item = parsePCB_SHAPE( m_board );
1035 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
1036 bulkAddedItems.push_back( item );
1037 break;
1038
1039 case T_image:
1041 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
1042 bulkAddedItems.push_back( item );
1043 break;
1044
1045 case T_gr_text:
1046 item = parsePCB_TEXT( m_board );
1047 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
1048 bulkAddedItems.push_back( item );
1049 break;
1050
1051 case T_gr_text_box:
1052 item = parsePCB_TEXTBOX( m_board );
1053 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
1054 bulkAddedItems.push_back( item );
1055 break;
1056
1057 case T_table:
1058 item = parsePCB_TABLE( m_board );
1059 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
1060 bulkAddedItems.push_back( item );
1061 break;
1062
1063 case T_dimension:
1064 item = parseDIMENSION( m_board );
1065 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
1066 bulkAddedItems.push_back( item );
1067 break;
1068
1069 case T_module: // legacy token
1070 case T_footprint:
1071 item = parseFOOTPRINT();
1072 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
1073 bulkAddedItems.push_back( item );
1074 break;
1075
1076 case T_segment:
1077 item = parsePCB_TRACK();
1078 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
1079 bulkAddedItems.push_back( item );
1080 break;
1081
1082 case T_arc:
1083 item = parseARC();
1084 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
1085 bulkAddedItems.push_back( item );
1086 break;
1087
1088 case T_group:
1090 break;
1091
1092 case T_generated:
1094 break;
1095
1096 case T_via:
1097 item = parsePCB_VIA();
1098 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
1099 bulkAddedItems.push_back( item );
1100 break;
1101
1102 case T_zone:
1103 item = parseZONE( m_board );
1104 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
1105 bulkAddedItems.push_back( item );
1106 break;
1107
1108 case T_target:
1109 item = parsePCB_TARGET();
1110 m_board->Add( item, ADD_MODE::BULK_APPEND, true );
1111 bulkAddedItems.push_back( item );
1112 break;
1113
1114 case T_embedded_fonts:
1115 {
1117 NeedRIGHT();
1118 break;
1119 }
1120
1121 case T_embedded_files:
1122 {
1123 EMBEDDED_FILES_PARSER embeddedFilesParser( reader );
1124 embeddedFilesParser.SyncLineReaderWith( *this );
1125
1126 try
1127 {
1128 embeddedFilesParser.ParseEmbedded( m_board->GetEmbeddedFiles() );
1129 }
1130 catch( const PARSE_ERROR& e )
1131 {
1132 wxLogError( e.What() );
1133 }
1134
1135 SyncLineReaderWith( embeddedFilesParser );
1136 break;
1137 }
1138
1139 default:
1140 wxString err;
1141 err.Printf( _( "Unknown token '%s'" ), FromUTF8() );
1142 THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
1143 }
1144 }
1145
1146 if( bulkAddedItems.size() > 0 )
1147 m_board->FinalizeBulkAdd( bulkAddedItems );
1148
1149 m_board->SetProperties( properties );
1150
1151 if( m_undefinedLayers.size() > 0 )
1152 {
1153 PCB_LAYER_ID destLayer = Cmts_User;
1154 wxString msg, undefinedLayerNames, destLayerName;
1155
1156 for( const wxString& layerName : m_undefinedLayers )
1157 {
1158 if( !undefinedLayerNames.IsEmpty() )
1159 undefinedLayerNames += wxT( ", " );
1160
1161 undefinedLayerNames += layerName;
1162 }
1163
1164 destLayerName = m_board->GetLayerName( destLayer );
1165
1166 if( Pgm().IsGUI() && m_queryUserCallback )
1167 {
1168 msg.Printf( _( "Items found on undefined layers (%s).\n"
1169 "Do you wish to rescue them to the %s layer?\n"
1170 "\n"
1171 "Zones will need to be refilled." ),
1172 undefinedLayerNames, destLayerName );
1173
1174 if( !m_queryUserCallback( _( "Undefined Layers Warning" ), wxICON_WARNING, msg,
1175 _( "Rescue" ) ) )
1176 {
1177 THROW_IO_ERROR( wxT( "CANCEL" ) );
1178 }
1179
1180 // Make sure the destination layer is enabled, even if not in the file
1182
1183 const auto visitItem = [&]( BOARD_ITEM& curr_item )
1184 {
1185 LSET layers = curr_item.GetLayerSet();
1186
1187 if( layers.test( Rescue ) )
1188 {
1189 layers.set( destLayer );
1190 layers.reset( Rescue );
1191 }
1192
1193 curr_item.SetLayerSet( layers );
1194 };
1195
1196 for( PCB_TRACK* track : m_board->Tracks() )
1197 {
1198 if( track->Type() == PCB_VIA_T )
1199 {
1200 PCB_VIA* via = static_cast<PCB_VIA*>( track );
1201 PCB_LAYER_ID top_layer, bottom_layer;
1202
1203 if( via->GetViaType() == VIATYPE::THROUGH )
1204 continue;
1205
1206 via->LayerPair( &top_layer, &bottom_layer );
1207
1208 if( top_layer == Rescue || bottom_layer == Rescue )
1209 {
1210 if( top_layer == Rescue )
1211 top_layer = F_Cu;
1212
1213 if( bottom_layer == Rescue )
1214 bottom_layer = B_Cu;
1215
1216 via->SetLayerPair( top_layer, bottom_layer );
1217 }
1218 }
1219 else
1220 {
1221 visitItem( *track );
1222 }
1223 }
1224
1225 for( BOARD_ITEM* zone : m_board->Zones() )
1226 visitItem( *zone );
1227
1228 for( BOARD_ITEM* drawing : m_board->Drawings() )
1229 visitItem( *drawing );
1230
1231 for( FOOTPRINT* fp : m_board->Footprints() )
1232 {
1233 for( BOARD_ITEM* drawing : fp->GraphicalItems() )
1234 visitItem( *drawing );
1235
1236 for( BOARD_ITEM* zone : fp->Zones() )
1237 visitItem( *zone );
1238
1239 for( PCB_FIELD* field : fp->GetFields() )
1240 visitItem( *field );
1241 }
1242
1243 m_undefinedLayers.clear();
1244 }
1245 else
1246 {
1247 THROW_IO_ERROR( wxT( "One or more undefined undefinedLayerNames was found; "
1248 "open the board in the PCB Editor to resolve." ) );
1249 }
1250 }
1251
1252 // Clear unused zone data
1253 {
1254 LSET layers = m_board->GetEnabledLayers();
1255
1256 for( BOARD_ITEM* zone : m_board->Zones() )
1257 {
1258 ZONE* z = static_cast<ZONE*>( zone );
1259
1260 z->SetLayerSetAndRemoveUnusedFills( z->GetLayerSet() & layers );
1261 }
1262 }
1263
1264 // Ensure all footprints have their embedded data from the board
1266
1267 return m_board;
1268}
1269
1270
1272{
1273 auto getItem =
1274 [&]( const KIID& aId )
1275 {
1276 BOARD_ITEM* aItem = nullptr;
1277
1278 if( BOARD* board = dynamic_cast<BOARD*>( aParent ) )
1279 {
1280 aItem = board->GetItem( aId );
1281 }
1282 else if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( aParent ) )
1283 {
1284 footprint->RunOnChildren(
1285 [&]( BOARD_ITEM* child )
1286 {
1287 if( child->m_Uuid == aId )
1288 aItem = child;
1289 },
1290 RECURSE_MODE::NO_RECURSE );
1291 }
1292
1293 return aItem;
1294 };
1295
1296 // Now that we've parsed the other Uuids in the file we can resolve the uuids referred
1297 // to in the group declarations we saw.
1298 //
1299 // First add all group objects so subsequent GetItem() calls for nested groups work.
1300
1301 std::vector<const GROUP_INFO*> groupTypeObjects;
1302
1303 for( const GROUP_INFO& groupInfo : m_groupInfos )
1304 groupTypeObjects.emplace_back( &groupInfo );
1305
1306 for( const GENERATOR_INFO& genInfo : m_generatorInfos )
1307 groupTypeObjects.emplace_back( &genInfo );
1308
1309 for( const GROUP_INFO* groupInfo : groupTypeObjects )
1310 {
1311 PCB_GROUP* group = nullptr;
1312
1313 if( const GENERATOR_INFO* genInfo = dynamic_cast<const GENERATOR_INFO*>( groupInfo ) )
1314 {
1316
1317 PCB_GENERATOR* gen;
1318 group = gen = mgr.CreateFromType( genInfo->genType );
1319
1320 if( !gen )
1321 {
1322 THROW_IO_ERROR( wxString::Format(
1323 _( "Cannot create generated object of type '%s'" ), genInfo->genType ) );
1324 }
1325
1326 gen->SetLayer( genInfo->layer );
1327 gen->SetProperties( genInfo->properties );
1328 }
1329 else
1330 {
1331 group = new PCB_GROUP( groupInfo->parent );
1332 group->SetName( groupInfo->name );
1333 }
1334
1335 const_cast<KIID&>( group->m_Uuid ) = groupInfo->uuid;
1336
1337 if( groupInfo->locked )
1338 group->SetLocked( true );
1339
1340 if( groupInfo->parent->Type() == PCB_FOOTPRINT_T )
1341 static_cast<FOOTPRINT*>( groupInfo->parent )->Add( group, ADD_MODE::INSERT, true );
1342 else
1343 static_cast<BOARD*>( groupInfo->parent )->Add( group, ADD_MODE::INSERT, true );
1344 }
1345
1346 wxString error;
1347
1348 for( const GROUP_INFO* groupInfo : groupTypeObjects )
1349 {
1350 if( PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( getItem( groupInfo->uuid ) ) )
1351 {
1352 for( const KIID& aUuid : groupInfo->memberUuids )
1353 {
1354 BOARD_ITEM* item = nullptr;
1355
1356 if( m_appendToExisting )
1357 item = getItem( m_resetKIIDMap[ aUuid.AsString() ] );
1358 else
1359 item = getItem( aUuid );
1360
1361 if( !item || item->Type() == NOT_USED )
1362 {
1363 // This is the deleted item singleton, which means we didn't find the uuid.
1364 continue;
1365 }
1366
1367 // We used to allow fp items in non-footprint groups. It was a mistake. Check
1368 // to make sure they the item and group are owned by the same parent (will both
1369 // be nullptr in the board case).
1370 if( item->GetParentFootprint() == group->GetParentFootprint() )
1371 group->AddItem( item );
1372 }
1373 }
1374 }
1375
1376 // Don't allow group cycles
1377 if( m_board )
1378 m_board->GroupsSanityCheck( true );
1379}
1380
1381
1383{
1384 wxCHECK_RET( CurTok() == T_kicad_pcb,
1385 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a header." ) );
1386
1387 NeedLEFT();
1388
1389 T tok = NextTok();
1390
1391 if( tok == T_version )
1392 {
1393 m_requiredVersion = parseInt( FromUTF8().mb_str( wxConvUTF8 ) );
1394 NeedRIGHT();
1395 }
1396 else
1397 {
1398 m_requiredVersion = 20201115; // Last version before we started writing version #s
1399 // in footprint files as well as board files.
1400 }
1401
1403
1405}
1406
1407
1409{
1410 wxCHECK_RET( CurTok() == T_general,
1411 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
1412 wxT( " as a general section." ) );
1413
1414 T token;
1415
1416 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1417 {
1418 if( token != T_LEFT )
1419 Expecting( T_LEFT );
1420
1421 token = NextTok();
1422
1423 switch( token )
1424 {
1425 case T_thickness:
1427 NeedRIGHT();
1428 break;
1429
1430 case T_legacy_teardrops:
1432 break;
1433
1434 default: // Skip everything else.
1435 while( ( token = NextTok() ) != T_RIGHT )
1436 {
1437 if( !IsSymbol( token ) && token != T_NUMBER )
1438 Expecting( "symbol or number" );
1439 }
1440 }
1441 }
1442}
1443
1444
1446{
1447 wxCHECK_RET( ( CurTok() == T_page && m_requiredVersion <= 20200119 ) || CurTok() == T_paper,
1448 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a PAGE_INFO." ) );
1449
1450 T token;
1451 PAGE_INFO pageInfo;
1452
1453 NeedSYMBOL();
1454
1455 wxString pageType = FromUTF8();
1456
1457 if( !pageInfo.SetType( pageType ) )
1458 {
1459 wxString err;
1460 err.Printf( _( "Page type '%s' is not valid." ), FromUTF8() );
1461 THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
1462 }
1463
1464 if( pageType == PAGE_INFO::Custom )
1465 {
1466 double width = parseDouble( "width" ); // width in mm
1467
1468 // Perform some controls to avoid crashes if the size is edited by hands
1469 if( width < MIN_PAGE_SIZE_MM )
1470 width = MIN_PAGE_SIZE_MM;
1471 else if( width > MAX_PAGE_SIZE_PCBNEW_MM )
1473
1474 double height = parseDouble( "height" ); // height in mm
1475
1476 if( height < MIN_PAGE_SIZE_MM )
1477 height = MIN_PAGE_SIZE_MM;
1478 else if( height > MAX_PAGE_SIZE_PCBNEW_MM )
1479 height = MAX_PAGE_SIZE_PCBNEW_MM;
1480
1481 pageInfo.SetWidthMM( width );
1482 pageInfo.SetHeightMM( height );
1483 }
1484
1485 token = NextTok();
1486
1487 if( token == T_portrait )
1488 {
1489 pageInfo.SetPortrait( true );
1490 NeedRIGHT();
1491 }
1492 else if( token != T_RIGHT )
1493 {
1494 Expecting( "portrait|)" );
1495 }
1496
1497 m_board->SetPageSettings( pageInfo );
1498}
1499
1500
1502{
1503 wxCHECK_RET( CurTok() == T_title_block,
1504 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as TITLE_BLOCK." ) );
1505
1506 T token;
1507 TITLE_BLOCK titleBlock;
1508
1509 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1510 {
1511 if( token != T_LEFT )
1512 Expecting( T_LEFT );
1513
1514 token = NextTok();
1515
1516 switch( token )
1517 {
1518 case T_title:
1519 NextTok();
1520 titleBlock.SetTitle( FromUTF8() );
1521 break;
1522
1523 case T_date:
1524 NextTok();
1525 titleBlock.SetDate( FromUTF8() );
1526 break;
1527
1528 case T_rev:
1529 NextTok();
1530 titleBlock.SetRevision( FromUTF8() );
1531 break;
1532
1533 case T_company:
1534 NextTok();
1535 titleBlock.SetCompany( FromUTF8() );
1536 break;
1537
1538 case T_comment:
1539 {
1540 int commentNumber = parseInt( "comment" );
1541
1542 switch( commentNumber )
1543 {
1544 case 1:
1545 NextTok();
1546 titleBlock.SetComment( 0, FromUTF8() );
1547 break;
1548
1549 case 2:
1550 NextTok();
1551 titleBlock.SetComment( 1, FromUTF8() );
1552 break;
1553
1554 case 3:
1555 NextTok();
1556 titleBlock.SetComment( 2, FromUTF8() );
1557 break;
1558
1559 case 4:
1560 NextTok();
1561 titleBlock.SetComment( 3, FromUTF8() );
1562 break;
1563
1564 case 5:
1565 NextTok();
1566 titleBlock.SetComment( 4, FromUTF8() );
1567 break;
1568
1569 case 6:
1570 NextTok();
1571 titleBlock.SetComment( 5, FromUTF8() );
1572 break;
1573
1574 case 7:
1575 NextTok();
1576 titleBlock.SetComment( 6, FromUTF8() );
1577 break;
1578
1579 case 8:
1580 NextTok();
1581 titleBlock.SetComment( 7, FromUTF8() );
1582 break;
1583
1584 case 9:
1585 NextTok();
1586 titleBlock.SetComment( 8, FromUTF8() );
1587 break;
1588
1589 default:
1590 wxString err;
1591 err.Printf( wxT( "%d is not a valid title block comment number" ), commentNumber );
1592 THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
1593 }
1594
1595 break;
1596 }
1597
1598 default:
1599 Expecting( "title, date, rev, company, or comment" );
1600 }
1601
1602 NeedRIGHT();
1603 }
1604
1605 m_board->SetTitleBlock( titleBlock );
1606}
1607
1608
1610{
1611 T token;
1612
1613 std::string name;
1614 std::string userName;
1615 std::string type;
1616 bool isVisible = true;
1617
1618 aLayer->clear();
1619
1620 if( CurTok() != T_LEFT )
1621 Expecting( T_LEFT );
1622
1623 // this layer_num is not used, we DO depend on LAYER_T however.
1624 int layer_num = parseInt( "layer index" );
1625
1626 NeedSYMBOLorNUMBER();
1627 name = CurText();
1628
1629 NeedSYMBOL();
1630 type = CurText();
1631
1632 token = NextTok();
1633
1634 // @todo Figure out why we are looking for a hide token in the layer definition.
1635 if( token == T_hide )
1636 {
1637 isVisible = false;
1638 NeedRIGHT();
1639 }
1640 else if( token == T_STRING )
1641 {
1642 userName = CurText();
1643 NeedRIGHT();
1644 }
1645 else if( token != T_RIGHT )
1646 {
1647 Expecting( "hide, user defined name, or )" );
1648 }
1649
1650 aLayer->m_type = LAYER::ParseType( type.c_str() );
1651 aLayer->m_number = layer_num;
1652 aLayer->m_visible = isVisible;
1653
1654 if( m_requiredVersion >= 20200922 )
1655 {
1656 aLayer->m_userName = From_UTF8( userName.c_str() );
1657 aLayer->m_name = From_UTF8( name.c_str() );
1658 }
1659 else // Older versions didn't have a dedicated user name field
1660 {
1661 aLayer->m_name = aLayer->m_userName = From_UTF8( name.c_str() );
1662 }
1663}
1664
1665
1667{
1668 T token;
1669 wxString name;
1670 int dielectric_idx = 1; // the index of dielectric layers
1672
1673 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1674 {
1675 if( CurTok() != T_LEFT )
1676 Expecting( T_LEFT );
1677
1678 token = NextTok();
1679
1680 if( token != T_layer )
1681 {
1682 switch( token )
1683 {
1684 case T_copper_finish:
1685 NeedSYMBOL();
1686 stackup.m_FinishType = FromUTF8();
1687 NeedRIGHT();
1688 break;
1689
1690 case T_edge_plating:
1691 token = NextTok();
1692 stackup.m_EdgePlating = token == T_yes;
1693 NeedRIGHT();
1694 break;
1695
1696 case T_dielectric_constraints:
1697 token = NextTok();
1698 stackup.m_HasDielectricConstrains = token == T_yes;
1699 NeedRIGHT();
1700 break;
1701
1702 case T_edge_connector:
1703 token = NextTok();
1705
1706 if( token == T_yes )
1708 else if( token == T_bevelled )
1710
1711 NeedRIGHT();
1712 break;
1713
1714 case T_castellated_pads:
1715 token = NextTok();
1716 stackup.m_CastellatedPads = token == T_yes;
1717 NeedRIGHT();
1718 break;
1719
1720 default:
1721 // Currently, skip this item if not defined, because the stackup def
1722 // is a moving target
1723 //Expecting( "copper_finish, edge_plating, dielectric_constrains, edge_connector, castellated_pads" );
1724 skipCurrent();
1725 break;
1726 }
1727
1728 continue;
1729 }
1730
1731 NeedSYMBOL();
1732 name = FromUTF8();
1733
1734 // init the layer id. For dielectric, layer id = UNDEFINED_LAYER
1735 PCB_LAYER_ID layerId = m_board->GetLayerID( name );
1736
1737 // Init the type
1739
1740 if( layerId == F_SilkS || layerId == B_SilkS )
1742 else if( layerId == F_Mask || layerId == B_Mask )
1744 else if( layerId == F_Paste || layerId == B_Paste )
1746 else if( layerId == UNDEFINED_LAYER )
1748 else if( !( layerId & 1 ) )
1749 type = BS_ITEM_TYPE_COPPER;
1750
1751 BOARD_STACKUP_ITEM* item = nullptr;
1752
1753 if( type != BS_ITEM_TYPE_UNDEFINED )
1754 {
1755 item = new BOARD_STACKUP_ITEM( type );
1756 item->SetBrdLayerId( layerId );
1757
1758 if( type == BS_ITEM_TYPE_DIELECTRIC )
1759 item->SetDielectricLayerId( dielectric_idx++ );
1760
1761 stackup.Add( item );
1762 }
1763 else
1764 {
1765 Expecting( "layer_name" );
1766 }
1767
1768 bool has_next_sublayer = true;
1769 int sublayer_idx = 0; // the index of dielectric sub layers
1770 // sublayer 0 is always existing (main sublayer)
1771
1772 while( has_next_sublayer )
1773 {
1774 has_next_sublayer = false;
1775
1776 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1777 {
1778 if( token == T_addsublayer )
1779 {
1780 has_next_sublayer = true;
1781 break;
1782 }
1783
1784 if( token == T_LEFT )
1785 {
1786 token = NextTok();
1787
1788 switch( token )
1789 {
1790 case T_type:
1791 NeedSYMBOL();
1792 item->SetTypeName( FromUTF8() );
1793 NeedRIGHT();
1794 break;
1795
1796 case T_thickness:
1797 item->SetThickness( parseBoardUnits( T_thickness ), sublayer_idx );
1798 token = NextTok();
1799
1800 if( token == T_LEFT )
1801 break;
1802
1803 if( token == T_locked )
1804 {
1805 // Dielectric thickness can be locked (for impedance controlled layers)
1806 if( type == BS_ITEM_TYPE_DIELECTRIC )
1807 item->SetThicknessLocked( true, sublayer_idx );
1808
1809 NeedRIGHT();
1810 }
1811
1812 break;
1813
1814 case T_material:
1815 NeedSYMBOL();
1816 item->SetMaterial( FromUTF8(), sublayer_idx );
1817 NeedRIGHT();
1818 break;
1819
1820 case T_epsilon_r:
1821 NextTok();
1822 item->SetEpsilonR( parseDouble(), sublayer_idx );
1823 NeedRIGHT();
1824 break;
1825
1826 case T_loss_tangent:
1827 NextTok();
1828 item->SetLossTangent( parseDouble(), sublayer_idx );
1829 NeedRIGHT();
1830 break;
1831
1832 case T_color:
1833 NeedSYMBOL();
1834 name = FromUTF8();
1835
1836 // Older versions didn't store opacity with custom colors
1837 if( name.StartsWith( wxT( "#" ) ) && m_requiredVersion < 20210824 )
1838 {
1840
1841 if( item->GetType() == BS_ITEM_TYPE_SOLDERMASK )
1842 color = color.WithAlpha( DEFAULT_SOLDERMASK_OPACITY );
1843 else
1844 color = color.WithAlpha( 1.0 );
1845
1846 wxColour wx_color = color.ToColour();
1847
1848 // Open-code wxColour::GetAsString() because 3.0 doesn't handle rgba
1849 name.Printf( wxT("#%02X%02X%02X%02X" ),
1850 wx_color.Red(),
1851 wx_color.Green(),
1852 wx_color.Blue(),
1853 wx_color.Alpha() );
1854 }
1855
1856 item->SetColor( name, sublayer_idx );
1857 NeedRIGHT();
1858 break;
1859
1860 default:
1861 // Currently, skip this item if not defined, because the stackup def
1862 // is a moving target
1863 //Expecting( "type, thickness, material, epsilon_r, loss_tangent, color" );
1864 skipCurrent();
1865 }
1866 }
1867 }
1868
1869 if( has_next_sublayer ) // Prepare reading the next sublayer description
1870 {
1871 sublayer_idx++;
1872 item->AddDielectricPrms( sublayer_idx );
1873 }
1874 }
1875 }
1876
1877 if( token != T_RIGHT )
1878 {
1879 Expecting( ")" );
1880 }
1881
1882 // Success:
1884}
1885
1886
1887void PCB_IO_KICAD_SEXPR_PARSER::createOldLayerMapping( std::unordered_map< std::string, std::string >& aMap )
1888{
1889 // N.B. This mapping only includes Italian, Polish and French as they were the only languages
1890 // that mapped the layer names as of cc2022b1ac739aa673d2a0b7a2047638aa7a47b3 (kicad-i18n)
1891 // when the bug was fixed in KiCad source.
1892
1893 // Italian
1894 aMap["Adesivo.Retro"] = "B.Adhes";
1895 aMap["Adesivo.Fronte"] = "F.Adhes";
1896 aMap["Pasta.Retro"] = "B.Paste";
1897 aMap["Pasta.Fronte"] = "F.Paste";
1898 aMap["Serigrafia.Retro"] = "B.SilkS";
1899 aMap["Serigrafia.Fronte"] = "F.SilkS";
1900 aMap["Maschera.Retro"] = "B.Mask";
1901 aMap["Maschera.Fronte"] = "F.Mask";
1902 aMap["Grafica"] = "Dwgs.User";
1903 aMap["Commenti"] = "Cmts.User";
1904 aMap["Eco1"] = "Eco1.User";
1905 aMap["Eco2"] = "Eco2.User";
1906 aMap["Contorno.scheda"] = "Edge.Cuts";
1907
1908 // Polish
1909 aMap["Kleju_Dolna"] = "B.Adhes";
1910 aMap["Kleju_Gorna"] = "F.Adhes";
1911 aMap["Pasty_Dolna"] = "B.Paste";
1912 aMap["Pasty_Gorna"] = "F.Paste";
1913 aMap["Opisowa_Dolna"] = "B.SilkS";
1914 aMap["Opisowa_Gorna"] = "F.SilkS";
1915 aMap["Maski_Dolna"] = "B.Mask";
1916 aMap["Maski_Gorna"] = "F.Mask";
1917 aMap["Rysunkowa"] = "Dwgs.User";
1918 aMap["Komentarzy"] = "Cmts.User";
1919 aMap["ECO1"] = "Eco1.User";
1920 aMap["ECO2"] = "Eco2.User";
1921 aMap["Krawedziowa"] = "Edge.Cuts";
1922
1923 // French
1924 aMap["Dessous.Adhes"] = "B.Adhes";
1925 aMap["Dessus.Adhes"] = "F.Adhes";
1926 aMap["Dessous.Pate"] = "B.Paste";
1927 aMap["Dessus.Pate"] = "F.Paste";
1928 aMap["Dessous.SilkS"] = "B.SilkS";
1929 aMap["Dessus.SilkS"] = "F.SilkS";
1930 aMap["Dessous.Masque"] = "B.Mask";
1931 aMap["Dessus.Masque"] = "F.Mask";
1932 aMap["Dessin.User"] = "Dwgs.User";
1933 aMap["Contours.Ci"] = "Edge.Cuts";
1934}
1935
1936
1938{
1939 wxCHECK_RET( CurTok() == T_layers,
1940 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as layers." ) );
1941
1942 T token;
1943 LSET visibleLayers;
1944 LSET enabledLayers;
1945 int copperLayerCount = 0;
1946 LAYER layer;
1947 bool anyHidden = false;
1948
1949 std::unordered_map< std::string, std::string > v3_layer_names;
1950 std::vector<LAYER> cu;
1951
1952 createOldLayerMapping( v3_layer_names );
1953
1954 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1955 {
1956 parseLayer( &layer );
1957
1958 if( layer.m_type == LT_UNDEFINED ) // it's a non-copper layer
1959 break;
1960
1961 cu.push_back( layer ); // it's copper
1962 }
1963
1964 // All Cu layers are parsed, but not the non-cu layers here.
1965
1966 // The original *.kicad_pcb file format and the inverted
1967 // Cu stack format both have all the Cu layers first, so use this
1968 // trick to handle either. The layer number in the (layers ..)
1969 // s-expression element are ignored.
1970 if( cu.size() )
1971 {
1972 // Rework the layer numbers, which changed when the Cu stack
1973 // was flipped. So we instead use position in the list.
1974 for( size_t i = 1; i < cu.size() - 1; i++ )
1975 {
1976 int tmpLayer = LSET::NameToLayer( cu[i].m_name );
1977
1978 if( tmpLayer < 0 )
1979 tmpLayer = ( i + 1 ) * 2;
1980
1981 cu[i].m_number = tmpLayer;
1982 }
1983
1984 cu[0].m_number = F_Cu;
1985 cu[cu.size()-1].m_number = B_Cu;
1986
1987 for( auto& cu_layer : cu )
1988 {
1989 enabledLayers.set( cu_layer.m_number );
1990
1991 if( cu_layer.m_visible )
1992 visibleLayers.set( cu_layer.m_number );
1993 else
1994 anyHidden = true;
1995
1996 m_board->SetLayerDescr( PCB_LAYER_ID( cu_layer.m_number ), cu_layer );
1997
1998 UTF8 name = cu_layer.m_name;
1999
2000 m_layerIndices[ name ] = PCB_LAYER_ID( cu_layer.m_number );
2001 m_layerMasks[ name ] = LSET( { PCB_LAYER_ID( cu_layer.m_number ) } );
2002 }
2003
2004 copperLayerCount = cu.size();
2005 }
2006
2007 // process non-copper layers
2008 while( token != T_RIGHT )
2009 {
2010 LAYER_ID_MAP::const_iterator it = m_layerIndices.find( UTF8( layer.m_name ) );
2011
2012 if( it == m_layerIndices.end() )
2013 {
2014 auto new_layer_it = v3_layer_names.find( layer.m_name.ToStdString() );
2015
2016 if( new_layer_it != v3_layer_names.end() )
2017 it = m_layerIndices.find( new_layer_it->second );
2018
2019 if( it == m_layerIndices.end() )
2020 {
2021 wxString error;
2022 error.Printf( _( "Layer '%s' in file '%s' at line %d is not in fixed layer hash." ),
2023 layer.m_name,
2024 CurSource(),
2025 CurLineNumber(),
2026 CurOffset() );
2027
2028 THROW_IO_ERROR( error );
2029 }
2030
2031 // If we are here, then we have found a translated layer name. Put it in the maps
2032 // so that items on this layer get the appropriate layer ID number.
2033 m_layerIndices[ UTF8( layer.m_name ) ] = it->second;
2034 m_layerMasks[ UTF8( layer.m_name ) ] = LSET( { it->second } );
2035 layer.m_name = it->first;
2036 }
2037
2038 layer.m_number = it->second;
2039 enabledLayers.set( layer.m_number );
2040
2041 if( layer.m_visible )
2042 visibleLayers.set( layer.m_number );
2043 else
2044 anyHidden = true;
2045
2046 m_board->SetLayerDescr( it->second, layer );
2047
2048 token = NextTok();
2049
2050 if( token != T_LEFT )
2051 break;
2052
2053 parseLayer( &layer );
2054 }
2055
2056 // We need at least 2 copper layers and there must be an even number of them.
2057 if( copperLayerCount < 2 || (copperLayerCount % 2) != 0 )
2058 {
2059 wxString err = wxString::Format( _( "%d is not a valid layer count" ), copperLayerCount );
2060
2061 THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
2062 }
2063
2064 m_board->SetCopperLayerCount( copperLayerCount );
2065 m_board->SetEnabledLayers( enabledLayers );
2066
2067 // Only set this if any layers were explicitly marked as hidden. Otherwise, we want to leave
2068 // this alone; default visibility will show everything
2069 if( anyHidden )
2070 m_board->m_LegacyVisibleLayers = visibleLayers;
2071}
2072
2073
2075{
2076 LSET_MAP::const_iterator it = aMap.find( curText );
2077
2078 if( it == aMap.end() )
2079 return LSET( { Rescue } );
2080
2081 return it->second;
2082}
2083
2084
2086{
2087 // avoid constructing another std::string, use lexer's directly
2088 LAYER_ID_MAP::const_iterator it = aMap.find( curText );
2089
2090 if( it == aMap.end() )
2091 {
2092 m_undefinedLayers.insert( curText );
2093 return Rescue;
2094 }
2095
2096 // Some files may have saved items to the Rescue Layer due to an issue in v5
2097 if( it->second == Rescue )
2098 m_undefinedLayers.insert( curText );
2099
2100 return it->second;
2101}
2102
2103
2105{
2106 wxCHECK_MSG( CurTok() == T_layer, UNDEFINED_LAYER,
2107 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as layer." ) );
2108
2109 NextTok();
2110
2111 PCB_LAYER_ID layerIndex = lookUpLayer( m_layerIndices );
2112
2113 // Handle closing ) in object parser.
2114
2115 return layerIndex;
2116}
2117
2118
2120{
2121 wxCHECK_MSG( CurTok() == T_layers, LSET(),
2122 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as item layers." ) );
2123
2124 LSET layerMask;
2125
2126 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
2127 {
2128 layerMask |= lookUpLayerSet( m_layerMasks );
2129 }
2130
2131 return layerMask;
2132}
2133
2134
2136{
2137 LSET layerMask = parseBoardItemLayersAsMask();
2138
2139 if( ( layerMask & LSET::AllCuMask() ).count() != 1 )
2140 Expecting( "single copper layer" );
2141
2142 if( ( layerMask & LSET( { F_Mask, B_Mask } ) ).count() > 1 )
2143 Expecting( "max one soldermask layer" );
2144
2145 if( ( ( layerMask & LSET::InternalCuMask() ).any()
2146 && ( layerMask & LSET( { F_Mask, B_Mask } ) ).any() ) )
2147 {
2148 Expecting( "no mask layer when track is on internal layer" );
2149 }
2150
2151 if( ( layerMask & LSET( { F_Cu, B_Mask } ) ).count() > 1 )
2152 Expecting( "copper and mask on the same side" );
2153
2154 if( ( layerMask & LSET( { B_Cu, F_Mask } ) ).count() > 1 )
2155 Expecting( "copper and mask on the same side" );
2156
2157 return layerMask;
2158}
2159
2160
2162{
2163 wxCHECK_RET( CurTok() == T_setup,
2164 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as setup." ) );
2165
2167 const std::shared_ptr<NETCLASS>& defaultNetClass = bds.m_NetSettings->GetDefaultNetclass();
2168 ZONE_SETTINGS& zoneSettings = bds.GetDefaultZoneSettings();
2169
2170 // Missing soldermask min width value means that the user has set the value to 0 and
2171 // not the default value (0.25mm)
2172 bds.m_SolderMaskMinWidth = 0;
2173
2174 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
2175 {
2176 if( token != T_LEFT )
2177 Expecting( T_LEFT );
2178
2179 token = NextTok();
2180
2181 switch( token )
2182 {
2183 case T_stackup:
2185 break;
2186
2187 case T_last_trace_width: // not used now
2188 /* lastTraceWidth =*/ parseBoardUnits( T_last_trace_width );
2189 NeedRIGHT();
2190 break;
2191
2192 case T_user_trace_width:
2193 {
2194 // Make room for the netclass value
2195 if( bds.m_TrackWidthList.empty() )
2196 bds.m_TrackWidthList.emplace_back( 0 );
2197
2198 int trackWidth = parseBoardUnits( T_user_trace_width );
2199
2200 if( !m_appendToExisting || !alg::contains( bds.m_TrackWidthList, trackWidth ) )
2201 bds.m_TrackWidthList.push_back( trackWidth );
2202
2204 NeedRIGHT();
2205 break;
2206 }
2207
2208 case T_trace_clearance:
2209 defaultNetClass->SetClearance( parseBoardUnits( T_trace_clearance ) );
2211 NeedRIGHT();
2212 break;
2213
2214 case T_zone_clearance:
2215 zoneSettings.m_ZoneClearance = parseBoardUnits( T_zone_clearance );
2217 NeedRIGHT();
2218 break;
2219
2220 case T_zone_45_only: // legacy setting
2221 /* zoneSettings.m_Zone_45_Only = */ parseBool();
2223 NeedRIGHT();
2224 break;
2225
2226 case T_clearance_min:
2227 bds.m_MinClearance = parseBoardUnits( T_clearance_min );
2229 NeedRIGHT();
2230 break;
2231
2232 case T_trace_min:
2233 bds.m_TrackMinWidth = parseBoardUnits( T_trace_min );
2235 NeedRIGHT();
2236 break;
2237
2238 case T_via_size:
2239 defaultNetClass->SetViaDiameter( parseBoardUnits( T_via_size ) );
2241 NeedRIGHT();
2242 break;
2243
2244 case T_via_drill:
2245 defaultNetClass->SetViaDrill( parseBoardUnits( T_via_drill ) );
2247 NeedRIGHT();
2248 break;
2249
2250 case T_via_min_annulus:
2251 bds.m_ViasMinAnnularWidth = parseBoardUnits( T_via_min_annulus );
2253 NeedRIGHT();
2254 break;
2255
2256 case T_via_min_size:
2257 bds.m_ViasMinSize = parseBoardUnits( T_via_min_size );
2259 NeedRIGHT();
2260 break;
2261
2262 case T_through_hole_min:
2263 bds.m_MinThroughDrill = parseBoardUnits( T_through_hole_min );
2265 NeedRIGHT();
2266 break;
2267
2268 // Legacy token for T_through_hole_min
2269 case T_via_min_drill:
2270 bds.m_MinThroughDrill = parseBoardUnits( T_via_min_drill );
2272 NeedRIGHT();
2273 break;
2274
2275 case T_hole_to_hole_min:
2276 bds.m_HoleToHoleMin = parseBoardUnits( T_hole_to_hole_min );
2278 NeedRIGHT();
2279 break;
2280
2281 case T_user_via:
2282 {
2283 int viaSize = parseBoardUnits( "user via size" );
2284 int viaDrill = parseBoardUnits( "user via drill" );
2285 VIA_DIMENSION via( viaSize, viaDrill );
2286
2287 // Make room for the netclass value
2288 if( bds.m_ViasDimensionsList.empty() )
2289 bds.m_ViasDimensionsList.emplace_back( VIA_DIMENSION( 0, 0 ) );
2290
2292 bds.m_ViasDimensionsList.emplace_back( via );
2293
2295 NeedRIGHT();
2296 break;
2297 }
2298
2299 case T_uvia_size:
2300 defaultNetClass->SetuViaDiameter( parseBoardUnits( T_uvia_size ) );
2302 NeedRIGHT();
2303 break;
2304
2305 case T_uvia_drill:
2306 defaultNetClass->SetuViaDrill( parseBoardUnits( T_uvia_drill ) );
2308 NeedRIGHT();
2309 break;
2310
2311 case T_uvias_allowed:
2312 parseBool();
2314 NeedRIGHT();
2315 break;
2316
2317 case T_blind_buried_vias_allowed:
2318 parseBool();
2320 NeedRIGHT();
2321 break;
2322
2323 case T_uvia_min_size:
2324 bds.m_MicroViasMinSize = parseBoardUnits( T_uvia_min_size );
2326 NeedRIGHT();
2327 break;
2328
2329 case T_uvia_min_drill:
2330 bds.m_MicroViasMinDrill = parseBoardUnits( T_uvia_min_drill );
2332 NeedRIGHT();
2333 break;
2334
2335 case T_user_diff_pair:
2336 {
2337 int width = parseBoardUnits( "user diff-pair width" );
2338 int gap = parseBoardUnits( "user diff-pair gap" );
2339 int viaGap = parseBoardUnits( "user diff-pair via gap" );
2340 DIFF_PAIR_DIMENSION diffPair( width, gap, viaGap );
2341
2343 bds.m_DiffPairDimensionsList.emplace_back( diffPair );
2344
2346 NeedRIGHT();
2347 break;
2348 }
2349
2350 case T_segment_width: // note: legacy (pre-6.0) token
2351 bds.m_LineThickness[ LAYER_CLASS_COPPER ] = parseBoardUnits( T_segment_width );
2353 NeedRIGHT();
2354 break;
2355
2356 case T_edge_width: // note: legacy (pre-6.0) token
2357 bds.m_LineThickness[ LAYER_CLASS_EDGES ] = parseBoardUnits( T_edge_width );
2359 NeedRIGHT();
2360 break;
2361
2362 case T_mod_edge_width: // note: legacy (pre-6.0) token
2363 bds.m_LineThickness[ LAYER_CLASS_SILK ] = parseBoardUnits( T_mod_edge_width );
2365 NeedRIGHT();
2366 break;
2367
2368 case T_pcb_text_width: // note: legacy (pre-6.0) token
2369 bds.m_TextThickness[ LAYER_CLASS_COPPER ] = parseBoardUnits( T_pcb_text_width );
2371 NeedRIGHT();
2372 break;
2373
2374 case T_mod_text_width: // note: legacy (pre-6.0) token
2375 bds.m_TextThickness[ LAYER_CLASS_SILK ] = parseBoardUnits( T_mod_text_width );
2377 NeedRIGHT();
2378 break;
2379
2380 case T_pcb_text_size: // note: legacy (pre-6.0) token
2381 bds.m_TextSize[ LAYER_CLASS_COPPER ].x = parseBoardUnits( "pcb text width" );
2382 bds.m_TextSize[ LAYER_CLASS_COPPER ].y = parseBoardUnits( "pcb text height" );
2384 NeedRIGHT();
2385 break;
2386
2387 case T_mod_text_size: // note: legacy (pre-6.0) token
2388 bds.m_TextSize[ LAYER_CLASS_SILK ].x = parseBoardUnits( "footprint text width" );
2389 bds.m_TextSize[ LAYER_CLASS_SILK ].y = parseBoardUnits( "footprint text height" );
2391 NeedRIGHT();
2392 break;
2393
2394 case T_defaults:
2395 parseDefaults( bds );
2397 break;
2398
2399 case T_pad_size:
2400 {
2401 VECTOR2I sz;
2402 sz.x = parseBoardUnits( "master pad width" );
2403 sz.y = parseBoardUnits( "master pad height" );
2404 bds.m_Pad_Master->SetSize( PADSTACK::ALL_LAYERS, sz );
2406 NeedRIGHT();
2407 break;
2408 }
2409
2410 case T_pad_drill:
2411 {
2412 int drillSize = parseBoardUnits( T_pad_drill );
2413 bds.m_Pad_Master->SetDrillSize( VECTOR2I( drillSize, drillSize ) );
2415 NeedRIGHT();
2416 break;
2417 }
2418
2419 case T_pad_to_mask_clearance:
2420 bds.m_SolderMaskExpansion = parseBoardUnits( T_pad_to_mask_clearance );
2421 NeedRIGHT();
2422 break;
2423
2424 case T_solder_mask_min_width:
2425 bds.m_SolderMaskMinWidth = parseBoardUnits( T_solder_mask_min_width );
2426 NeedRIGHT();
2427 break;
2428
2429 case T_pad_to_paste_clearance:
2430 bds.m_SolderPasteMargin = parseBoardUnits( T_pad_to_paste_clearance );
2431 NeedRIGHT();
2432 break;
2433
2434 case T_pad_to_paste_clearance_ratio:
2435 bds.m_SolderPasteMarginRatio = parseDouble( T_pad_to_paste_clearance_ratio );
2436 NeedRIGHT();
2437 break;
2438
2439 case T_allow_soldermask_bridges_in_footprints:
2441 NeedRIGHT();
2442 break;
2443
2444 case T_tenting:
2445 {
2446 auto [front, back] = parseFrontBackOptBool( true );
2447 bds.m_TentViasFront = front.value_or( false );
2448 bds.m_TentViasBack = back.value_or( false );
2449 break;
2450 }
2451
2452 case T_covering:
2453 {
2454 auto [front, back] = parseFrontBackOptBool();
2455 bds.m_CoverViasFront = front.value_or( false );
2456 bds.m_CoverViasBack = back.value_or( false );
2457 break;
2458 }
2459
2460 case T_plugging:
2461 {
2462 auto [front, back] = parseFrontBackOptBool();
2463 bds.m_PlugViasFront = front.value_or( false );
2464 bds.m_PlugViasBack = back.value_or( false );
2465 break;
2466 }
2467
2468 case T_capping:
2469 {
2470 bds.m_CapVias = parseBool();
2471 NeedRIGHT();
2472 break;
2473 }
2474
2475 case T_filling:
2476 {
2477 bds.m_FillVias = parseBool();
2478 NeedRIGHT();
2479 break;
2480 }
2481
2482 case T_aux_axis_origin:
2483 {
2484 int x = parseBoardUnits( "auxiliary origin X" );
2485 int y = parseBoardUnits( "auxiliary origin Y" );
2486 bds.SetAuxOrigin( VECTOR2I( x, y ) );
2487
2488 // Aux origin still stored in board for the moment
2489 //m_board->m_LegacyDesignSettingsLoaded = true;
2490 NeedRIGHT();
2491 break;
2492 }
2493
2494 case T_grid_origin:
2495 {
2496 int x = parseBoardUnits( "grid origin X" );
2497 int y = parseBoardUnits( "grid origin Y" );
2498 bds.SetGridOrigin( VECTOR2I( x, y ) );
2499 // Grid origin still stored in board for the moment
2500 //m_board->m_LegacyDesignSettingsLoaded = true;
2501 NeedRIGHT();
2502 break;
2503 }
2504
2505 // Stored in board prior to 6.0
2506 case T_visible_elements:
2507 {
2508 // Make sure to start with DefaultVisible so all new layers are set
2510
2511 int visible = parseHex() | MIN_VISIBILITY_MASK;
2512
2513 for( size_t i = 0; i < sizeof( int ) * CHAR_BIT; i++ )
2514 m_board->m_LegacyVisibleItems.set( i, visible & ( 1u << i ) );
2515
2516 NeedRIGHT();
2517 break;
2518 }
2519
2520 case T_max_error:
2521 bds.m_MaxError = parseBoardUnits( T_max_error );
2523 NeedRIGHT();
2524 break;
2525
2526 case T_filled_areas_thickness:
2527 // Ignore this value, it is not used anymore
2528 parseBool();
2529 NeedRIGHT();
2530 break;
2531
2532 case T_pcbplotparams:
2533 {
2534 PCB_PLOT_PARAMS plotParams;
2535 PCB_PLOT_PARAMS_PARSER parser( reader, m_requiredVersion );
2536 // parser must share the same current line as our current PCB parser
2537 // synchronize it.
2538 parser.SyncLineReaderWith( *this );
2539
2540 plotParams.Parse( &parser );
2541 SyncLineReaderWith( parser );
2542
2543 m_board->SetPlotOptions( plotParams );
2544
2545 if( plotParams.GetLegacyPlotViaOnMaskLayer().has_value() )
2546 {
2547 bool tent = !( *plotParams.GetLegacyPlotViaOnMaskLayer() );
2550 }
2551
2552 break;
2553 }
2554 case T_zone_defaults:
2556 break;
2557
2558 default:
2559 Unexpected( CurText() );
2560 }
2561 }
2562
2563 // Set up a default stackup in case the file doesn't define one, and now we know
2564 // the enabled layers
2566 {
2567 BOARD_STACKUP& stackup = bds.GetStackupDescriptor();
2568 stackup.RemoveAll();
2570 }
2571}
2572
2573
2575{
2576 T token;
2577
2578 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2579 {
2580 if( token != T_LEFT )
2581 {
2582 Expecting( T_LEFT );
2583 }
2584
2585 token = NextTok();
2586
2587 switch( token )
2588 {
2589 case T_property:
2591 break;
2592 default:
2593 Unexpected( CurText() );
2594 }
2595 }
2596}
2597
2598
2600 std::map<PCB_LAYER_ID, ZONE_LAYER_PROPERTIES>& aProperties )
2601{
2602 T token;
2603
2605 ZONE_LAYER_PROPERTIES properties;
2606
2607 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2608 {
2609 if( token != T_LEFT )
2610 {
2611 Expecting( T_LEFT );
2612 }
2613
2614 token = NextTok();
2615
2616 switch( token )
2617 {
2618 case T_layer:
2619 layer = parseBoardItemLayer();
2620 NeedRIGHT();
2621 break;
2622 case T_hatch_position:
2623 {
2624 properties.hatching_offset = parseXY();
2625 NeedRIGHT();
2626 break;
2627 }
2628 default:
2629 Unexpected( CurText() );
2630 break;
2631 }
2632 }
2633
2634 aProperties.emplace( layer, properties );
2635}
2636
2637
2639{
2640 T token;
2641
2642 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2643 {
2644 if( token != T_LEFT )
2645 Expecting( T_LEFT );
2646
2647 token = NextTok();
2648
2649 switch( token )
2650 {
2651 case T_edge_clearance:
2652 designSettings.m_CopperEdgeClearance = parseBoardUnits( T_edge_clearance );
2654 NeedRIGHT();
2655 break;
2656
2657 case T_copper_line_width:
2658 designSettings.m_LineThickness[ LAYER_CLASS_COPPER ] = parseBoardUnits( token );
2659 NeedRIGHT();
2660 break;
2661
2662 case T_copper_text_dims:
2663 parseDefaultTextDims( designSettings, LAYER_CLASS_COPPER );
2664 break;
2665
2666 case T_courtyard_line_width:
2667 designSettings.m_LineThickness[ LAYER_CLASS_COURTYARD ] = parseBoardUnits( token );
2668 NeedRIGHT();
2669 break;
2670
2671 case T_edge_cuts_line_width:
2672 designSettings.m_LineThickness[ LAYER_CLASS_EDGES ] = parseBoardUnits( token );
2673 NeedRIGHT();
2674 break;
2675
2676 case T_silk_line_width:
2677 designSettings.m_LineThickness[ LAYER_CLASS_SILK ] = parseBoardUnits( token );
2678 NeedRIGHT();
2679 break;
2680
2681 case T_silk_text_dims:
2682 parseDefaultTextDims( designSettings, LAYER_CLASS_SILK );
2683 break;
2684
2685 case T_fab_layers_line_width:
2686 designSettings.m_LineThickness[ LAYER_CLASS_FAB ] = parseBoardUnits( token );
2687 NeedRIGHT();
2688 break;
2689
2690 case T_fab_layers_text_dims:
2691 parseDefaultTextDims( designSettings, LAYER_CLASS_FAB );
2692 break;
2693
2694 case T_other_layers_line_width:
2695 designSettings.m_LineThickness[ LAYER_CLASS_OTHERS ] = parseBoardUnits( token );
2696 NeedRIGHT();
2697 break;
2698
2699 case T_other_layers_text_dims:
2700 parseDefaultTextDims( designSettings, LAYER_CLASS_OTHERS );
2701 break;
2702
2703 case T_dimension_units:
2704 designSettings.m_DimensionUnitsMode =
2705 static_cast<DIM_UNITS_MODE>( parseInt( "dimension units" ) );
2706 NeedRIGHT();
2707 break;
2708
2709 case T_dimension_precision:
2710 designSettings.m_DimensionPrecision =
2711 static_cast<DIM_PRECISION>( parseInt( "dimension precision" ) );
2712 NeedRIGHT();
2713 break;
2714
2715 default:
2716 Unexpected( CurText() );
2717 }
2718 }
2719}
2720
2721
2723{
2724 T token;
2725
2726 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2727 {
2728 if( token == T_LEFT )
2729 token = NextTok();
2730
2731 switch( token )
2732 {
2733 case T_size:
2734 aSettings.m_TextSize[ aLayer ].x = parseBoardUnits( "default text size X" );
2735 aSettings.m_TextSize[ aLayer ].y = parseBoardUnits( "default text size Y" );
2736 NeedRIGHT();
2737 break;
2738
2739 case T_thickness:
2740 aSettings.m_TextThickness[ aLayer ] = parseBoardUnits( "default text width" );
2741 NeedRIGHT();
2742 break;
2743
2744 case T_italic:
2745 aSettings.m_TextItalic[ aLayer ] = true;
2746 break;
2747
2748 case T_keep_upright:
2749 aSettings.m_TextUpright[ aLayer ] = true;
2750 break;
2751
2752 default:
2753 Expecting( "size, thickness, italic or keep_upright" );
2754 }
2755 }
2756}
2757
2758
2760{
2761 wxCHECK_RET( CurTok() == T_net,
2762 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as net." ) );
2763
2764 int netCode = parseInt( "net number" );
2765
2766 NeedSYMBOLorNUMBER();
2767 wxString name = FromUTF8();
2768
2769 // Convert overbar syntax from `~...~` to `~{...}`. These were left out of the first merge
2770 // so the version is a bit later.
2771 if( m_requiredVersion < 20210606 )
2773
2774 NeedRIGHT();
2775
2776 // net 0 should be already in list, so store this net
2777 // if it is not the net 0, or if the net 0 does not exists.
2778 // (TODO: a better test.)
2780 {
2781 NETINFO_ITEM* net = new NETINFO_ITEM( m_board, name, netCode );
2782 m_board->Add( net, ADD_MODE::INSERT, true );
2783
2784 // Store the new code mapping
2785 pushValueIntoMap( netCode, net->GetNetCode() );
2786 }
2787}
2788
2789
2791{
2792 wxCHECK_RET( CurTok() == T_net_class,
2793 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as net class." ) );
2794
2795 T token;
2796
2797 std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( wxEmptyString );
2798
2799 // Read netclass name (can be a name or just a number like track width)
2800 NeedSYMBOLorNUMBER();
2801 nc->SetName( FromUTF8() );
2802 NeedSYMBOL();
2803 nc->SetDescription( FromUTF8() );
2804
2805 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2806 {
2807 if( token != T_LEFT )
2808 Expecting( T_LEFT );
2809
2810 token = NextTok();
2811
2812 switch( token )
2813 {
2814 case T_clearance:
2815 nc->SetClearance( parseBoardUnits( T_clearance ) );
2816 break;
2817
2818 case T_trace_width:
2819 nc->SetTrackWidth( parseBoardUnits( T_trace_width ) );
2820 break;
2821
2822 case T_via_dia:
2823 nc->SetViaDiameter( parseBoardUnits( T_via_dia ) );
2824 break;
2825
2826 case T_via_drill:
2827 nc->SetViaDrill( parseBoardUnits( T_via_drill ) );
2828 break;
2829
2830 case T_uvia_dia:
2831 nc->SetuViaDiameter( parseBoardUnits( T_uvia_dia ) );
2832 break;
2833
2834 case T_uvia_drill:
2835 nc->SetuViaDrill( parseBoardUnits( T_uvia_drill ) );
2836 break;
2837
2838 case T_diff_pair_width:
2839 nc->SetDiffPairWidth( parseBoardUnits( T_diff_pair_width ) );
2840 break;
2841
2842 case T_diff_pair_gap:
2843 nc->SetDiffPairGap( parseBoardUnits( T_diff_pair_gap ) );
2844 break;
2845
2846 case T_add_net:
2847 {
2848 NeedSYMBOLorNUMBER();
2849
2850 wxString netName = FromUTF8();
2851
2852 // Convert overbar syntax from `~...~` to `~{...}`. These were left out of the
2853 // first merge so the version is a bit later.
2854 if( m_requiredVersion < 20210606 )
2855 netName = ConvertToNewOverbarNotation( FromUTF8() );
2856
2857 m_board->GetDesignSettings().m_NetSettings->SetNetclassPatternAssignment(
2858 netName, nc->GetName() );
2859
2860 break;
2861 }
2862
2863 default:
2864 Expecting( "clearance, trace_width, via_dia, via_drill, uvia_dia, uvia_drill, "
2865 "diff_pair_width, diff_pair_gap or add_net" );
2866 }
2867
2868 NeedRIGHT();
2869 }
2870
2871 std::shared_ptr<NET_SETTINGS>& netSettings = m_board->GetDesignSettings().m_NetSettings;
2872
2873 if( netSettings->HasNetclass( nc->GetName() ) )
2874 {
2875 // Must have been a name conflict, this is a bad board file.
2876 // User may have done a hand edit to the file.
2877 wxString error;
2878 error.Printf( _( "Duplicate NETCLASS name '%s' in file '%s' at line %d, offset %d." ),
2879 nc->GetName().GetData(), CurSource().GetData(), CurLineNumber(),
2880 CurOffset() );
2881 THROW_IO_ERROR( error );
2882 }
2883 else if( nc->GetName() == netSettings->GetDefaultNetclass()->GetName() )
2884 {
2885 netSettings->SetDefaultNetclass( nc );
2886 }
2887 else
2888 {
2889 netSettings->SetNetclass( nc->GetName(), nc );
2890 }
2891}
2892
2893
2895{
2896 wxCHECK_MSG( CurTok() == T_fp_arc || CurTok() == T_fp_circle || CurTok() == T_fp_curve ||
2897 CurTok() == T_fp_rect || CurTok() == T_fp_line || CurTok() == T_fp_poly ||
2898 CurTok() == T_gr_arc || CurTok() == T_gr_circle || CurTok() == T_gr_curve ||
2899 CurTok() == T_gr_rect || CurTok() == T_gr_bbox || CurTok() == T_gr_line ||
2900 CurTok() == T_gr_poly || CurTok() == T_gr_vector, nullptr,
2901 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_SHAPE." ) );
2902
2903 T token;
2904 VECTOR2I pt;
2905 STROKE_PARAMS stroke( 0, LINE_STYLE::SOLID );
2906 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( aParent );
2907
2908 switch( CurTok() )
2909 {
2910 case T_gr_arc:
2911 case T_fp_arc:
2912 shape->SetShape( SHAPE_T::ARC );
2913 token = NextTok();
2914
2915 if( token == T_locked )
2916 {
2917 shape->SetLocked( true );
2918 token = NextTok();
2919 }
2920
2921 if( token != T_LEFT )
2922 Expecting( T_LEFT );
2923
2924 token = NextTok();
2925
2927 {
2928 // In legacy files the start keyword actually gives the arc center...
2929 if( token != T_start )
2930 Expecting( T_start );
2931
2932 pt.x = parseBoardUnits( "X coordinate" );
2933 pt.y = parseBoardUnits( "Y coordinate" );
2934 shape->SetCenter( pt );
2935 NeedRIGHT();
2936 NeedLEFT();
2937 token = NextTok();
2938
2939 // ... and the end keyword gives the start point of the arc
2940 if( token != T_end )
2941 Expecting( T_end );
2942
2943 pt.x = parseBoardUnits( "X coordinate" );
2944 pt.y = parseBoardUnits( "Y coordinate" );
2945 shape->SetStart( pt );
2946 NeedRIGHT();
2947 NeedLEFT();
2948 token = NextTok();
2949
2950 if( token != T_angle )
2951 Expecting( T_angle );
2952
2953 shape->SetArcAngleAndEnd( EDA_ANGLE( parseDouble( "arc angle" ), DEGREES_T ), true );
2954 NeedRIGHT();
2955 }
2956 else
2957 {
2958 VECTOR2I arc_start, arc_mid, arc_end;
2959
2960 if( token != T_start )
2961 Expecting( T_start );
2962
2963 arc_start.x = parseBoardUnits( "X coordinate" );
2964 arc_start.y = parseBoardUnits( "Y coordinate" );
2965 NeedRIGHT();
2966 NeedLEFT();
2967 token = NextTok();
2968
2969 if( token != T_mid )
2970 Expecting( T_mid );
2971
2972 arc_mid.x = parseBoardUnits( "X coordinate" );
2973 arc_mid.y = parseBoardUnits( "Y coordinate" );
2974 NeedRIGHT();
2975 NeedLEFT();
2976 token = NextTok();
2977
2978 if( token != T_end )
2979 Expecting( T_end );
2980
2981 arc_end.x = parseBoardUnits( "X coordinate" );
2982 arc_end.y = parseBoardUnits( "Y coordinate" );
2983 NeedRIGHT();
2984
2985 shape->SetArcGeometry( arc_start, arc_mid, arc_end );
2986 }
2987
2988 break;
2989
2990 case T_gr_circle:
2991 case T_fp_circle:
2992 shape->SetShape( SHAPE_T::CIRCLE );
2993 token = NextTok();
2994
2995 if( token == T_locked )
2996 {
2997 shape->SetLocked( true );
2998 token = NextTok();
2999 }
3000
3001 if( token != T_LEFT )
3002 Expecting( T_LEFT );
3003
3004 token = NextTok();
3005
3006 if( token != T_center )
3007 Expecting( T_center );
3008
3009 pt.x = parseBoardUnits( "X coordinate" );
3010 pt.y = parseBoardUnits( "Y coordinate" );
3011 shape->SetStart( pt );
3012 NeedRIGHT();
3013 NeedLEFT();
3014
3015 token = NextTok();
3016
3017 if( token != T_end )
3018 Expecting( T_end );
3019
3020 pt.x = parseBoardUnits( "X coordinate" );
3021 pt.y = parseBoardUnits( "Y coordinate" );
3022 shape->SetEnd( pt );
3023 NeedRIGHT();
3024 break;
3025
3026 case T_gr_curve:
3027 case T_fp_curve:
3028 shape->SetShape( SHAPE_T::BEZIER );
3029 token = NextTok();
3030
3031 if( token == T_locked )
3032 {
3033 shape->SetLocked( true );
3034 token = NextTok();
3035 }
3036
3037 if( token != T_LEFT )
3038 Expecting( T_LEFT );
3039
3040 token = NextTok();
3041
3042 if( token != T_pts )
3043 Expecting( T_pts );
3044
3045 shape->SetStart( parseXY() );
3046 shape->SetBezierC1( parseXY());
3047 shape->SetBezierC2( parseXY());
3048 shape->SetEnd( parseXY() );
3049 shape->RebuildBezierToSegmentsPointsList( ARC_HIGH_DEF );
3050 NeedRIGHT();
3051 break;
3052
3053 case T_gr_bbox:
3054 case T_gr_rect:
3055 case T_fp_rect:
3056 shape->SetShape( SHAPE_T::RECTANGLE );
3057 token = NextTok();
3058
3059 if( token == T_locked )
3060 {
3061 shape->SetLocked( true );
3062 token = NextTok();
3063 }
3064
3065 if( token != T_LEFT )
3066 Expecting( T_LEFT );
3067
3068 token = NextTok();
3069
3070 if( token != T_start )
3071 Expecting( T_start );
3072
3073 pt.x = parseBoardUnits( "X coordinate" );
3074 pt.y = parseBoardUnits( "Y coordinate" );
3075 shape->SetStart( pt );
3076 NeedRIGHT();
3077 NeedLEFT();
3078 token = NextTok();
3079
3080 if( token != T_end )
3081 Expecting( T_end );
3082
3083 pt.x = parseBoardUnits( "X coordinate" );
3084 pt.y = parseBoardUnits( "Y coordinate" );
3085 shape->SetEnd( pt );
3086
3087 if( aParent && aParent->Type() == PCB_FOOTPRINT_T )
3088 {
3089 // Footprint shapes are stored in board-relative coordinates, but we want the
3090 // normalization to remain in footprint-relative coordinates.
3091 }
3092 else
3093 {
3094 shape->Normalize();
3095 }
3096
3097 NeedRIGHT();
3098 break;
3099
3100 case T_gr_vector:
3101 case T_gr_line:
3102 case T_fp_line:
3103 shape->SetShape( SHAPE_T::SEGMENT );
3104 token = NextTok();
3105
3106 if( token == T_locked )
3107 {
3108 shape->SetLocked( true );
3109 token = NextTok();
3110 }
3111
3112 if( token != T_LEFT )
3113 Expecting( T_LEFT );
3114
3115 token = NextTok();
3116
3117 if( token != T_start )
3118 Expecting( T_start );
3119
3120 pt.x = parseBoardUnits( "X coordinate" );
3121 pt.y = parseBoardUnits( "Y coordinate" );
3122 shape->SetStart( pt );
3123 NeedRIGHT();
3124 NeedLEFT();
3125 token = NextTok();
3126
3127 if( token != T_end )
3128 Expecting( T_end );
3129
3130 pt.x = parseBoardUnits( "X coordinate" );
3131 pt.y = parseBoardUnits( "Y coordinate" );
3132 shape->SetEnd( pt );
3133 NeedRIGHT();
3134 break;
3135
3136 case T_gr_poly:
3137 case T_fp_poly:
3138 {
3139 shape->SetShape( SHAPE_T::POLY );
3140 shape->SetPolyPoints( {} );
3141
3142 SHAPE_LINE_CHAIN& outline = shape->GetPolyShape().Outline( 0 );
3143
3144 token = NextTok();
3145
3146 if( token == T_locked )
3147 {
3148 shape->SetLocked( true );
3149 token = NextTok();
3150 }
3151
3152 if( token != T_LEFT )
3153 Expecting( T_LEFT );
3154
3155 token = NextTok();
3156
3157 if( token != T_pts )
3158 Expecting( T_pts );
3159
3160 while( (token = NextTok() ) != T_RIGHT )
3161 parseOutlinePoints( outline );
3162
3163 break;
3164 }
3165
3166 default:
3167 if( aParent && aParent->Type() == PCB_FOOTPRINT_T )
3168 {
3169 Expecting( "fp_arc, fp_circle, fp_curve, fp_line, fp_poly or fp_rect" );
3170 }
3171 else
3172 {
3173 Expecting( "gr_arc, gr_circle, gr_curve, gr_vector, gr_line, gr_poly, gr_rect or "
3174 "gr_bbox" );
3175 }
3176 }
3177
3178 bool foundFill = false;
3179
3180 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3181 {
3182 if( token != T_LEFT )
3183 Expecting( T_LEFT );
3184
3185 token = NextTok();
3186
3187 switch( token )
3188 {
3189 case T_angle: // legacy token; ignore value
3190 parseDouble( "arc angle" );
3191 NeedRIGHT();
3192 break;
3193
3194 case T_layer:
3195 shape->SetLayer( parseBoardItemLayer() );
3196 NeedRIGHT();
3197 break;
3198
3199 case T_layers:
3200 shape->SetLayerSet( parseBoardItemLayersAsMask() );
3201 break;
3202
3203 case T_solder_mask_margin:
3204 shape->SetLocalSolderMaskMargin( parseBoardUnits( "local solder mask margin value" ) );
3205 NeedRIGHT();
3206 break;
3207
3208 case T_width: // legacy token
3209 stroke.SetWidth( parseBoardUnits( T_width ) );
3210 NeedRIGHT();
3211 break;
3212
3213 case T_stroke:
3214 {
3215 STROKE_PARAMS_PARSER strokeParser( reader, pcbIUScale.IU_PER_MM );
3216 strokeParser.SyncLineReaderWith( *this );
3217
3218 strokeParser.ParseStroke( stroke );
3219 SyncLineReaderWith( strokeParser );
3220 break;
3221 }
3222
3223 case T_tstamp:
3224 case T_uuid:
3225 NextTok();
3226 const_cast<KIID&>( shape->m_Uuid ) = CurStrToKIID();
3227 NeedRIGHT();
3228 break;
3229
3230 case T_fill:
3231 foundFill = true;
3232
3233 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3234 {
3235 if( token == T_LEFT )
3236 token = NextTok();
3237
3238 switch( token )
3239 {
3240 // T_yes was used to indicate filling when first introduced, so treat it like a
3241 // solid fill since that was the only fill available at the time.
3242 case T_yes:
3243 case T_solid: shape->SetFillMode( FILL_T::FILLED_SHAPE ); break;
3244
3245 case T_none:
3246 case T_no: shape->SetFillMode( FILL_T::NO_FILL ); break;
3247
3248 case T_hatch: shape->SetFillMode( FILL_T::HATCH ); break;
3249 case T_reverse_hatch: shape->SetFillMode( FILL_T::REVERSE_HATCH ); break;
3250 case T_cross_hatch: shape->SetFillMode( FILL_T::CROSS_HATCH ); break;
3251
3252 default: Expecting( "yes, no, solid, none, hatch, reverse_hatch or cross_hatch" );
3253 }
3254 }
3255
3256 break;
3257
3258 case T_status: // legacy token; ignore value
3259 parseHex();
3260 NeedRIGHT();
3261 break;
3262
3263 // Handle "(locked)" from 5.99 development, and "(locked yes)" from modern times
3264 case T_locked:
3265 shape->SetLocked( parseMaybeAbsentBool( true ) );
3266 break;
3267
3268 case T_net:
3269 if( !shape->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
3270 {
3271 wxLogError( _( "Invalid net ID in\nfile: '%s'\nline: %d\noffset: %d." ),
3272 CurSource(), CurLineNumber(), CurOffset() );
3273 }
3274
3275 NeedRIGHT();
3276 break;
3277
3278 default:
3279 Expecting( "layer, width, fill, tstamp, uuid, locked, net, status, "
3280 "or solder_mask_margin" );
3281 }
3282 }
3283
3284 if( !foundFill )
3285 {
3286 // Legacy versions didn't have a filled flag but allowed some shapes to indicate they
3287 // should be filled by specifying a 0 stroke-width.
3288 if( stroke.GetWidth() == 0
3289 && ( shape->GetShape() == SHAPE_T::RECTANGLE || shape->GetShape() == SHAPE_T::CIRCLE ) )
3290 {
3291 shape->SetFilled( true );
3292 }
3293 else if( shape->GetShape() == SHAPE_T::POLY && shape->GetLayer() != Edge_Cuts )
3294 {
3295 // Polygons on non-Edge_Cuts layers were always filled.
3296 shape->SetFilled( true );
3297 }
3298 }
3299
3300 // Only filled shapes may have a zero line-width. This is not permitted in KiCad but some
3301 // external tools can generate invalid files.
3302 if( stroke.GetWidth() <= 0 && !shape->IsAnyFill() )
3303 {
3305 }
3306
3307 shape->SetStroke( stroke );
3308
3309 if( FOOTPRINT* parentFP = shape->GetParentFootprint() )
3310 {
3311 shape->Rotate( { 0, 0 }, parentFP->GetOrientation() );
3312 shape->Move( parentFP->GetPosition() );
3313 }
3314
3315 return shape.release();
3316}
3317
3318
3320{
3321 wxCHECK_MSG( CurTok() == T_image, nullptr,
3322 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a reference image." ) );
3323
3324 T token;
3325 std::unique_ptr<PCB_REFERENCE_IMAGE> bitmap = std::make_unique<PCB_REFERENCE_IMAGE>( aParent );
3326
3327 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3328 {
3329 if( token != T_LEFT )
3330 Expecting( T_LEFT );
3331
3332 token = NextTok();
3333
3334 switch( token )
3335 {
3336 case T_at:
3337 {
3338 VECTOR2I pos;
3339 pos.x = parseBoardUnits( "X coordinate" );
3340 pos.y = parseBoardUnits( "Y coordinate" );
3341 bitmap->SetPosition( pos );
3342 NeedRIGHT();
3343 break;
3344 }
3345
3346 case T_layer:
3347 bitmap->SetLayer( parseBoardItemLayer() );
3348 NeedRIGHT();
3349 break;
3350
3351 case T_scale:
3352 {
3353 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
3354 refImage.SetImageScale( parseDouble( "image scale factor" ) );
3355
3356 if( !std::isnormal( refImage.GetImageScale() ) )
3357 refImage.SetImageScale( 1.0 );
3358
3359 NeedRIGHT();
3360 break;
3361 }
3362 case T_data:
3363 {
3364 token = NextTok();
3365
3366 wxString data;
3367
3368 // Reserve 512K because most image files are going to be larger than the default
3369 // 1K that wxString reserves.
3370 data.reserve( 1 << 19 );
3371
3372 while( token != T_RIGHT )
3373 {
3374 if( !IsSymbol( token ) )
3375 Expecting( "base64 image data" );
3376
3377 data += FromUTF8();
3378 token = NextTok();
3379 }
3380
3381 wxMemoryBuffer buffer = wxBase64Decode( data );
3382
3383 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
3384 if( !refImage.ReadImageFile( buffer ) )
3385 THROW_IO_ERROR( _( "Failed to read image data." ) );
3386
3387 break;
3388 }
3389
3390 case T_locked:
3391 {
3392 // This has only ever been (locked yes) format
3393 const bool locked = parseBool();
3394 bitmap->SetLocked( locked );
3395
3396 NeedRIGHT();
3397 break;
3398 }
3399
3400 case T_uuid:
3401 {
3402 NextTok();
3403 const_cast<KIID&>( bitmap->m_Uuid ) = CurStrToKIID();
3404 NeedRIGHT();
3405 break;
3406 }
3407
3408 default:
3409 Expecting( "at, layer, scale, data, locked or uuid" );
3410 }
3411 }
3412
3413 return bitmap.release();
3414}
3415
3416
3418{
3419 wxCHECK_MSG( CurTok() == T_gr_text || CurTok() == T_fp_text, nullptr,
3420 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TEXT." ) );
3421
3422 FOOTPRINT* parentFP = dynamic_cast<FOOTPRINT*>( aParent );
3423 std::unique_ptr<PCB_TEXT> text;
3424
3425 T token = NextTok();
3426
3427 // If a base text is provided, we have a derived text already parsed and just need to update it
3428 if( aBaseText )
3429 {
3430 text = std::unique_ptr<PCB_TEXT>( aBaseText );
3431 }
3432 else if( parentFP )
3433 {
3434 switch( token )
3435 {
3436 case T_reference:
3437 text = std::make_unique<PCB_FIELD>( parentFP, FIELD_T::REFERENCE );
3438 break;
3439
3440 case T_value:
3441 text = std::make_unique<PCB_FIELD>( parentFP, FIELD_T::VALUE );
3442 break;
3443
3444 case T_user:
3445 text = std::make_unique<PCB_TEXT>( parentFP );
3446 break;
3447
3448 default:
3449 THROW_IO_ERROR( wxString::Format( _( "Cannot handle footprint text type %s" ),
3450 FromUTF8() ) );
3451 }
3452
3453 token = NextTok();
3454 }
3455 else
3456 {
3457 text = std::make_unique<PCB_TEXT>( aParent );
3458 }
3459
3460 // Legacy bare locked token
3461 if( token == T_locked )
3462 {
3463 text->SetLocked( true );
3464 token = NextTok();
3465 }
3466
3467 if( !IsSymbol( token ) && (int) token != DSN_NUMBER )
3468 Expecting( "text value" );
3469
3470 wxString value = FromUTF8();
3471 value.Replace( wxT( "%V" ), wxT( "${VALUE}" ) );
3472 value.Replace( wxT( "%R" ), wxT( "${REFERENCE}" ) );
3473 text->SetText( value );
3474
3475 NeedLEFT();
3476
3477 parsePCB_TEXT_effects( text.get(), aBaseText );
3478
3479 if( parentFP )
3480 {
3481 // Convert hidden footprint text (which is no longer supported) into a hidden field
3482 if( !text->IsVisible() && text->Type() == PCB_TEXT_T )
3483 return new PCB_FIELD( text.get(), FIELD_T::USER );
3484 }
3485 else
3486 {
3487 // Hidden PCB text is no longer supported
3488 text->SetVisible( true );
3489 }
3490
3491 return text.release();
3492}
3493
3494
3496{
3497 FOOTPRINT* parentFP = dynamic_cast<FOOTPRINT*>( aText->GetParent() );
3498 bool hasAngle = false; // Old files do not have a angle specified.
3499 // in this case it is 0 expected to be 0
3500 bool hasPos = false;
3501
3502 // By default, texts in footprints have a locked rotation (i.e. rot = -90 ... 90 deg)
3503 if( parentFP )
3504 aText->SetKeepUpright( true );
3505
3506 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
3507 {
3508 if( token == T_LEFT )
3509 token = NextTok();
3510
3511 switch( token )
3512 {
3513 case T_at:
3514 {
3515 VECTOR2I pt;
3516
3517 hasPos = true;
3518 pt.x = parseBoardUnits( "X coordinate" );
3519 pt.y = parseBoardUnits( "Y coordinate" );
3520 aText->SetTextPos( pt );
3521 token = NextTok();
3522
3523 if( CurTok() == T_NUMBER )
3524 {
3526 hasAngle = true;
3527 token = NextTok();
3528 }
3529
3530 // Legacy location of this token; presence implies true
3531 if( parentFP && CurTok() == T_unlocked )
3532 {
3533 aText->SetKeepUpright( false );
3534 token = NextTok();
3535 }
3536
3537 if( (int) token != DSN_RIGHT )
3538 Expecting( DSN_RIGHT );
3539
3540 break;
3541 }
3542
3543 case T_layer:
3544 aText->SetLayer( parseBoardItemLayer() );
3545
3546 token = NextTok();
3547
3548 if( token == T_knockout )
3549 {
3550 aText->SetIsKnockout( true );
3551 token = NextTok();
3552 }
3553
3554 if( (int) token != DSN_RIGHT )
3555 Expecting( DSN_RIGHT );
3556
3557 break;
3558
3559 case T_tstamp:
3560 case T_uuid:
3561 NextTok();
3562 const_cast<KIID&>( aText->m_Uuid ) = CurStrToKIID();
3563 NeedRIGHT();
3564 break;
3565
3566 case T_hide:
3567 {
3568 // In older files, the hide token appears bare, and indicates hide==true.
3569 // In newer files, it will be an explicit bool in a list like (hide yes)
3570 bool hide = parseMaybeAbsentBool( true );
3571
3572 if( parentFP )
3573 aText->SetVisible( !hide );
3574 else
3575 Expecting( "layer, effects, locked, render_cache, uuid or tstamp" );
3576
3577 break;
3578 }
3579
3580 case T_locked:
3581 // Newer list-enclosed locked
3582 aText->SetLocked( parseBool() );
3583 NeedRIGHT();
3584 break;
3585
3586 // Confusingly, "unlocked" is not the opposite of "locked", but refers to "keep upright"
3587 case T_unlocked:
3588 if( parentFP )
3589 aText->SetKeepUpright( !parseBool() );
3590 else
3591 Expecting( "layer, effects, locked, render_cache or tstamp" );
3592
3593 NeedRIGHT();
3594 break;
3595
3596 case T_effects:
3597 parseEDA_TEXT( static_cast<EDA_TEXT*>( aText ) );
3598 break;
3599
3600 case T_render_cache:
3601 parseRenderCache( static_cast<EDA_TEXT*>( aText ) );
3602 break;
3603
3604 default:
3605 if( parentFP )
3606 Expecting( "layer, hide, effects, locked, render_cache or tstamp" );
3607 else
3608 Expecting( "layer, effects, locked, render_cache or tstamp" );
3609 }
3610 }
3611
3612 // If there is no orientation defined, then it is the default value of 0 degrees.
3613 if( !hasAngle )
3614 aText->SetTextAngle( ANGLE_0 );
3615
3616 if( parentFP && !dynamic_cast<PCB_DIMENSION_BASE*>( aBaseText ) )
3617 {
3618 // make PCB_TEXT rotation relative to the parent footprint.
3619 // It was read as absolute rotation from file
3620 // Note: this is not rue for PCB_DIMENSION items that use the board
3621 // coordinates
3622 aText->SetTextAngle( aText->GetTextAngle() - parentFP->GetOrientation() );
3623
3624 // Move and rotate the text to its board coordinates
3625 aText->Rotate( { 0, 0 }, parentFP->GetOrientation() );
3626
3627 // Only move offset from parent position if we read a position from the file.
3628 // These positions are relative to the parent footprint. If we don't have a position
3629 // then the text defaults to the parent position and moving again will double it.
3630 if (hasPos)
3631 aText->Move( parentFP->GetPosition() );
3632 }
3633}
3634
3635
3637{
3638 wxCHECK_MSG( CurTok() == T_gr_text_box || CurTok() == T_fp_text_box, nullptr,
3639 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TEXTBOX." ) );
3640
3641 std::unique_ptr<PCB_TEXTBOX> textbox = std::make_unique<PCB_TEXTBOX>( aParent );
3642
3643 parseTextBoxContent( textbox.get() );
3644
3645 return textbox.release();
3646}
3647
3648
3650{
3651 wxCHECK_MSG( CurTok() == T_table_cell, nullptr,
3652 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table cell." ) );
3653
3654 std::unique_ptr<PCB_TABLECELL> cell = std::make_unique<PCB_TABLECELL>( aParent );
3655
3656 parseTextBoxContent( cell.get() );
3657
3658 return cell.release();
3659}
3660
3661
3663{
3664 int left;
3665 int top;
3666 int right;
3667 int bottom;
3668 STROKE_PARAMS stroke( -1, LINE_STYLE::SOLID );
3669 bool foundMargins = false;
3670
3671 T token = NextTok();
3672
3673 // Legacy locked
3674 if( token == T_locked )
3675 {
3676 aTextBox->SetLocked( true );
3677 token = NextTok();
3678 }
3679
3680 if( !IsSymbol( token ) && (int) token != DSN_NUMBER )
3681 Expecting( "text value" );
3682
3683 aTextBox->SetText( FromUTF8() );
3684
3685 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3686 {
3687 if( token != T_LEFT )
3688 Expecting( T_LEFT );
3689
3690 token = NextTok();
3691
3692 switch( token )
3693 {
3694 case T_locked:
3695 aTextBox->SetLocked( parseMaybeAbsentBool( true ) );
3696 break;
3697
3698 case T_start:
3699 {
3700 int x = parseBoardUnits( "X coordinate" );
3701 int y = parseBoardUnits( "Y coordinate" );
3702 aTextBox->SetStart( VECTOR2I( x, y ) );
3703 NeedRIGHT();
3704
3705 NeedLEFT();
3706 token = NextTok();
3707
3708 if( token != T_end )
3709 Expecting( T_end );
3710
3711 x = parseBoardUnits( "X coordinate" );
3712 y = parseBoardUnits( "Y coordinate" );
3713 aTextBox->SetEnd( VECTOR2I( x, y ) );
3714 NeedRIGHT();
3715 break;
3716 }
3717
3718 case T_pts:
3719 {
3720 aTextBox->SetShape( SHAPE_T::POLY );
3721 aTextBox->GetPolyShape().RemoveAllContours();
3722 aTextBox->GetPolyShape().NewOutline();
3723
3724 while( (token = NextTok() ) != T_RIGHT )
3725 parseOutlinePoints( aTextBox->GetPolyShape().Outline( 0 ) );
3726
3727 break;
3728 }
3729
3730 case T_angle:
3731 // Set the angle of the text only, the coordinates of the box (a polygon) are
3732 // already at the right position, and must not be rotated
3733 aTextBox->EDA_TEXT::SetTextAngle( EDA_ANGLE( parseDouble( "text box angle" ), DEGREES_T ) );
3734 NeedRIGHT();
3735 break;
3736
3737 case T_stroke:
3738 {
3739 STROKE_PARAMS_PARSER strokeParser( reader, pcbIUScale.IU_PER_MM );
3740 strokeParser.SyncLineReaderWith( *this );
3741
3742 strokeParser.ParseStroke( stroke );
3743 SyncLineReaderWith( strokeParser );
3744 break;
3745 }
3746
3747 case T_border:
3748 aTextBox->SetBorderEnabled( parseBool() );
3749 NeedRIGHT();
3750 break;
3751
3752 case T_margins:
3753 parseMargins( left, top, right, bottom );
3754 aTextBox->SetMarginLeft( left );
3755 aTextBox->SetMarginTop( top );
3756 aTextBox->SetMarginRight( right );
3757 aTextBox->SetMarginBottom( bottom );
3758 foundMargins = true;
3759 NeedRIGHT();
3760 break;
3761
3762 case T_layer:
3763 aTextBox->SetLayer( parseBoardItemLayer() );
3764 NeedRIGHT();
3765 break;
3766
3767 case T_knockout:
3768 if( [[maybe_unused]] PCB_TABLECELL* cell = dynamic_cast<PCB_TABLECELL*>( aTextBox ) )
3769 {
3770 Expecting( "locked, start, pts, angle, width, margins, layer, effects, span, "
3771 "render_cache, uuid or tstamp" );
3772 }
3773 else
3774 {
3775 aTextBox->SetIsKnockout( parseBool() );
3776 }
3777
3778 NeedRIGHT();
3779 break;
3780
3781 case T_span:
3782 if( PCB_TABLECELL* cell = dynamic_cast<PCB_TABLECELL*>( aTextBox ) )
3783 {
3784 cell->SetColSpan( parseInt( "column span" ) );
3785 cell->SetRowSpan( parseInt( "row span" ) );
3786 }
3787 else
3788 {
3789 Expecting( "locked, start, pts, angle, width, stroke, border, margins, knockout, "
3790 "layer, effects, render_cache, uuid or tstamp" );
3791 }
3792
3793 NeedRIGHT();
3794 break;
3795
3796 case T_tstamp:
3797 case T_uuid:
3798 NextTok();
3799 const_cast<KIID&>( aTextBox->m_Uuid ) = CurStrToKIID();
3800 NeedRIGHT();
3801 break;
3802
3803 case T_effects:
3804 parseEDA_TEXT( static_cast<EDA_TEXT*>( aTextBox ) );
3805 break;
3806
3807 case T_render_cache:
3808 parseRenderCache( static_cast<EDA_TEXT*>( aTextBox ) );
3809 break;
3810
3811 default:
3812 if( dynamic_cast<PCB_TABLECELL*>( aTextBox ) != nullptr )
3813 {
3814 Expecting( "locked, start, pts, angle, width, margins, layer, effects, span, "
3815 "render_cache, uuid or tstamp" );
3816 }
3817 else
3818 {
3819 Expecting( "locked, start, pts, angle, width, stroke, border, margins, knockout,"
3820 "layer, effects, render_cache, uuid or tstamp" );
3821 }
3822 }
3823 }
3824
3825 aTextBox->SetStroke( stroke );
3826
3827 if( m_requiredVersion < 20230825 ) // compat, we move to an explicit flag
3828 aTextBox->SetBorderEnabled( stroke.GetWidth() >= 0 );
3829
3830 if( !foundMargins )
3831 {
3832 int margin = aTextBox->GetLegacyTextMargin();
3833 aTextBox->SetMarginLeft( margin );
3834 aTextBox->SetMarginTop( margin );
3835 aTextBox->SetMarginRight( margin );
3836 aTextBox->SetMarginBottom( margin );
3837 }
3838
3839 if( FOOTPRINT* parentFP = aTextBox->GetParentFootprint() )
3840 {
3841 aTextBox->Rotate( { 0, 0 }, parentFP->GetOrientation() );
3842 aTextBox->Move( parentFP->GetPosition() );
3843 }
3844}
3845
3846
3848{
3849 wxCHECK_MSG( CurTok() == T_table, nullptr,
3850 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table." ) );
3851
3852 T token;
3853 STROKE_PARAMS borderStroke( -1, LINE_STYLE::SOLID );
3854 STROKE_PARAMS separatorsStroke( -1, LINE_STYLE::SOLID );
3855 std::unique_ptr<PCB_TABLE> table = std::make_unique<PCB_TABLE>( aParent, -1 );
3856
3857 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3858 {
3859 if( token != T_LEFT )
3860 Expecting( T_LEFT );
3861
3862 token = NextTok();
3863
3864 switch( token )
3865 {
3866 case T_column_count:
3867 table->SetColCount( parseInt( "column count" ) );
3868 NeedRIGHT();
3869 break;
3870
3871 case T_locked:
3872 table->SetLocked( parseBool() );
3873 NeedRIGHT();
3874 break;
3875
3876 case T_angle: // legacy token no longer used
3877 NeedRIGHT();
3878 break;
3879
3880 case T_layer:
3881 table->SetLayer( parseBoardItemLayer() );
3882 NeedRIGHT();
3883 break;
3884
3885 case T_column_widths:
3886 {
3887 int col = 0;
3888
3889 while( ( token = NextTok() ) != T_RIGHT )
3890 table->SetColWidth( col++, parseBoardUnits() );
3891
3892 break;
3893 }
3894
3895 case T_row_heights:
3896 {
3897 int row = 0;
3898
3899 while( ( token = NextTok() ) != T_RIGHT )
3900 table->SetRowHeight( row++, parseBoardUnits() );
3901
3902 break;
3903 }
3904
3905 case T_cells:
3906 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3907 {
3908 if( token != T_LEFT )
3909 Expecting( T_LEFT );
3910
3911 token = NextTok();
3912
3913 if( token != T_table_cell )
3914 Expecting( "table_cell" );
3915
3916 table->AddCell( parsePCB_TABLECELL( table.get() ) );
3917 }
3918
3919 break;
3920
3921 case T_border:
3922 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3923 {
3924 if( token != T_LEFT )
3925 Expecting( T_LEFT );
3926
3927 token = NextTok();
3928
3929 switch( token )
3930 {
3931 case T_external:
3932 table->SetStrokeExternal( parseBool() );
3933 NeedRIGHT();
3934 break;
3935
3936 case T_header:
3937 table->SetStrokeHeaderSeparator( parseBool() );
3938 NeedRIGHT();
3939 break;
3940
3941 case T_stroke:
3942 {
3943 STROKE_PARAMS_PARSER strokeParser( reader, pcbIUScale.IU_PER_MM );
3944 strokeParser.SyncLineReaderWith( *this );
3945
3946 strokeParser.ParseStroke( borderStroke );
3947 SyncLineReaderWith( strokeParser );
3948
3949 table->SetBorderStroke( borderStroke );
3950 break;
3951 }
3952
3953 default:
3954 Expecting( "external, header or stroke" );
3955 break;
3956 }
3957 }
3958
3959 break;
3960
3961 case T_separators:
3962 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3963 {
3964 if( token != T_LEFT )
3965 Expecting( T_LEFT );
3966
3967 token = NextTok();
3968
3969 switch( token )
3970 {
3971 case T_rows:
3972 table->SetStrokeRows( parseBool() );
3973 NeedRIGHT();
3974 break;
3975
3976 case T_cols:
3977 table->SetStrokeColumns( parseBool() );
3978 NeedRIGHT();
3979 break;
3980
3981 case T_stroke:
3982 {
3983 STROKE_PARAMS_PARSER strokeParser( reader, pcbIUScale.IU_PER_MM );
3984 strokeParser.SyncLineReaderWith( *this );
3985
3986 strokeParser.ParseStroke( separatorsStroke );
3987 SyncLineReaderWith( strokeParser );
3988
3989 table->SetSeparatorsStroke( separatorsStroke );
3990 break;
3991 }
3992
3993 default:
3994 Expecting( "rows, cols, or stroke" );
3995 break;
3996 }
3997 }
3998
3999 break;
4000
4001 default:
4002 Expecting( "columns, layer, col_widths, row_heights, border, separators, header or "
4003 "cells" );
4004 }
4005 }
4006
4007 return table.release();
4008}
4009
4010
4012{
4013 wxCHECK_MSG( CurTok() == T_dimension, nullptr,
4014 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as DIMENSION." ) );
4015
4016 T token;
4017 bool locked = false;
4018 std::unique_ptr<PCB_DIMENSION_BASE> dim;
4019
4020 token = NextTok();
4021
4022 // Free 'locked' token from 6.0/7.0 formats
4023 if( token == T_locked )
4024 {
4025 locked = true;
4026 token = NextTok();
4027 }
4028
4029 // skip value that used to be saved
4030 if( token != T_LEFT )
4031 NeedLEFT();
4032
4033 token = NextTok();
4034
4035 bool isLegacyDimension = false;
4036 bool isStyleKnown = false;
4037
4038 // Old format
4039 if( token == T_width )
4040 {
4041 isLegacyDimension = true;
4042 dim = std::make_unique<PCB_DIM_ALIGNED>( aParent );
4043 dim->SetLineThickness( parseBoardUnits( "dimension width value" ) );
4044 NeedRIGHT();
4045 }
4046 else
4047 {
4048 if( token != T_type )
4049 Expecting( T_type );
4050
4051 switch( NextTok() )
4052 {
4053 case T_aligned: dim = std::make_unique<PCB_DIM_ALIGNED>( aParent ); break;
4054 case T_orthogonal: dim = std::make_unique<PCB_DIM_ORTHOGONAL>( aParent ); break;
4055 case T_leader: dim = std::make_unique<PCB_DIM_LEADER>( aParent ); break;
4056 case T_center: dim = std::make_unique<PCB_DIM_CENTER>( aParent ); break;
4057 case T_radial: dim = std::make_unique<PCB_DIM_RADIAL>( aParent ); break;
4058 default: wxFAIL_MSG( wxT( "Cannot parse unknown dimension type " )
4059 + GetTokenString( CurTok() ) );
4060 }
4061
4062 NeedRIGHT();
4063
4064 // Before parsing further, set default properites for old KiCad file
4065 // versions that didnt have these properties:
4066 dim->SetArrowDirection( DIM_ARROW_DIRECTION::OUTWARD );
4067 }
4068
4069 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4070 {
4071 if( token != T_LEFT )
4072 Expecting( T_LEFT );
4073
4074 token = NextTok();
4075
4076 switch( token )
4077 {
4078 case T_layer:
4079 dim->SetLayer( parseBoardItemLayer() );
4080 NeedRIGHT();
4081 break;
4082
4083 case T_tstamp:
4084 case T_uuid:
4085 NextTok();
4086 const_cast<KIID&>( dim->m_Uuid ) = CurStrToKIID();
4087 NeedRIGHT();
4088 break;
4089
4090 case T_gr_text:
4091 {
4092 // In old pcb files, when parsing the text we do not yet know
4093 // if the text is kept aligned or not, and its DIM_TEXT_POSITION option.
4094 // Leave the text not aligned for now to read the text angle, and no
4095 // constraint for DIM_TEXT_POSITION in this case.
4096 // It will be set aligned (or not) later
4097 bool is_aligned = dim->GetKeepTextAligned();
4098 DIM_TEXT_POSITION t_dim_pos = dim->GetTextPositionMode();
4099
4100 if( !isStyleKnown )
4101 {
4102 dim->SetTextPositionMode( DIM_TEXT_POSITION::MANUAL );
4103 dim->SetKeepTextAligned( false );
4104 }
4105
4106 parsePCB_TEXT( m_board, dim.get() );
4107
4108 if( isLegacyDimension )
4109 {
4110 EDA_UNITS units = EDA_UNITS::MM;
4111
4112 if( !EDA_UNIT_UTILS::FetchUnitsFromString( dim->GetText(), units ) )
4113 dim->SetAutoUnits( true ); //Not determined => use automatic units
4114
4115 dim->SetUnits( units );
4116 }
4117
4118 if( !isStyleKnown )
4119 {
4120 dim->SetKeepTextAligned( is_aligned );
4121 dim->SetTextPositionMode( t_dim_pos );
4122 }
4123 break;
4124 }
4125
4126 // New format: feature points
4127 case T_pts:
4128 {
4129 VECTOR2I point;
4130
4131 parseXY( &point.x, &point.y );
4132 dim->SetStart( point );
4133 parseXY( &point.x, &point.y );
4134 dim->SetEnd( point );
4135
4136 NeedRIGHT();
4137 break;
4138 }
4139
4140 case T_height:
4141 {
4142 int height = parseBoardUnits( "dimension height value" );
4143 NeedRIGHT();
4144
4145 if( dim->Type() == PCB_DIM_ORTHOGONAL_T || dim->Type() == PCB_DIM_ALIGNED_T )
4146 {
4147 PCB_DIM_ALIGNED* aligned = static_cast<PCB_DIM_ALIGNED*>( dim.get() );
4148 aligned->SetHeight( height );
4149 }
4150
4151 break;
4152 }
4153
4154 case T_leader_length:
4155 {
4156 int length = parseBoardUnits( "leader length value" );
4157 NeedRIGHT();
4158
4159 if( dim->Type() == PCB_DIM_RADIAL_T )
4160 {
4161 PCB_DIM_RADIAL* radial = static_cast<PCB_DIM_RADIAL*>( dim.get() );
4162 radial->SetLeaderLength( length );
4163 }
4164
4165 break;
4166 }
4167
4168 case T_orientation:
4169 {
4170 int orientation = parseInt( "orthogonal dimension orientation" );
4171 NeedRIGHT();
4172
4173 if( dim->Type() == PCB_DIM_ORTHOGONAL_T )
4174 {
4175 PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dim.get() );
4176 orientation = std::clamp( orientation, 0, 1 );
4177 ortho->SetOrientation( static_cast<PCB_DIM_ORTHOGONAL::DIR>( orientation ) );
4178 }
4179
4180 break;
4181 }
4182
4183 case T_format:
4184 {
4185 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4186 {
4187 switch( token )
4188 {
4189 case T_LEFT:
4190 continue;
4191
4192 case T_prefix:
4193 NeedSYMBOLorNUMBER();
4194 dim->SetPrefix( FromUTF8() );
4195 NeedRIGHT();
4196 break;
4197
4198 case T_suffix:
4199 NeedSYMBOLorNUMBER();
4200 dim->SetSuffix( FromUTF8() );
4201 NeedRIGHT();
4202 break;
4203
4204 case T_units:
4205 {
4206 int mode = parseInt( "dimension units mode" );
4207 mode = std::max( 0, std::min( 4, mode ) );
4208 dim->SetUnitsMode( static_cast<DIM_UNITS_MODE>( mode ) );
4209 NeedRIGHT();
4210 break;
4211 }
4212
4213 case T_units_format:
4214 {
4215 int format = parseInt( "dimension units format" );
4216 format = std::clamp( format, 0, 3 );
4217 dim->SetUnitsFormat( static_cast<DIM_UNITS_FORMAT>( format ) );
4218 NeedRIGHT();
4219 break;
4220 }
4221
4222 case T_precision:
4223 dim->SetPrecision( static_cast<DIM_PRECISION>( parseInt( "dimension precision" ) ) );
4224 NeedRIGHT();
4225 break;
4226
4227 case T_override_value:
4228 NeedSYMBOLorNUMBER();
4229 dim->SetOverrideTextEnabled( true );
4230 dim->SetOverrideText( FromUTF8() );
4231 NeedRIGHT();
4232 break;
4233
4234 case T_suppress_zeroes:
4235 dim->SetSuppressZeroes( parseMaybeAbsentBool( true ) );
4236 break;
4237
4238 default:
4239 std::cerr << "Unknown format token: " << GetTokenString( token ) << std::endl;
4240 Expecting( "prefix, suffix, units, units_format, precision, override_value, "
4241 "suppress_zeroes" );
4242 }
4243 }
4244 break;
4245 }
4246
4247 case T_style:
4248 {
4249 isStyleKnown = true;
4250
4251 // new format: default to keep text aligned off unless token is present
4252 dim->SetKeepTextAligned( false );
4253
4254 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4255 {
4256 switch( token )
4257 {
4258 case T_LEFT:
4259 continue;
4260
4261 case T_thickness:
4262 dim->SetLineThickness( parseBoardUnits( "extension line thickness value" ) );
4263 NeedRIGHT();
4264 break;
4265
4266 case T_arrow_direction:
4267 {
4268 token = NextTok();
4269
4270 if( token == T_inward )
4271 dim->ChangeArrowDirection( DIM_ARROW_DIRECTION::INWARD );
4272 else if( token == T_outward )
4273 dim->ChangeArrowDirection( DIM_ARROW_DIRECTION::OUTWARD );
4274 else
4275 Expecting( "inward or outward" );
4276
4277 NeedRIGHT();
4278 break;
4279 }
4280 case T_arrow_length:
4281
4282 dim->SetArrowLength( parseBoardUnits( "arrow length value" ) );
4283 NeedRIGHT();
4284 break;
4285
4286 case T_text_position_mode:
4287 {
4288 int mode = parseInt( "text position mode" );
4289 mode = std::max( 0, std::min( 3, mode ) );
4290 dim->SetTextPositionMode( static_cast<DIM_TEXT_POSITION>( mode ) );
4291 NeedRIGHT();
4292 break;
4293 }
4294
4295 case T_extension_height:
4296 {
4297 PCB_DIM_ALIGNED* aligned = dynamic_cast<PCB_DIM_ALIGNED*>( dim.get() );
4298 wxCHECK_MSG( aligned, nullptr, wxT( "Invalid extension_height token" ) );
4299 aligned->SetExtensionHeight( parseBoardUnits( "extension height value" ) );
4300 NeedRIGHT();
4301 break;
4302 }
4303
4304 case T_extension_offset:
4305 dim->SetExtensionOffset( parseBoardUnits( "extension offset value" ) );
4306 NeedRIGHT();
4307 break;
4308
4309 case T_keep_text_aligned:
4310 dim->SetKeepTextAligned( parseMaybeAbsentBool( true ) );
4311 break;
4312
4313 case T_text_frame:
4314 {
4315 wxCHECK_MSG( dim->Type() == PCB_DIM_LEADER_T, nullptr,
4316 wxT( "Invalid text_frame token" ) );
4317
4318 PCB_DIM_LEADER* leader = static_cast<PCB_DIM_LEADER*>( dim.get() );
4319
4320 int textFrame = parseInt( "text frame mode" );
4321 textFrame = std::clamp( textFrame, 0, 3 );
4322 leader->SetTextBorder( static_cast<DIM_TEXT_BORDER>( textFrame ));
4323 NeedRIGHT();
4324 break;
4325 }
4326
4327 default:
4328 Expecting( "thickness, arrow_length, arrow_direction, text_position_mode, "
4329 "extension_height, extension_offset" );
4330 }
4331 }
4332
4333 break;
4334 }
4335
4336 // Old format: feature1 stores a feature line. We only care about the origin.
4337 case T_feature1:
4338 {
4339 NeedLEFT();
4340 token = NextTok();
4341
4342 if( token != T_pts )
4343 Expecting( T_pts );
4344
4345 VECTOR2I point;
4346
4347 parseXY( &point.x, &point.y );
4348 dim->SetStart( point );
4349
4350 parseXY( nullptr, nullptr ); // Ignore second point
4351 NeedRIGHT();
4352 NeedRIGHT();
4353 break;
4354 }
4355
4356 // Old format: feature2 stores a feature line. We only care about the end point.
4357 case T_feature2:
4358 {
4359 NeedLEFT();
4360 token = NextTok();
4361
4362 if( token != T_pts )
4363 Expecting( T_pts );
4364
4365 VECTOR2I point;
4366
4367 parseXY( &point.x, &point.y );
4368 dim->SetEnd( point );
4369
4370 parseXY( nullptr, nullptr ); // Ignore second point
4371
4372 NeedRIGHT();
4373 NeedRIGHT();
4374 break;
4375 }
4376
4377 case T_crossbar:
4378 {
4379 NeedLEFT();
4380 token = NextTok();
4381
4382 if( token == T_pts )
4383 {
4384 // If we have a crossbar, we know we're an old aligned dim
4385 PCB_DIM_ALIGNED* aligned = static_cast<PCB_DIM_ALIGNED*>( dim.get() );
4386
4387 // Old style: calculate height from crossbar
4388 VECTOR2I point1, point2;
4389 parseXY( &point1.x, &point1.y );
4390 parseXY( &point2.x, &point2.y );
4391 aligned->UpdateHeight( point2, point1 ); // Yes, backwards intentionally
4392 NeedRIGHT();
4393 }
4394
4395 NeedRIGHT();
4396 break;
4397 }
4398
4399 // Arrow: no longer saved; no-op
4400 case T_arrow1a:
4401 NeedLEFT();
4402 token = NextTok();
4403
4404 if( token != T_pts )
4405 Expecting( T_pts );
4406
4407 parseXY( nullptr, nullptr );
4408 parseXY( nullptr, nullptr );
4409 NeedRIGHT();
4410 NeedRIGHT();
4411 break;
4412
4413 // Arrow: no longer saved; no-op
4414 case T_arrow1b:
4415 NeedLEFT();
4416 token = NextTok();
4417
4418 if( token != T_pts )
4419 Expecting( T_pts );
4420
4421 parseXY( nullptr, nullptr );
4422 parseXY( nullptr, nullptr );
4423 NeedRIGHT();
4424 NeedRIGHT();
4425 break;
4426
4427 // Arrow: no longer saved; no-op
4428 case T_arrow2a:
4429 NeedLEFT();
4430 token = NextTok();
4431
4432 if( token != T_pts )
4433 Expecting( T_pts );
4434
4435 parseXY( nullptr, nullptr );
4436 parseXY( nullptr, nullptr );
4437 NeedRIGHT();
4438 NeedRIGHT();
4439 break;
4440
4441 // Arrow: no longer saved; no-op
4442 case T_arrow2b:
4443 NeedLEFT();
4444 token = NextTok();
4445
4446 if( token != T_pts )
4447 Expecting( T_pts );
4448
4449 parseXY( nullptr, nullptr );
4450 parseXY( nullptr, nullptr );
4451 NeedRIGHT();
4452 NeedRIGHT();
4453 break;
4454
4455 // Handle (locked yes) from modern times
4456 case T_locked:
4457 {
4458 // Unsure if we ever wrote out (locked) for dimensions, so use maybeAbsent just in case
4459 bool isLocked = parseMaybeAbsentBool( true );
4460 dim->SetLocked( isLocked );
4461 break;
4462 }
4463
4464 default:
4465 Expecting( "layer, tstamp, uuid, gr_text, feature1, feature2, crossbar, arrow1a, "
4466 "arrow1b, arrow2a, or arrow2b" );
4467 }
4468 }
4469
4470 if( locked )
4471 dim->SetLocked( true );
4472
4473 dim->Update();
4474
4475 return dim.release();
4476}
4477
4478
4479FOOTPRINT* PCB_IO_KICAD_SEXPR_PARSER::parseFOOTPRINT( wxArrayString* aInitialComments )
4480{
4481 try
4482 {
4483 return parseFOOTPRINT_unchecked( aInitialComments );
4484 }
4485 catch( const PARSE_ERROR& parse_error )
4486 {
4487 if( m_tooRecent )
4488 throw FUTURE_FORMAT_ERROR( parse_error, GetRequiredVersion() );
4489 else
4490 throw;
4491 }
4492}
4493
4494
4496{
4497 wxCHECK_MSG( CurTok() == T_module || CurTok() == T_footprint, nullptr,
4498 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as FOOTPRINT." ) );
4499
4500 wxString name;
4501 VECTOR2I pt;
4502 T token;
4503 LIB_ID fpid;
4504 int attributes = 0;
4505
4506 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
4507
4508 footprint->SetInitialComments( aInitialComments );
4509
4510 if( m_board )
4511 {
4512 footprint->SetStaticComponentClass(
4514 }
4515
4516 token = NextTok();
4517
4518 if( !IsSymbol( token ) && token != T_NUMBER )
4519 Expecting( "symbol|number" );
4520
4521 name = FromUTF8();
4522
4523 if( !name.IsEmpty() && fpid.Parse( name, true ) >= 0 )
4524 {
4525 THROW_IO_ERROR( wxString::Format( _( "Invalid footprint ID in\nfile: %s\nline: %d\n"
4526 "offset: %d." ),
4527 CurSource(), CurLineNumber(), CurOffset() ) );
4528 }
4529
4530 auto checkVersion =
4531 [&]()
4532 {
4534 {
4535 throw FUTURE_FORMAT_ERROR( fmt::format( "{}", m_requiredVersion ),
4537 }
4538 };
4539
4540 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4541 {
4542 if( token == T_LEFT )
4543 token = NextTok();
4544
4545 switch( token )
4546 {
4547 case T_version:
4548 {
4549 // Theoretically a footprint nested in a PCB could declare its own version, though
4550 // as of writing this comment we don't do that. Just in case, take the greater
4551 // version.
4552 int this_version = parseInt( FromUTF8().mb_str( wxConvUTF8 ) );
4553 NeedRIGHT();
4554 m_requiredVersion = std::max( m_requiredVersion, this_version );
4556 footprint->SetFileFormatVersionAtLoad( this_version );
4557 break;
4558 }
4559
4560 case T_generator:
4561 // We currently ignore the generator when parsing. It is included in the file for manual
4562 // indication of where the footprint came from.
4563 NeedSYMBOL();
4564 NeedRIGHT();
4565 break;
4566
4567 case T_generator_version:
4568 {
4569 NeedSYMBOL();
4570 m_generatorVersion = FromUTF8();
4571 NeedRIGHT();
4572
4573 // If the format includes a generator version, by this point we have enough info to
4574 // do the version check here
4575 checkVersion();
4576
4577 break;
4578 }
4579
4580 case T_locked:
4581 footprint->SetLocked( parseMaybeAbsentBool( true ) );
4582 break;
4583
4584 case T_placed:
4585 footprint->SetIsPlaced( parseMaybeAbsentBool( true ) );
4586 break;
4587
4588 case T_layer:
4589 {
4590 // Footprints can be only on the front side or the back side.
4591 // but because we can find some stupid layer in file, ensure a
4592 // acceptable layer is set for the footprint
4594 footprint->SetLayer( layer == B_Cu ? B_Cu : F_Cu );
4595 NeedRIGHT();
4596 break;
4597 }
4598
4599 case T_tedit:
4600 parseHex();
4601 NeedRIGHT();
4602 break;
4603
4604 case T_tstamp:
4605 case T_uuid:
4606 NextTok();
4607 const_cast<KIID&>( footprint->m_Uuid ) = CurStrToKIID();
4608 NeedRIGHT();
4609 break;
4610
4611 case T_at:
4612 pt.x = parseBoardUnits( "X coordinate" );
4613 pt.y = parseBoardUnits( "Y coordinate" );
4614 footprint->SetPosition( pt );
4615 token = NextTok();
4616
4617 if( token == T_NUMBER )
4618 {
4619 footprint->SetOrientation( EDA_ANGLE( parseDouble(), DEGREES_T ) );
4620 NeedRIGHT();
4621 }
4622 else if( token != T_RIGHT )
4623 {
4624 Expecting( T_RIGHT );
4625 }
4626
4627 break;
4628
4629 case T_descr:
4630 NeedSYMBOLorNUMBER(); // some symbols can be 0508, so a number is also a symbol here
4631 footprint->SetLibDescription( FromUTF8() );
4632 NeedRIGHT();
4633 break;
4634
4635 case T_tags:
4636 NeedSYMBOLorNUMBER(); // some symbols can be 0508, so a number is also a symbol here
4637 footprint->SetKeywords( FromUTF8() );
4638 NeedRIGHT();
4639 break;
4640
4641 case T_property:
4642 {
4643 wxString value;
4644
4645 NeedSYMBOL();
4646 wxString pName = FromUTF8();
4647 NeedSYMBOL();
4648 wxString pValue = FromUTF8();
4649
4650 // Prior to PCB fields, we used to use properties for special values instead of
4651 // using (keyword_example "value")
4652 if( m_requiredVersion < 20230620 )
4653 {
4654 // Skip legacy non-field properties sent from symbols that should not be kept
4655 // in footprints.
4656 if( pName == "ki_keywords" || pName == "ki_locked" )
4657 {
4658 NeedRIGHT();
4659 break;
4660 }
4661
4662 // Description from symbol (not the fooprint library description stored in (descr) )
4663 // used to be stored as a reserved key value
4664 if( pName == "ki_description" )
4665 {
4666 footprint->GetField( FIELD_T::DESCRIPTION )->SetText( pValue );
4667 NeedRIGHT();
4668 break;
4669 }
4670
4671 // Sheet file and name used to be stored as properties invisible to the user
4672 if( pName == "Sheetfile" || pName == "Sheet file" )
4673 {
4674 footprint->SetSheetfile( pValue );
4675 NeedRIGHT();
4676 break;
4677 }
4678
4679 if( pName == "Sheetname" || pName == "Sheet name" )
4680 {
4681 footprint->SetSheetname( pValue );
4682 NeedRIGHT();
4683 break;
4684 }
4685 }
4686
4687 PCB_FIELD* field = nullptr;
4688 std::unique_ptr<PCB_FIELD> unusedField;
4689
4690 // 8.0.0rc3 had a bug where these properties were mistakenly added to the footprint as
4691 // fields, this will remove them as fields but still correctly set the footprint filters
4692 if( pName == "ki_fp_filters" )
4693 {
4694 footprint->SetFilters( pValue );
4695
4696 // Use the text effect parsing function because it will handle ki_fp_filters as a
4697 // property with no text effects, but will also handle parsing the text effects.
4698 // We just drop the effects if they're present.
4699 unusedField = std::make_unique<PCB_FIELD>( footprint.get(), FIELD_T::USER );
4700 field = unusedField.get();
4701 }
4702 else if( pName == "Footprint" )
4703 {
4704 // Until V9, footprints had a Footprint field that usually (but not always)
4705 // duplicated the footprint's LIB_ID. In V9 this was removed. Parse it
4706 // like any other, but don't add it to anything.
4707 unusedField = std::make_unique<PCB_FIELD>( footprint.get(), FIELD_T::FOOTPRINT );
4708 field = unusedField.get();
4709 }
4710 else if( footprint->HasField( pName ) )
4711 {
4712 field = footprint->GetField( pName );
4713 field->SetText( pValue );
4714 }
4715 else
4716 {
4717 field = new PCB_FIELD( footprint.get(), FIELD_T::USER, pName );
4718 footprint->Add( field );
4719
4720 field->SetText( pValue );
4721 field->SetLayer( footprint->GetLayer() == F_Cu ? F_Fab : B_Fab );
4722
4723 if( m_board ) // can be null when reading a lib
4725 }
4726
4727 // Hide the field by default if it is a legacy field that did not have
4728 // text effects applied, since hide is a negative effect
4729 if( m_requiredVersion < 20230620 )
4730 field->SetVisible( false );
4731 else
4732 field->SetVisible( true );
4733
4734 parsePCB_TEXT_effects( field );
4735 }
4736 break;
4737
4738 case T_path:
4739 NeedSYMBOLorNUMBER(); // Paths can be numerical so a number is also a symbol here
4740 footprint->SetPath( KIID_PATH( FromUTF8() ) );
4741 NeedRIGHT();
4742 break;
4743
4744 case T_sheetname:
4745 NeedSYMBOL();
4746 footprint->SetSheetname( FromUTF8() );
4747 NeedRIGHT();
4748 break;
4749
4750 case T_sheetfile:
4751 NeedSYMBOL();
4752 footprint->SetSheetfile( FromUTF8() );
4753 NeedRIGHT();
4754 break;
4755
4756 case T_autoplace_cost90:
4757 case T_autoplace_cost180:
4758 parseInt( "legacy auto-place cost" );
4759 NeedRIGHT();
4760 break;
4761
4762 case T_private_layers:
4763 {
4764 LSET privateLayers;
4765
4766 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4767 {
4768 auto it = m_layerIndices.find( CurStr() );
4769
4770 if( it != m_layerIndices.end() )
4771 privateLayers.set( it->second );
4772 else
4773 Expecting( "layer name" );
4774 }
4775
4776 if( m_requiredVersion < 20220427 )
4777 {
4778 privateLayers.set( Edge_Cuts, false );
4779 privateLayers.set( Margin, false );
4780 }
4781
4782 footprint->SetPrivateLayers( privateLayers );
4783 break;
4784 }
4785
4786 case T_net_tie_pad_groups:
4787 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4788 footprint->AddNetTiePadGroup( CurStr() );
4789
4790 break;
4791
4792 case T_duplicate_pad_numbers_are_jumpers:
4793 footprint->SetDuplicatePadNumbersAreJumpers( parseBool() );
4794 NeedRIGHT();
4795 break;
4796
4797 case T_jumper_pad_groups:
4798 {
4799 // This should only be formatted if there is at least one group
4800 std::vector<std::set<wxString>>& groups = footprint->JumperPadGroups();
4801 std::set<wxString>* currentGroup = nullptr;
4802
4803 for( token = NextTok(); currentGroup || token != T_RIGHT; token = NextTok() )
4804 {
4805 switch( static_cast<int>( token ) )
4806 {
4807 case T_LEFT:
4808 currentGroup = &groups.emplace_back();
4809 break;
4810
4811 case DSN_STRING:
4812 currentGroup->insert( FromUTF8() );
4813 break;
4814
4815 case T_RIGHT:
4816 currentGroup = nullptr;
4817 break;
4818
4819 default:
4820 Expecting( "list of pad names" );
4821 }
4822 }
4823
4824 break;
4825 }
4826
4827 case T_solder_mask_margin:
4828 footprint->SetLocalSolderMaskMargin( parseBoardUnits( "local solder mask margin value" ) );
4829 NeedRIGHT();
4830
4831 // In pre-9.0 files "0" meant inherit.
4832 if( m_requiredVersion <= 20240201 && footprint->GetLocalSolderMaskMargin() == 0 )
4833 footprint->SetLocalSolderMaskMargin( {} );
4834
4835 break;
4836
4837 case T_solder_paste_margin:
4838 footprint->SetLocalSolderPasteMargin( parseBoardUnits( "local solder paste margin value" ) );
4839 NeedRIGHT();
4840
4841 // In pre-9.0 files "0" meant inherit.
4842 if( m_requiredVersion <= 20240201 && footprint->GetLocalSolderPasteMargin() == 0 )
4843 footprint->SetLocalSolderPasteMargin( {} );
4844
4845 break;
4846
4847 case T_solder_paste_ratio: // legacy token
4848 case T_solder_paste_margin_ratio:
4849 footprint->SetLocalSolderPasteMarginRatio( parseDouble( "local solder paste margin ratio value" ) );
4850 NeedRIGHT();
4851
4852 // In pre-9.0 files "0" meant inherit.
4853 if( m_requiredVersion <= 20240201 && footprint->GetLocalSolderPasteMarginRatio() == 0 )
4854 footprint->SetLocalSolderPasteMarginRatio( {} );
4855
4856 break;
4857
4858 case T_clearance:
4859 footprint->SetLocalClearance( parseBoardUnits( "local clearance value" ) );
4860 NeedRIGHT();
4861
4862 // In pre-9.0 files "0" meant inherit.
4863 if( m_requiredVersion <= 20240201 && footprint->GetLocalClearance() == 0 )
4864 footprint->SetLocalClearance( {} );
4865
4866 break;
4867
4868 case T_zone_connect:
4869 footprint->SetLocalZoneConnection((ZONE_CONNECTION) parseInt( "zone connection value" ) );
4870 NeedRIGHT();
4871 break;
4872
4873 case T_thermal_width:
4874 case T_thermal_gap:
4875 // Interestingly, these have never been exposed in the GUI
4876 parseBoardUnits( token );
4877 NeedRIGHT();
4878 break;
4879
4880 case T_attr:
4881 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4882 {
4883 switch( token )
4884 {
4885 case T_virtual: // legacy token prior to version 20200826
4887 break;
4888
4889 case T_through_hole:
4890 attributes |= FP_THROUGH_HOLE;
4891 break;
4892
4893 case T_smd:
4894 attributes |= FP_SMD;
4895 break;
4896
4897 case T_board_only:
4898 attributes |= FP_BOARD_ONLY;
4899 break;
4900
4901 case T_exclude_from_pos_files:
4902 attributes |= FP_EXCLUDE_FROM_POS_FILES;
4903 break;
4904
4905 case T_exclude_from_bom:
4906 attributes |= FP_EXCLUDE_FROM_BOM;
4907 break;
4908
4909 case T_allow_missing_courtyard:
4910 attributes |= FP_ALLOW_MISSING_COURTYARD;
4911 break;
4912
4913 case T_dnp:
4914 attributes |= FP_DNP;
4915 break;
4916
4917 case T_allow_soldermask_bridges:
4918 attributes |= FP_ALLOW_SOLDERMASK_BRIDGES;
4919 break;
4920
4921 default:
4922 Expecting( "through_hole, smd, virtual, board_only, exclude_from_pos_files, "
4923 "exclude_from_bom or allow_solder_mask_bridges" );
4924 }
4925 }
4926
4927 break;
4928
4929 case T_fp_text:
4930 {
4931 PCB_TEXT* text = parsePCB_TEXT( footprint.get() );
4932
4933 if( PCB_FIELD* field = dynamic_cast<PCB_FIELD*>( text ) )
4934 {
4935 switch( field->GetId() )
4936 {
4937 case FIELD_T::REFERENCE:
4938 footprint->Reference() = PCB_FIELD( *text, FIELD_T::REFERENCE );
4939 const_cast<KIID&>( footprint->Reference().m_Uuid ) = text->m_Uuid;
4940 delete text;
4941 break;
4942
4943 case FIELD_T::VALUE:
4944 footprint->Value() = PCB_FIELD( *text, FIELD_T::VALUE );
4945 const_cast<KIID&>( footprint->Value().m_Uuid ) = text->m_Uuid;
4946 delete text;
4947 break;
4948
4949 default:
4950 // Fields other than reference and value weren't historically
4951 // stored in fp_texts so we don't need to handle them here
4952 break;
4953 }
4954 }
4955 else
4956 {
4957 footprint->Add( text, ADD_MODE::APPEND, true );
4958 }
4959
4960 break;
4961 }
4962
4963 case T_fp_text_box:
4964 {
4965 PCB_TEXTBOX* textbox = parsePCB_TEXTBOX( footprint.get() );
4966 footprint->Add( textbox, ADD_MODE::APPEND, true );
4967 break;
4968 }
4969
4970 case T_table:
4971 {
4972 PCB_TABLE* table = parsePCB_TABLE( footprint.get() );
4973 footprint->Add( table, ADD_MODE::APPEND, true );
4974 break;
4975 }
4976
4977 case T_fp_arc:
4978 case T_fp_circle:
4979 case T_fp_curve:
4980 case T_fp_rect:
4981 case T_fp_line:
4982 case T_fp_poly:
4983 {
4984 PCB_SHAPE* shape = parsePCB_SHAPE( footprint.get() );
4985 footprint->Add( shape, ADD_MODE::APPEND, true );
4986 break;
4987 }
4988
4989 case T_image:
4990 {
4992 footprint->Add( image, ADD_MODE::APPEND, true );
4993 break;
4994 }
4995
4996 case T_dimension:
4997 {
4998 PCB_DIMENSION_BASE* dimension = parseDIMENSION( footprint.get() );
4999 footprint->Add( dimension, ADD_MODE::APPEND, true );
5000 break;
5001 }
5002
5003 case T_pad:
5004 {
5005 PAD* pad = parsePAD( footprint.get() );
5006 footprint->Add( pad, ADD_MODE::APPEND, true );
5007 break;
5008 }
5009
5010 case T_model:
5011 {
5012 FP_3DMODEL* model = parse3DModel();
5013 footprint->Add3DModel( model );
5014 delete model;
5015 break;
5016 }
5017
5018 case T_zone:
5019 {
5020 ZONE* zone = parseZONE( footprint.get() );
5021 footprint->Add( zone, ADD_MODE::APPEND, true );
5022 break;
5023 }
5024
5025 case T_group:
5026 parseGROUP( footprint.get() );
5027 break;
5028
5029 case T_embedded_fonts:
5030 {
5031 footprint->GetEmbeddedFiles()->SetAreFontsEmbedded( parseBool() );
5032 NeedRIGHT();
5033 break;
5034 }
5035
5036 case T_embedded_files:
5037 {
5038 EMBEDDED_FILES_PARSER embeddedFilesParser( reader );
5039 embeddedFilesParser.SyncLineReaderWith( *this );
5040
5041 try
5042 {
5043 embeddedFilesParser.ParseEmbedded( footprint->GetEmbeddedFiles() );
5044 }
5045 catch( const PARSE_ERROR& e )
5046 {
5047 wxLogError( e.What() );
5048 }
5049
5050 SyncLineReaderWith( embeddedFilesParser );
5051 break;
5052 }
5053
5054 case T_component_classes:
5055 {
5056 std::unordered_set<wxString> componentClassNames;
5057
5058 while( ( token = NextTok() ) != T_RIGHT )
5059 {
5060 if( token != T_LEFT )
5061 Expecting( T_LEFT );
5062
5063 if( ( token = NextTok() ) != T_class )
5064 Expecting( T_class );
5065
5066 NeedSYMBOLorNUMBER();
5067 componentClassNames.insert( From_UTF8( CurText() ) );
5068 NeedRIGHT();
5069 }
5070
5071 footprint->SetTransientComponentClassNames( componentClassNames );
5072
5073 if( m_board )
5074 footprint->ResolveComponentClassNames( m_board, componentClassNames );
5075
5076 break;
5077 }
5078
5079 default:
5080 Expecting( "at, descr, locked, placed, tedit, tstamp, uuid, "
5081 "autoplace_cost90, autoplace_cost180, attr, clearance, "
5082 "embedded_files, fp_arc, fp_circle, fp_curve, fp_line, fp_poly, "
5083 "fp_rect, fp_text, pad, group, generator, model, path, solder_mask_margin, "
5084 "solder_paste_margin, solder_paste_margin_ratio, tags, thermal_gap, "
5085 "version, zone, zone_connect, or component_classes" );
5086 }
5087 }
5088
5089 // In legacy files the lack of attributes indicated a through-hole component which was by
5090 // default excluded from pos files. However there was a hack to look for SMD pads and
5091 // consider those "mislabeled through-hole components" and therefore include them in place
5092 // files. We probably don't want to get into that game so we'll just include them by
5093 // default and let the user change it if required.
5094 if( m_requiredVersion < 20200826 && attributes == 0 )
5095 attributes |= FP_THROUGH_HOLE;
5096
5098 {
5099 if( footprint->GetKeywords().StartsWith( wxT( "net tie" ) ) )
5100 {
5101 wxString padGroup;
5102
5103 for( PAD* pad : footprint->Pads() )
5104 {
5105 if( !padGroup.IsEmpty() )
5106 padGroup += wxS( ", " );
5107
5108 padGroup += pad->GetNumber();
5109 }
5110
5111 if( !padGroup.IsEmpty() )
5112 footprint->AddNetTiePadGroup( padGroup );
5113 }
5114 }
5115
5116 footprint->SetAttributes( attributes );
5117
5118 footprint->SetFPID( fpid );
5119
5120 return footprint.release();
5121}
5122
5123
5125{
5126 wxCHECK_MSG( CurTok() == T_pad, nullptr,
5127 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PAD." ) );
5128
5129 VECTOR2I sz;
5130 VECTOR2I pt;
5131 bool foundNet = false;
5132
5133 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aParent );
5134
5135 NeedSYMBOLorNUMBER();
5136 pad->SetNumber( FromUTF8() );
5137
5138 T token = NextTok();
5139
5140 switch( token )
5141 {
5142 case T_thru_hole:
5143 pad->SetAttribute( PAD_ATTRIB::PTH );
5144
5145 // The drill token is usually missing if 0 drill size is specified.
5146 // Emulate it using 1 nm drill size to avoid errors.
5147 // Drill size cannot be set to 0 in newer versions.
5148 pad->SetDrillSize( VECTOR2I( 1, 1 ) );
5149 break;
5150
5151 case T_smd:
5152 pad->SetAttribute( PAD_ATTRIB::SMD );
5153
5154 // Default PAD object is thru hole with drill.
5155 // SMD pads have no hole
5156 pad->SetDrillSize( VECTOR2I( 0, 0 ) );
5157 break;
5158
5159 case T_connect:
5160 pad->SetAttribute( PAD_ATTRIB::CONN );
5161
5162 // Default PAD object is thru hole with drill.
5163 // CONN pads have no hole
5164 pad->SetDrillSize( VECTOR2I( 0, 0 ) );
5165 break;
5166
5167 case T_np_thru_hole:
5168 pad->SetAttribute( PAD_ATTRIB::NPTH );
5169 break;
5170
5171 default:
5172 Expecting( "thru_hole, smd, connect, or np_thru_hole" );
5173 }
5174
5175 token = NextTok();
5176
5177 switch( token )
5178 {
5179 case T_circle:
5180 pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
5181 break;
5182
5183 case T_rect:
5184 pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::RECTANGLE );
5185 break;
5186
5187 case T_oval:
5188 pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::OVAL );
5189 break;
5190
5191 case T_trapezoid:
5192 pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::TRAPEZOID );
5193 break;
5194
5195 case T_roundrect:
5196 // Note: the shape can be PAD_SHAPE::ROUNDRECT or PAD_SHAPE::CHAMFERED_RECT
5197 // (if chamfer parameters are found later in pad descr.)
5198 pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::ROUNDRECT );
5199 break;
5200
5201 case T_custom:
5202 pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CUSTOM );
5203 break;
5204
5205 default:
5206 Expecting( "circle, rectangle, roundrect, oval, trapezoid or custom" );
5207 }
5208
5209 std::optional<EDA_ANGLE> thermalBrAngleOverride;
5210
5211 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5212 {
5213 if( token == T_locked )
5214 {
5215 // Pad locking is now a session preference
5216 token = NextTok();
5217 }
5218
5219 if( token != T_LEFT )
5220 Expecting( T_LEFT );
5221
5222 token = NextTok();
5223
5224 switch( token )
5225 {
5226 case T_size:
5227 sz.x = parseBoardUnits( "width value" );
5228 sz.y = parseBoardUnits( "height value" );
5229 pad->SetSize( PADSTACK::ALL_LAYERS, sz );
5230 NeedRIGHT();
5231 break;
5232
5233 case T_at:
5234 pt.x = parseBoardUnits( "X coordinate" );
5235 pt.y = parseBoardUnits( "Y coordinate" );
5236 pad->SetFPRelativePosition( pt );
5237 token = NextTok();
5238
5239 if( token == T_NUMBER )
5240 {
5241 pad->SetOrientation( EDA_ANGLE( parseDouble(), DEGREES_T ) );
5242 NeedRIGHT();
5243 }
5244 else if( token != T_RIGHT )
5245 {
5246 Expecting( ") or angle value" );
5247 }
5248
5249 break;
5250
5251 case T_rect_delta:
5252 {
5254 delta.x = parseBoardUnits( "rectangle delta width" );
5255 delta.y = parseBoardUnits( "rectangle delta height" );
5256 pad->SetDelta( PADSTACK::ALL_LAYERS, delta );
5257 NeedRIGHT();
5258 break;
5259 }
5260
5261 case T_drill:
5262 {
5263 bool haveWidth = false;
5264 VECTOR2I drillSize = pad->GetDrillSize();
5265
5266 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5267 {
5268 if( token == T_LEFT )
5269 token = NextTok();
5270
5271 switch( token )
5272 {
5273 case T_oval: pad->SetDrillShape( PAD_DRILL_SHAPE::OBLONG ); break;
5274
5275 case T_NUMBER:
5276 {
5277 if( !haveWidth )
5278 {
5279 drillSize.x = parseBoardUnits();
5280
5281 // If height is not defined the width and height are the same.
5282 drillSize.y = drillSize.x;
5283 haveWidth = true;
5284 }
5285 else
5286 {
5287 drillSize.y = parseBoardUnits();
5288 }
5289 }
5290
5291 break;
5292
5293 case T_offset:
5294 pt.x = parseBoardUnits( "drill offset x" );
5295 pt.y = parseBoardUnits( "drill offset y" );
5296 pad->SetOffset( PADSTACK::ALL_LAYERS, pt );
5297 NeedRIGHT();
5298 break;
5299
5300 default:
5301 Expecting( "oval, size, or offset" );
5302 }
5303 }
5304
5305 // This fixes a bug caused by setting the default PAD drill size to a value other
5306 // than 0 used to fix a bunch of debug assertions even though it is defined as a
5307 // through hole pad. Wouldn't a though hole pad with no drill be a surface mount
5308 // pad (or a conn pad which is a smd pad with no solder paste)?
5309 if( pad->GetAttribute() != PAD_ATTRIB::SMD && pad->GetAttribute() != PAD_ATTRIB::CONN )
5310 pad->SetDrillSize( drillSize );
5311 else
5312 pad->SetDrillSize( VECTOR2I( 0, 0 ) );
5313
5314 break;
5315 }
5316
5317 case T_layers:
5318 {
5319 LSET layerMask = parseBoardItemLayersAsMask();
5320 pad->SetLayerSet( layerMask );
5321 break;
5322 }
5323
5324 case T_net:
5325 foundNet = true;
5326
5327 if( ! pad->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
5328 {
5329 wxLogError( _( "Invalid net ID in\nfile: %s\nline: %d offset: %d" ),
5330 CurSource(), CurLineNumber(), CurOffset() );
5331 }
5332
5333 NeedSYMBOLorNUMBER();
5334
5335 // Test validity of the netname in file for netcodes expected having a net name
5336 if( m_board && pad->GetNetCode() > 0 )
5337 {
5338 wxString netName( FromUTF8() );
5339
5340 // Convert overbar syntax from `~...~` to `~{...}`. These were left out of the
5341 // first merge so the version is a bit later.
5342 if( m_requiredVersion < 20210606 )
5343 netName = ConvertToNewOverbarNotation( netName );
5344
5345 if( netName != m_board->FindNet( pad->GetNetCode() )->GetNetname() )
5346 {
5347 pad->SetNetCode( NETINFO_LIST::ORPHANED, /* aNoAssert */ true );
5348 wxLogError( _( "Net name doesn't match ID in\nfile: %s\nline: %d offset: %d" ),
5349 CurSource(), CurLineNumber(), CurOffset() );
5350 }
5351 }
5352
5353 NeedRIGHT();
5354 break;
5355
5356 case T_pinfunction:
5357 NeedSYMBOLorNUMBER();
5358 pad->SetPinFunction( FromUTF8() );
5359 NeedRIGHT();
5360 break;
5361
5362 case T_pintype:
5363 NeedSYMBOLorNUMBER();
5364 pad->SetPinType( FromUTF8() );
5365 NeedRIGHT();
5366 break;
5367
5368 case T_die_length:
5369 pad->SetPadToDieLength( parseBoardUnits( T_die_length ) );
5370 NeedRIGHT();
5371 break;
5372
5373 case T_solder_mask_margin:
5374 pad->SetLocalSolderMaskMargin( parseBoardUnits( "local solder mask margin value" ) );
5375 NeedRIGHT();
5376
5377 // In pre-9.0 files "0" meant inherit.
5378 if( m_requiredVersion <= 20240201 && pad->GetLocalSolderMaskMargin() == 0 )
5379 pad->SetLocalSolderMaskMargin( {} );
5380
5381 break;
5382
5383 case T_solder_paste_margin:
5384 pad->SetLocalSolderPasteMargin( parseBoardUnits( "local solder paste margin value" ) );
5385 NeedRIGHT();
5386
5387 // In pre-9.0 files "0" meant inherit.
5388 if( m_requiredVersion <= 20240201 && pad->GetLocalSolderPasteMargin() == 0 )
5389 pad->SetLocalSolderPasteMargin( {} );
5390
5391 break;
5392
5393 case T_solder_paste_margin_ratio:
5394 pad->SetLocalSolderPasteMarginRatio( parseDouble( "local solder paste margin ratio value" ) );
5395 NeedRIGHT();
5396
5397 // In pre-9.0 files "0" meant inherit.
5398 if( m_requiredVersion <= 20240201 && pad->GetLocalSolderPasteMarginRatio() == 0 )
5399 pad->SetLocalSolderPasteMarginRatio( {} );
5400
5401 break;
5402
5403 case T_clearance:
5404 pad->SetLocalClearance( parseBoardUnits( "local clearance value" ) );
5405 NeedRIGHT();
5406
5407 // In pre-9.0 files "0" meant inherit.
5408 if( m_requiredVersion <= 20240201 && pad->GetLocalClearance() == 0 )
5409 pad->SetLocalClearance( {} );
5410
5411 break;
5412
5413 case T_teardrops:
5414 parseTEARDROP_PARAMETERS( &pad->GetTeardropParams() );
5415 break;
5416
5417 case T_zone_connect:
5418 pad->SetLocalZoneConnection( (ZONE_CONNECTION) parseInt( "zone connection value" ) );
5419 NeedRIGHT();
5420 break;
5421
5422 case T_thermal_width: // legacy token
5423 case T_thermal_bridge_width:
5424 pad->SetLocalThermalSpokeWidthOverride( parseBoardUnits( token ) );
5425 NeedRIGHT();
5426 break;
5427
5428 case T_thermal_bridge_angle:
5429 thermalBrAngleOverride = EDA_ANGLE( parseDouble( "thermal spoke angle" ), DEGREES_T );
5430 NeedRIGHT();
5431 break;
5432
5433
5434 case T_thermal_gap:
5435 pad->SetThermalGap( parseBoardUnits( "thermal relief gap value" ) );
5436 NeedRIGHT();
5437 break;
5438
5439 case T_roundrect_rratio:
5440 pad->SetRoundRectRadiusRatio( PADSTACK::ALL_LAYERS,
5441 parseDouble( "roundrect radius ratio" ) );
5442 NeedRIGHT();
5443 break;
5444
5445 case T_chamfer_ratio:
5446 pad->SetChamferRectRatio( PADSTACK::ALL_LAYERS, parseDouble( "chamfer ratio" ) );
5447
5448 if( pad->GetChamferRectRatio( PADSTACK::ALL_LAYERS ) > 0 )
5449 pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CHAMFERED_RECT );
5450
5451 NeedRIGHT();
5452 break;
5453
5454 case T_chamfer:
5455 {
5456 int chamfers = 0;
5457 bool end_list = false;
5458
5459 while( !end_list )
5460 {
5461 token = NextTok();
5462
5463 switch( token )
5464 {
5465 case T_top_left:
5466 chamfers |= RECT_CHAMFER_TOP_LEFT;
5467 break;
5468
5469 case T_top_right:
5470 chamfers |= RECT_CHAMFER_TOP_RIGHT;
5471 break;
5472
5473 case T_bottom_left:
5474 chamfers |= RECT_CHAMFER_BOTTOM_LEFT;
5475 break;
5476
5477 case T_bottom_right:
5478 chamfers |= RECT_CHAMFER_BOTTOM_RIGHT;
5479 break;
5480
5481 case T_RIGHT:
5482 pad->SetChamferPositions( PADSTACK::ALL_LAYERS, chamfers );
5483 end_list = true;
5484 break;
5485
5486 default:
5487 Expecting( "chamfer_top_left chamfer_top_right chamfer_bottom_left or "
5488 "chamfer_bottom_right" );
5489 }
5490 }
5491
5492 if( pad->GetChamferPositions( PADSTACK::ALL_LAYERS ) != RECT_NO_CHAMFER )
5493 pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CHAMFERED_RECT );
5494
5495 break;
5496 }
5497
5498 case T_property:
5499 while( token != T_RIGHT )
5500 {
5501 token = NextTok();
5502
5503 switch( token )
5504 {
5505 case T_pad_prop_bga: pad->SetProperty( PAD_PROP::BGA ); break;
5506 case T_pad_prop_fiducial_glob: pad->SetProperty( PAD_PROP::FIDUCIAL_GLBL ); break;
5507 case T_pad_prop_fiducial_loc: pad->SetProperty( PAD_PROP::FIDUCIAL_LOCAL ); break;
5508 case T_pad_prop_testpoint: pad->SetProperty( PAD_PROP::TESTPOINT ); break;
5509 case T_pad_prop_castellated: pad->SetProperty( PAD_PROP::CASTELLATED ); break;
5510 case T_pad_prop_heatsink: pad->SetProperty( PAD_PROP::HEATSINK ); break;
5511 case T_pad_prop_mechanical: pad->SetProperty( PAD_PROP::MECHANICAL ); break;
5512 case T_none: pad->SetProperty( PAD_PROP::NONE ); break;
5513 case T_RIGHT: break;
5514
5515 default:
5516#if 0 // Currently: skip unknown property
5517 Expecting( "pad_prop_bga pad_prop_fiducial_glob pad_prop_fiducial_loc"
5518 " pad_prop_heatsink or pad_prop_castellated" );
5519#endif
5520 break;
5521 }
5522 }
5523
5524 break;
5525
5526 case T_options:
5527 parsePAD_option( pad.get() );
5528 break;
5529
5530 case T_padstack:
5531 parsePadstack( pad.get() );
5532 break;
5533
5534 case T_primitives:
5535 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5536 {
5537 if( token == T_LEFT )
5538 token = NextTok();
5539
5540 switch( token )
5541 {
5542 case T_gr_arc:
5543 case T_gr_line:
5544 case T_gr_circle:
5545 case T_gr_rect:
5546 case T_gr_poly:
5547 case T_gr_curve:
5548 pad->AddPrimitive( PADSTACK::ALL_LAYERS, parsePCB_SHAPE( nullptr ) );
5549 break;
5550
5551 case T_gr_bbox:
5552 {
5553 PCB_SHAPE* numberBox = parsePCB_SHAPE( nullptr );
5554 numberBox->SetIsProxyItem();
5555 pad->AddPrimitive( PADSTACK::ALL_LAYERS, numberBox );
5556 break;
5557 }
5558
5559 case T_gr_vector:
5560 {
5561 PCB_SHAPE* spokeTemplate = parsePCB_SHAPE( nullptr );
5562 spokeTemplate->SetIsProxyItem();
5563 pad->AddPrimitive( PADSTACK::ALL_LAYERS, spokeTemplate );
5564 break;
5565 }
5566
5567 default:
5568 Expecting( "gr_line, gr_arc, gr_circle, gr_curve, gr_rect, gr_bbox or gr_poly" );
5569 break;
5570 }
5571 }
5572
5573 break;
5574
5575 case T_remove_unused_layers:
5576 {
5577 bool remove = parseMaybeAbsentBool( true );
5578 pad->SetRemoveUnconnected( remove );
5579 break;
5580 }
5581
5582 case T_keep_end_layers:
5583 {
5584 bool keep = parseMaybeAbsentBool( true );
5585 pad->SetKeepTopBottom( keep );
5586 break;
5587 }
5588
5589 case T_tenting:
5590 {
5591 auto [front, back] = parseFrontBackOptBool( true );
5592 pad->Padstack().FrontOuterLayers().has_solder_mask = front;
5593 pad->Padstack().BackOuterLayers().has_solder_mask = back;
5594 break;
5595 }
5596
5597 case T_zone_layer_connections:
5598 {
5599 LSET cuLayers = pad->GetLayerSet() & LSET::AllCuMask();
5600
5601 for( PCB_LAYER_ID layer : cuLayers.Seq() )
5602 pad->SetZoneLayerOverride( layer, ZLO_FORCE_NO_ZONE_CONNECTION );
5603
5604 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5605 {
5607
5608 if( !IsCopperLayer( layer ) )
5609 Expecting( "copper layer name" );
5610
5611 pad->SetZoneLayerOverride( layer, ZLO_FORCE_FLASHED );
5612 }
5613
5614 break;
5615 }
5616
5617 // Continue to process "(locked)" format which was output during 5.99 development
5618 case T_locked:
5619 // Pad locking is now a session preference
5620 parseMaybeAbsentBool( true );
5621 break;
5622
5623 case T_tstamp:
5624 case T_uuid:
5625 NextTok();
5626 const_cast<KIID&>( pad->m_Uuid ) = CurStrToKIID();
5627 NeedRIGHT();
5628 break;
5629
5630 default:
5631 Expecting( "at, locked, drill, layers, net, die_length, roundrect_rratio, "
5632 "solder_mask_margin, solder_paste_margin, solder_paste_margin_ratio, uuid, "
5633 "clearance, tstamp, primitives, remove_unused_layers, keep_end_layers, "
5634 "pinfunction, pintype, zone_connect, thermal_width, thermal_gap, padstack or "
5635 "teardrops" );
5636 }
5637 }
5638
5639 if( !foundNet )
5640 {
5641 // Make sure default netclass is correctly assigned to pads that don't define a net.
5642 pad->SetNetCode( 0, /* aNoAssert */ true );
5643 }
5644
5645 if( thermalBrAngleOverride )
5646 {
5647 pad->SetThermalSpokeAngle( *thermalBrAngleOverride );
5648 }
5649 else
5650 {
5651 // This is here because custom pad anchor shape isn't known before reading (options
5652 if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CIRCLE )
5653 {
5654 pad->SetThermalSpokeAngle( ANGLE_45 );
5655 }
5656 else if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CUSTOM
5657 && pad->GetAnchorPadShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CIRCLE )
5658 {
5659 if( m_requiredVersion <= 20211014 ) // 6.0
5660 pad->SetThermalSpokeAngle( ANGLE_90 );
5661 else
5662 pad->SetThermalSpokeAngle( ANGLE_45 );
5663 }
5664 else
5665 {
5666 pad->SetThermalSpokeAngle( ANGLE_90 );
5667 }
5668 }
5669
5670 if( !pad->CanHaveNumber() )
5671 {
5672 // At some point it was possible to assign a number to aperture pads so we need to clean
5673 // those out here.
5674 pad->SetNumber( wxEmptyString );
5675 }
5676
5677 // Zero-sized pads are likely algorithmically unsafe.
5678 if( pad->GetSizeX() <= 0 || pad->GetSizeY() <= 0 )
5679 {
5680 pad->SetSize( PADSTACK::ALL_LAYERS,
5681 VECTOR2I( pcbIUScale.mmToIU( 0.001 ), pcbIUScale.mmToIU( 0.001 ) ) );
5682
5683 wxLogWarning( _( "Invalid zero-sized pad pinned to %s in\nfile: %s\nline: %d\noffset: %d" ),
5684 wxT( "1µm" ), CurSource(), CurLineNumber(), CurOffset() );
5685 }
5686
5687 return pad.release();
5688}
5689
5690
5692{
5693 // Parse only the (option ...) inside a pad description
5694 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
5695 {
5696 if( token != T_LEFT )
5697 Expecting( T_LEFT );
5698
5699 token = NextTok();
5700
5701 switch( token )
5702 {
5703 case T_anchor:
5704 token = NextTok();
5705 // Custom shaped pads have a "anchor pad", which is the reference
5706 // for connection calculations.
5707 // Because this is an anchor, only the 2 very basic shapes are managed:
5708 // circle and rect. The default is circle
5709 switch( token )
5710 {
5711 case T_circle: // default
5712 break;
5713
5714 case T_rect:
5715 aPad->SetAnchorPadShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::RECTANGLE );
5716 break;
5717
5718 default:
5719 // Currently, because pad options is a moving target
5720 // just skip unknown keywords
5721 break;
5722 }
5723 NeedRIGHT();
5724 break;
5725
5726 case T_clearance:
5727 token = NextTok();
5728 // Custom shaped pads have a clearance area that is the pad shape
5729 // (like usual pads) or the convex hull of the pad shape.
5730 switch( token )
5731 {
5732 case T_outline:
5734 break;
5735
5736 case T_convexhull:
5738 break;
5739
5740 default:
5741 // Currently, because pad options is a moving target
5742 // just skip unknown keywords
5743 break;
5744 }
5745
5746 NeedRIGHT();
5747 break;
5748
5749 default:
5750 // Currently, because pad options is a moving target
5751 // just skip unknown keywords
5752 while( (token = NextTok() ) != T_RIGHT )
5753 {}
5754
5755 break;
5756 }
5757 }
5758
5759 return true;
5760}
5761
5762
5764{
5765 PADSTACK& padstack = aPad->Padstack();
5766
5767 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
5768 {
5769 if( token != T_LEFT )
5770 Expecting( T_LEFT );
5771
5772 token = NextTok();
5773
5774 switch( token )
5775 {
5776 case T_mode:
5777 token = NextTok();
5778
5779 switch( token )
5780 {
5781 case T_front_inner_back:
5783 break;
5784
5785 case T_custom:
5786 padstack.SetMode( PADSTACK::MODE::CUSTOM );
5787 break;
5788
5789 default:
5790 Expecting( "front_inner_back or custom" );
5791 }
5792
5793 NeedRIGHT();
5794 break;
5795
5796 case T_layer:
5797 {
5798 NextTok();
5799 PCB_LAYER_ID curLayer = UNDEFINED_LAYER;
5800
5801 if( curText == "Inner" )
5802 {
5803 if( padstack.Mode() != PADSTACK::MODE::FRONT_INNER_BACK )
5804 {
5805 THROW_IO_ERROR( wxString::Format( _( "Invalid padstack layer in\nfile: %s\n"
5806 "line: %d\noffset: %d." ),
5807 CurSource(), CurLineNumber(), CurOffset() ) );
5808 }
5809
5810 curLayer = PADSTACK::INNER_LAYERS;
5811 }
5812 else
5813 {
5814 curLayer = lookUpLayer( m_layerIndices );
5815 }
5816
5817 if( !IsCopperLayer( curLayer ) )
5818 {
5819 wxString error;
5820 error.Printf( _( "Invalid padstack layer '%s' in file '%s' at line %d, offset %d." ),
5821 curText, CurSource().GetData(), CurLineNumber(), CurOffset() );
5822 THROW_IO_ERROR( error );
5823 }
5824
5825 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5826 {
5827 if( token != T_LEFT )
5828 Expecting( T_LEFT );
5829
5830 token = NextTok();
5831
5832 switch( token )
5833 {
5834 case T_shape:
5835 token = NextTok();
5836
5837 switch( token )
5838 {
5839 case T_circle:
5840 aPad->SetShape( curLayer, PAD_SHAPE::CIRCLE );
5841 break;
5842
5843 case T_rect:
5844 aPad->SetShape( curLayer, PAD_SHAPE::RECTANGLE );
5845 break;
5846
5847 case T_oval:
5848 aPad->SetShape( curLayer, PAD_SHAPE::OVAL );
5849 break;
5850
5851 case T_trapezoid:
5852 aPad->SetShape( curLayer, PAD_SHAPE::TRAPEZOID );
5853 break;
5854
5855 case T_roundrect:
5856 // Note: the shape can be PAD_SHAPE::ROUNDRECT or PAD_SHAPE::CHAMFERED_RECT
5857 // (if chamfer parameters are found later in pad descr.)
5858 aPad->SetShape( curLayer, PAD_SHAPE::ROUNDRECT );
5859 break;
5860
5861 case T_custom:
5862 aPad->SetShape( curLayer, PAD_SHAPE::CUSTOM );
5863 break;
5864
5865 default:
5866 Expecting( "circle, rectangle, roundrect, oval, trapezoid or custom" );
5867 }
5868
5869 NeedRIGHT();
5870 break;
5871
5872 case T_size:
5873 {
5874 VECTOR2I sz;
5875 sz.x = parseBoardUnits( "width value" );
5876 sz.y = parseBoardUnits( "height value" );
5877 aPad->SetSize( curLayer, sz );
5878 NeedRIGHT();
5879 break;
5880 }
5881
5882 case T_offset:
5883 {
5884 VECTOR2I pt;
5885 pt.x = parseBoardUnits( "drill offset x" );
5886 pt.y = parseBoardUnits( "drill offset y" );
5887 aPad->SetOffset( curLayer, pt );
5888 NeedRIGHT();
5889 break;
5890 }
5891
5892 case T_rect_delta:
5893 {
5895 delta.x = parseBoardUnits( "rectangle delta width" );
5896 delta.y = parseBoardUnits( "rectangle delta height" );
5897 aPad->SetDelta( curLayer, delta );
5898 NeedRIGHT();
5899 break;
5900 }
5901
5902 case T_roundrect_rratio:
5903 aPad->SetRoundRectRadiusRatio( curLayer,
5904 parseDouble( "roundrect radius ratio" ) );
5905 NeedRIGHT();
5906 break;
5907
5908 case T_chamfer_ratio:
5909 {
5910 double ratio = parseDouble( "chamfer ratio" );
5911 aPad->SetChamferRectRatio( curLayer, ratio );
5912
5913 if( ratio > 0 )
5914 aPad->SetShape( curLayer, PAD_SHAPE::CHAMFERED_RECT );
5915
5916 NeedRIGHT();
5917 break;
5918 }
5919
5920 case T_chamfer:
5921 {
5922 int chamfers = 0;
5923 bool end_list = false;
5924
5925 while( !end_list )
5926 {
5927 token = NextTok();
5928
5929 switch( token )
5930 {
5931 case T_top_left:
5932 chamfers |= RECT_CHAMFER_TOP_LEFT;
5933 break;
5934
5935 case T_top_right:
5936 chamfers |= RECT_CHAMFER_TOP_RIGHT;
5937 break;
5938
5939 case T_bottom_left:
5940 chamfers |= RECT_CHAMFER_BOTTOM_LEFT;
5941 break;
5942
5943 case T_bottom_right:
5944 chamfers |= RECT_CHAMFER_BOTTOM_RIGHT;
5945 break;
5946
5947 case T_RIGHT:
5948 aPad->SetChamferPositions( curLayer, chamfers );
5949 end_list = true;
5950 break;
5951
5952 default:
5953 Expecting( "chamfer_top_left chamfer_top_right chamfer_bottom_left or "
5954 "chamfer_bottom_right" );
5955 }
5956 }
5957
5958 if( end_list && chamfers != RECT_NO_CHAMFER )
5959 aPad->SetShape( curLayer, PAD_SHAPE::CHAMFERED_RECT );
5960
5961 break;
5962 }
5963
5964 case T_thermal_bridge_width:
5965 padstack.ThermalSpokeWidth( curLayer ) =
5966 parseBoardUnits( "thermal relief spoke width" );
5967 NeedRIGHT();
5968 break;
5969
5970 case T_thermal_gap:
5971 padstack.ThermalGap( curLayer ) = parseBoardUnits( "thermal relief gap value" );
5972 NeedRIGHT();
5973 break;
5974
5975 case T_thermal_bridge_angle:
5976 padstack.SetThermalSpokeAngle(
5977 EDA_ANGLE( parseDouble( "thermal spoke angle" ), DEGREES_T ) );
5978 NeedRIGHT();
5979 break;
5980
5981 case T_zone_connect:
5982 padstack.ZoneConnection( curLayer ) = magic_enum::enum_cast<ZONE_CONNECTION>(
5983 parseInt( "zone connection value" ) );
5984 NeedRIGHT();
5985 break;
5986
5987 case T_clearance:
5988 padstack.Clearance( curLayer ) = parseBoardUnits( "local clearance value" );
5989 NeedRIGHT();
5990 break;
5991
5992 case T_tenting:
5993 {
5994 auto [front, back] = parseFrontBackOptBool( true );
5995 padstack.FrontOuterLayers().has_solder_mask = front;
5996 padstack.BackOuterLayers().has_solder_mask = back;
5997 break;
5998 }
5999
6000 // TODO: refactor parsePAD_options to work on padstacks too
6001 case T_options:
6002 {
6003 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6004 {
6005 if( token != T_LEFT )
6006 Expecting( T_LEFT );
6007
6008 token = NextTok();
6009
6010 switch( token )
6011 {
6012 case T_anchor:
6013 token = NextTok();
6014 // Custom shaped pads have a "anchor pad", which is the reference
6015 // for connection calculations.
6016 // Because this is an anchor, only the 2 very basic shapes are managed:
6017 // circle and rect. The default is circle
6018 switch( token )
6019 {
6020 case T_circle: // default
6021 break;
6022
6023 case T_rect:
6024 padstack.SetAnchorShape( PAD_SHAPE::RECTANGLE, curLayer );
6025 break;
6026
6027 default:
6028 // Currently, because pad options is a moving target
6029 // just skip unknown keywords
6030 break;
6031 }
6032 NeedRIGHT();
6033 break;
6034
6035 case T_clearance:
6036 token = NextTok();
6037 // TODO: m_customShapeInZoneMode is not per-layer at the moment
6038 NeedRIGHT();
6039 break;
6040
6041 default:
6042 // Currently, because pad options is a moving target
6043 // just skip unknown keywords
6044 while( ( token = NextTok() ) != T_RIGHT )
6045 {
6046 }
6047
6048 break;
6049 }
6050 }
6051
6052 break;
6053 }
6054
6055 // TODO: deduplicate with non-padstack parser
6056 case T_primitives:
6057 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6058 {
6059 if( token == T_LEFT )
6060 token = NextTok();
6061
6062 switch( token )
6063 {
6064 case T_gr_arc:
6065 case T_gr_line:
6066 case T_gr_circle:
6067 case T_gr_rect:
6068 case T_gr_poly:
6069 case T_gr_curve:
6070 padstack.AddPrimitive( parsePCB_SHAPE( nullptr ), curLayer );
6071 break;
6072
6073 case T_gr_bbox:
6074 {
6075 PCB_SHAPE* numberBox = parsePCB_SHAPE( nullptr );
6076 numberBox->SetIsProxyItem();
6077 padstack.AddPrimitive( numberBox, curLayer );
6078 break;
6079 }
6080
6081 case T_gr_vector:
6082 {
6083 PCB_SHAPE* spokeTemplate = parsePCB_SHAPE( nullptr );
6084 spokeTemplate->SetIsProxyItem();
6085 padstack.AddPrimitive( spokeTemplate, curLayer );
6086 break;
6087 }
6088
6089 default:
6090 Expecting( "gr_line, gr_arc, gr_circle, gr_curve, gr_rect, gr_bbox or gr_poly" );
6091 break;
6092 }
6093 }
6094
6095 break;
6096
6097 default:
6098 // Not strict-parsing padstack layers yet
6099 continue;
6100 }
6101 }
6102
6103 break;
6104 }
6105
6106 default:
6107 Expecting( "mode or layer" );
6108 break;
6109 }
6110 }
6111}
6112
6113
6115{
6116 T token;
6117
6118 while( ( token = NextTok() ) != T_RIGHT )
6119 {
6120 // This token is the Uuid of the item in the group.
6121 // Since groups are serialized at the end of the file/footprint, the Uuid should already
6122 // have been seen and exist in the board.
6123 KIID uuid( CurStr() );
6124 aGroupInfo.memberUuids.push_back( uuid );
6125 }
6126}
6127
6128
6130{
6131 wxCHECK_RET( CurTok() == T_group,
6132 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_GROUP." ) );
6133
6134 T token;
6135
6136 m_groupInfos.push_back( GROUP_INFO() );
6137 GROUP_INFO& groupInfo = m_groupInfos.back();
6138 groupInfo.parent = aParent;
6139
6140 while( ( token = NextTok() ) != T_LEFT )
6141 {
6142 if( token == T_STRING )
6143 groupInfo.name = FromUTF8();
6144 else if( token == T_locked )
6145 groupInfo.locked = true;
6146 else
6147 Expecting( "group name or locked" );
6148 }
6149
6150 for( ; token != T_RIGHT; token = NextTok() )
6151 {
6152 if( token != T_LEFT )
6153 Expecting( T_LEFT );
6154
6155 token = NextTok();
6156
6157 switch( token )
6158 {
6159 // From formats [20200811, 20231215), 'id' was used instead of 'uuid'
6160 case T_id:
6161 case T_uuid:
6162 NextTok();
6163 groupInfo.uuid = CurStrToKIID();
6164 NeedRIGHT();
6165 break;
6166
6167 case T_locked:
6168 groupInfo.locked = parseBool();
6169 NeedRIGHT();
6170 break;
6171
6172 case T_members:
6173 {
6174 parseGROUP_members( groupInfo );
6175 break;
6176 }
6177
6178 default:
6179 Expecting( "uuid, locked, or members" );
6180 }
6181 }
6182}
6183
6184
6186{
6187 wxCHECK_RET( CurTok() == T_generated, wxT( "Cannot parse " ) + GetTokenString( CurTok() )
6188 + wxT( " as PCB_GENERATOR." ) );
6189
6190 T token;
6191
6192 m_generatorInfos.push_back( GENERATOR_INFO() );
6193 GENERATOR_INFO& genInfo = m_generatorInfos.back();
6194
6195 genInfo.layer = F_Cu;
6196 genInfo.parent = aParent;
6198
6199 NeedLEFT();
6200 token = NextTok();
6201
6202 // For formats [20231007, 20231215), 'id' was used instead of 'uuid'
6203 if( token != T_uuid && token != T_id )
6204 Expecting( T_uuid );
6205
6206 NextTok();
6207 genInfo.uuid = CurStrToKIID();
6208 NeedRIGHT();
6209
6210 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6211 {
6212 if( token != T_LEFT )
6213 Expecting( T_LEFT );
6214
6215 token = NextTok();
6216
6217 switch( token )
6218 {
6219 case T_type:
6220 NeedSYMBOL();
6221 genInfo.genType = FromUTF8();
6222 NeedRIGHT();
6223 break;
6224
6225 case T_name:
6226 NeedSYMBOL();
6227 genInfo.name = FromUTF8();
6228 NeedRIGHT();
6229 break;
6230
6231 case T_locked:
6232 token = NextTok();
6233 genInfo.locked = token == T_yes;
6234 NeedRIGHT();
6235 break;
6236
6237 case T_layer:
6238 genInfo.layer = parseBoardItemLayer();
6239 NeedRIGHT();
6240 break;
6241
6242 case T_members: parseGROUP_members( genInfo ); break;
6243
6244 default:
6245 {
6246 wxString pName = FromUTF8();
6247 T tok1 = NextTok();
6248
6249 switch( tok1 )
6250 {
6251 case T_yes:
6252 {
6253 genInfo.properties.emplace( pName, wxAny( true ) );
6254 NeedRIGHT();
6255 break;
6256 }
6257 case T_no:
6258 {
6259 genInfo.properties.emplace( pName, wxAny( false ) );
6260 NeedRIGHT();
6261 break;
6262 }
6263 case T_NUMBER:
6264 {
6265 double pValue = parseDouble();
6266 genInfo.properties.emplace( pName, wxAny( pValue ) );
6267 NeedRIGHT();
6268 break;
6269 }
6270 case T_STRING: // Quoted string
6271 {
6272 wxString pValue = FromUTF8();
6273 genInfo.properties.emplace( pName, pValue );
6274 NeedRIGHT();
6275 break;
6276 }
6277 case T_LEFT:
6278 {
6279 NeedSYMBOL();
6280 T tok2 = CurTok();
6281
6282 switch( tok2 )
6283 {
6284 case T_xy:
6285 {
6286 VECTOR2I pt;
6287
6288 pt.x = parseBoardUnits( "X coordinate" );
6289 pt.y = parseBoardUnits( "Y coordinate" );
6290
6291 genInfo.properties.emplace( pName, wxAny( pt ) );
6292 NeedRIGHT();
6293 NeedRIGHT();
6294
6295 break;
6296 }
6297 case T_pts:
6298 {
6300
6301 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6303
6304 NeedRIGHT();
6305
6306 genInfo.properties.emplace( pName, wxAny( chain ) );
6307 break;
6308 }
6309 default: Expecting( "xy or pts" );
6310 }
6311
6312 break;
6313 }
6314 default: Expecting( "a number, symbol, string or (" );
6315 }
6316
6317 break;
6318 }
6319 }
6320 }
6321
6322 // Previous versions had bugs which could save ghost tuning patterns. Ignore them.
6323 if( genInfo.genType == wxT( "tuning_pattern" ) && genInfo.memberUuids.empty() )
6324 m_generatorInfos.pop_back();
6325}
6326
6327
6329{
6330 wxCHECK_MSG( CurTok() == T_arc, nullptr,
6331 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as ARC." ) );
6332
6333 VECTOR2I pt;
6334 T token;
6335
6336 std::unique_ptr<PCB_ARC> arc = std::make_unique<PCB_ARC>( m_board );
6337
6338 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6339 {
6340 // Legacy locked
6341 if( token == T_locked )
6342 {
6343 arc->SetLocked( true );
6344 token = NextTok();
6345 }
6346
6347 if( token != T_LEFT )
6348 Expecting( T_LEFT );
6349
6350 token = NextTok();
6351
6352 switch( token )
6353 {
6354 case T_start:
6355 pt.x = parseBoardUnits( "start x" );
6356 pt.y = parseBoardUnits( "start y" );
6357 arc->SetStart( pt );
6358 NeedRIGHT();
6359 break;
6360
6361 case T_mid:
6362 pt.x = parseBoardUnits( "mid x" );
6363 pt.y = parseBoardUnits( "mid y" );
6364 arc->SetMid( pt );
6365 NeedRIGHT();
6366 break;
6367
6368 case T_end:
6369 pt.x = parseBoardUnits( "end x" );
6370 pt.y = parseBoardUnits( "end y" );
6371 arc->SetEnd( pt );
6372 NeedRIGHT();
6373 break;
6374
6375 case T_width:
6376 arc->SetWidth( parseBoardUnits( "width" ) );
6377 NeedRIGHT();
6378 break;
6379
6380 case T_layer:
6381 arc->SetLayer( parseBoardItemLayer() );
6382 NeedRIGHT();
6383 break;
6384
6385 case T_layers:
6386 arc->SetLayerSet( parseLayersForCuItemWithSoldermask() );
6387 break;
6388
6389 case T_solder_mask_margin:
6390 arc->SetLocalSolderMaskMargin( parseBoardUnits( "local solder mask margin value" ) );
6391 NeedRIGHT();
6392 break;
6393
6394 case T_net:
6395 if( !arc->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
6396 {
6397 wxLogError( _( "Invalid net ID in\nfile: %s\nline: %d\noffset: %d." ),
6398 CurSource(), CurLineNumber(), CurOffset() );
6399 }
6400 NeedRIGHT();
6401 break;
6402
6403 case T_tstamp:
6404 case T_uuid:
6405 NextTok();
6406 const_cast<KIID&>( arc->m_Uuid ) = CurStrToKIID();
6407 NeedRIGHT();
6408 break;
6409
6410 // We continue to parse the status field but it is no longer written
6411 case T_status:
6412 parseHex();
6413 NeedRIGHT();
6414 break;
6415
6416 case T_locked:
6417 arc->SetLocked( parseMaybeAbsentBool( true ) );
6418 break;
6419
6420 default:
6421 Expecting( "start, mid, end, width, layer, solder_mask_margin, net, tstamp, uuid, "
6422 "or status" );
6423 }
6424 }
6425
6426 return arc.release();
6427}
6428
6429
6431{
6432 wxCHECK_MSG( CurTok() == T_segment, nullptr,
6433 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TRACK." ) );
6434
6435 VECTOR2I pt;
6436 T token;
6437
6438 std::unique_ptr<PCB_TRACK> track = std::make_unique<PCB_TRACK>( m_board );
6439
6440 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6441 {
6442 // Legacy locked flag
6443 if( token == T_locked )
6444 {
6445 track->SetLocked( true );
6446 token = NextTok();
6447 }
6448
6449 if( token != T_LEFT )
6450 Expecting( T_LEFT );
6451
6452 token = NextTok();
6453
6454 switch( token )
6455 {
6456 case T_start:
6457 pt.x = parseBoardUnits( "start x" );
6458 pt.y = parseBoardUnits( "start y" );
6459 track->SetStart( pt );
6460 NeedRIGHT();
6461 break;
6462
6463 case T_end:
6464 pt.x = parseBoardUnits( "end x" );
6465 pt.y = parseBoardUnits( "end y" );
6466 track->SetEnd( pt );
6467 NeedRIGHT();
6468 break;
6469
6470 case T_width:
6471 track->SetWidth( parseBoardUnits( "width" ) );
6472 NeedRIGHT();
6473 break;
6474
6475 case T_layer:
6476 track->SetLayer( parseBoardItemLayer() );
6477 NeedRIGHT();
6478 break;
6479
6480 case T_layers:
6481 track->SetLayerSet( parseLayersForCuItemWithSoldermask() );
6482 break;
6483
6484 case T_solder_mask_margin:
6485 track->SetLocalSolderMaskMargin( parseBoardUnits( "local solder mask margin value" ) );
6486 NeedRIGHT();
6487 break;
6488
6489 case T_net:
6490 if( !track->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
6491 {
6492 wxLogError( _( "Invalid net ID in\nfile: '%s'\nline: %d\noffset: %d." ),
6493 CurSource(), CurLineNumber(), CurOffset() );
6494 }
6495 NeedRIGHT();
6496 break;
6497
6498 case T_tstamp:
6499 case T_uuid:
6500 NextTok();
6501 const_cast<KIID&>( track->m_Uuid ) = CurStrToKIID();
6502 NeedRIGHT();
6503 break;
6504
6505 // We continue to parse the status field but it is no longer written
6506 case T_status:
6507 parseHex();
6508 NeedRIGHT();
6509 break;
6510
6511 case T_locked:
6512 track->SetLocked( parseMaybeAbsentBool( true ) );
6513 break;
6514
6515 default:
6516 Expecting( "start, end, width, layer, solder_mask_margin, net, tstamp, uuid, "
6517 "or locked" );
6518 }
6519 }
6520
6521 return track.release();
6522}
6523
6524
6526{
6527 wxCHECK_MSG( CurTok() == T_via, nullptr,
6528 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_VIA." ) );
6529
6530 VECTOR2I pt;
6531 T token;
6532
6533 std::unique_ptr<PCB_VIA> via = std::make_unique<PCB_VIA>( m_board );
6534
6535 // File format default is no-token == no-feature.
6536 via->Padstack().SetUnconnectedLayerMode( PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL );
6537
6538 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6539 {
6540 // Legacy locked
6541 if( token == T_locked )
6542 {
6543 via->SetLocked( true );
6544 token = NextTok();
6545 }
6546
6547 if( token == T_LEFT )
6548 token = NextTok();
6549
6550 switch( token )
6551 {
6552 case T_blind:
6553 via->SetViaType( VIATYPE::BLIND_BURIED );
6554 break;
6555
6556 case T_micro:
6557 via->SetViaType( VIATYPE::MICROVIA );
6558 break;
6559
6560 case T_at:
6561 pt.x = parseBoardUnits( "start x" );
6562 pt.y = parseBoardUnits( "start y" );
6563 via->SetStart( pt );
6564 via->SetEnd( pt );
6565 NeedRIGHT();
6566 break;
6567
6568 case T_size:
6569 via->SetWidth( PADSTACK::ALL_LAYERS, parseBoardUnits( "via width" ) );
6570 NeedRIGHT();
6571 break;
6572
6573 case T_drill:
6574 via->SetDrill( parseBoardUnits( "drill diameter" ) );
6575 NeedRIGHT();
6576 break;
6577
6578 case T_layers:
6579 {
6580 PCB_LAYER_ID layer1, layer2;
6581 NextTok();
6582 layer1 = lookUpLayer( m_layerIndices );
6583 NextTok();
6584 layer2 = lookUpLayer( m_layerIndices );
6585 via->SetLayerPair( layer1, layer2 );
6586
6587 if( layer1 == UNDEFINED_LAYER || layer2 == UNDEFINED_LAYER )
6588 Expecting( "layer name" );
6589
6590 NeedRIGHT();
6591 break;
6592 }
6593
6594 case T_net:
6595 if( !via->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
6596 {
6597 wxLogError( _( "Invalid net ID in\nfile: %s\nline: %d\noffset: %d" ),
6598 CurSource(), CurLineNumber(), CurOffset() );
6599 }
6600
6601 NeedRIGHT();
6602 break;
6603
6604 case T_remove_unused_layers:
6605 {
6606 bool remove = parseMaybeAbsentBool( true );
6607 via->SetRemoveUnconnected( remove );
6608 break;
6609 }
6610
6611 case T_keep_end_layers:
6612 {
6613 bool keep = parseMaybeAbsentBool( true );
6614 via->SetKeepStartEnd( keep );
6615 break;
6616 }
6617
6618 case T_zone_layer_connections:
6619 {
6620 // Ensure only copper layers are stored int ZoneLayerOverride array
6621 LSET cuLayers = via->GetLayerSet() & LSET::AllCuMask();
6622
6623 for( PCB_LAYER_ID layer : cuLayers.Seq() )
6624 {
6625 via->SetZoneLayerOverride( layer, ZLO_FORCE_NO_ZONE_CONNECTION );
6626 }
6627
6628 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6629 {
6631
6632 if( !IsCopperLayer( layer ) )
6633 Expecting( "copper layer name" );
6634
6635 via->SetZoneLayerOverride( layer, ZLO_FORCE_FLASHED );
6636 }
6637 }
6638 break;
6639
6640 case T_padstack:
6641 parseViastack( via.get() );
6642 break;
6643
6644 case T_teardrops:
6645 parseTEARDROP_PARAMETERS( &via->GetTeardropParams() );
6646 break;
6647
6648 case T_tenting:
6649 {
6650 auto [front, back] = parseFrontBackOptBool( true );
6651 via->Padstack().FrontOuterLayers().has_solder_mask = front;
6652 via->Padstack().BackOuterLayers().has_solder_mask = back;
6653 break;
6654 }
6655 case T_covering:
6656 {
6657 auto [front, back] = parseFrontBackOptBool();
6658 via->Padstack().FrontOuterLayers().has_covering = front;
6659 via->Padstack().BackOuterLayers().has_covering = back;
6660 break;
6661 }
6662 case T_plugging:
6663 {
6664 auto [front, back] = parseFrontBackOptBool();
6665 via->Padstack().FrontOuterLayers().has_plugging = front;
6666 via->Padstack().BackOuterLayers().has_plugging = back;
6667 break;
6668 }
6669 case T_filling:
6670 {
6671 via->Padstack().Drill().is_filled = parseOptBool();
6672 NeedRIGHT();
6673 break;
6674 }
6675 case T_capping:
6676 {
6677 via->Padstack().Drill().is_capped = parseOptBool();
6678 NeedRIGHT();
6679 break;
6680 }
6681
6682 case T_tstamp:
6683 case T_uuid:
6684 NextTok();
6685 const_cast<KIID&>( via->m_Uuid ) = CurStrToKIID();
6686 NeedRIGHT();
6687 break;
6688
6689 // We continue to parse the status field but it is no longer written
6690 case T_status:
6691 parseHex();
6692 NeedRIGHT();
6693 break;
6694
6695 case T_locked:
6696 via->SetLocked( parseMaybeAbsentBool( true ) );
6697 break;
6698
6699 case T_free:
6700 via->SetIsFree( parseMaybeAbsentBool( true ) );
6701 break;
6702
6703 default:
6704 Expecting( "blind, micro, at, size, drill, layers, net, free, tstamp, uuid, status or "
6705 "teardrops" );
6706 }
6707 }
6708
6709 return via.release();
6710}
6711
6712
6713std::pair<std::optional<bool>, std::optional<bool>>
6715{
6716 T token = NextTok();
6717
6718 std::optional<bool> front{ std::nullopt };
6719 std::optional<bool> back{ std::nullopt };
6720
6721 if( token != T_LEFT && aLegacy )
6722 {
6723 // legacy format for tenting.
6724 if( token == T_front || token == T_back || token == T_none )
6725 {
6726 while( token != T_RIGHT )
6727 {
6728 if( token == T_front )
6729 {
6730 front = true;
6731 }
6732 else if( token == T_back )
6733 {
6734 back = true;
6735 }
6736 else if( token == T_none )
6737 {
6738 front.reset();
6739 back.reset();
6740 }
6741 else
6742 {
6743 Expecting( "front, back or none" );
6744 }
6745
6746 token = NextTok();
6747 }
6748
6749 return { front, back };
6750 }
6751 }
6752
6753 while( token != T_RIGHT )
6754 {
6755 if( token != T_LEFT )
6756 Expecting( "(" );
6757
6758 token = NextTok();
6759
6760 if( token == T_front )
6761 front = parseOptBool();
6762 else if( token == T_back )
6763 back = parseOptBool();
6764 else
6765 Expecting( "front or back" );
6766
6767 NeedRIGHT();
6768
6769 token = NextTok();
6770 }
6771
6772 return { front, back };
6773}
6774
6775
6777{
6778 PADSTACK& padstack = aVia->Padstack();
6779
6780 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
6781 {
6782 if( token != T_LEFT )
6783 Expecting( T_LEFT );
6784
6785 token = NextTok();
6786
6787 switch( token )
6788 {
6789 case T_mode:
6790 token = NextTok();
6791
6792 switch( token )
6793 {
6794 case T_front_inner_back:
6796 break;
6797
6798 case T_custom:
6799 padstack.SetMode( PADSTACK::MODE::CUSTOM );
6800 break;
6801
6802 default:
6803 Expecting( "front_inner_back or custom" );
6804 }
6805
6806 NeedRIGHT();
6807 break;
6808
6809 case T_layer:
6810 {
6811 NextTok();
6812 PCB_LAYER_ID curLayer = UNDEFINED_LAYER;
6813
6814 if( curText == "Inner" )
6815 {
6816 if( padstack.Mode() != PADSTACK::MODE::FRONT_INNER_BACK )
6817 {
6818 THROW_IO_ERROR( wxString::Format( _( "Invalid padstack layer in\nfile: %s\n"
6819 "line: %d\noffset: %d." ),
6820 CurSource(), CurLineNumber(), CurOffset() ) );
6821 }
6822
6823 curLayer = PADSTACK::INNER_LAYERS;
6824 }
6825 else
6826 {
6827 curLayer = lookUpLayer( m_layerIndices );
6828 }
6829
6830 if( !IsCopperLayer( curLayer ) )
6831 {
6832 wxString error;
6833 error.Printf( _( "Invalid padstack layer '%s' in file '%s' at line %d, offset %d." ),
6834 curText, CurSource().GetData(), CurLineNumber(), CurOffset() );
6835 THROW_IO_ERROR( error );
6836 }
6837
6838 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6839 {
6840 if( token != T_LEFT )
6841 Expecting( T_LEFT );
6842
6843 token = NextTok();
6844
6845 switch( token )
6846 {
6847
6848 case T_size:
6849 {
6850 int diameter = parseBoardUnits( "via width" );
6851 padstack.SetSize( { diameter, diameter }, curLayer );
6852 NeedRIGHT();
6853 break;
6854 }
6855
6856 default:
6857 // Currently only supporting custom via diameter per layer, not other properties
6858 Expecting( "size" );
6859 }
6860 }
6861
6862 break;
6863 }
6864
6865 default:
6866 Expecting( "mode or layer" );
6867 break;
6868 }
6869 }
6870}
6871
6872
6874{
6875 wxCHECK_MSG( CurTok() == T_zone, nullptr,
6876 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as ZONE." ) );
6877
6878 ZONE_BORDER_DISPLAY_STYLE hatchStyle = ZONE_BORDER_DISPLAY_STYLE::NO_HATCH;
6879
6880 int hatchPitch = ZONE::GetDefaultHatchPitch();
6881 T token;
6882 int tmp;
6883 wxString netnameFromfile; // the zone net name find in file
6884
6885 // bigger scope since each filled_polygon is concatenated in here
6886 std::map<PCB_LAYER_ID, SHAPE_POLY_SET> pts;
6887 std::map<PCB_LAYER_ID, std::vector<SEG>> legacySegs;
6888 PCB_LAYER_ID filledLayer;
6889 bool addedFilledPolygons = false;
6890 bool isStrokedFill = true;
6891
6892 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aParent );
6893
6894 zone->SetAssignedPriority( 0 );
6895
6896 // This is the default for board files:
6897 zone->SetIslandRemovalMode( ISLAND_REMOVAL_MODE::ALWAYS );
6898
6899 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6900 {
6901 // legacy locked
6902 if( token == T_locked )
6903 {
6904 zone->SetLocked( true );
6905 token = NextTok();
6906 }
6907
6908 if( token == T_LEFT )
6909 token = NextTok();
6910
6911 switch( token )
6912 {
6913 case T_net:
6914 // Init the net code only, not the netname, to be sure
6915 // the zone net name is the name read in file.
6916 // (When mismatch, the user will be prompted in DRC, to fix the actual name)
6917 tmp = getNetCode( parseInt( "net number" ) );
6918
6919 if( tmp < 0 )
6920 tmp = 0;
6921
6922 if( !zone->SetNetCode( tmp, /* aNoAssert */ true ) )
6923 {
6924 wxLogError( _( "Invalid net ID in\nfile: %s;\nline: %d\noffset: %d." ),
6925 CurSource(), CurLineNumber(), CurOffset() );
6926 }
6927
6928 NeedRIGHT();
6929 break;
6930
6931 case T_net_name:
6932 NeedSYMBOLorNUMBER();
6933 netnameFromfile = FromUTF8();
6934 NeedRIGHT();
6935 break;
6936
6937 case T_layer: // keyword for zones that are on only one layer
6938 zone->SetLayer( parseBoardItemLayer() );
6939 NeedRIGHT();
6940 break;
6941
6942 case T_layers: // keyword for zones that can live on a set of layers
6943 zone->SetLayerSet( parseBoardItemLayersAsMask() );
6944 break;
6945
6946 case T_property:
6947 parseZoneLayerProperty( zone->LayerProperties() );
6948 break;
6949
6950 case T_tstamp:
6951 case T_uuid:
6952 NextTok();
6953 const_cast<KIID&>( zone->m_Uuid ) = CurStrToKIID();
6954 NeedRIGHT();
6955 break;
6956
6957 case T_hatch:
6958 token = NextTok();
6959
6960 if( token != T_none && token != T_edge && token != T_full )
6961 Expecting( "none, edge, or full" );
6962
6963 switch( token )
6964 {
6965 default:
6966 case T_none: hatchStyle = ZONE_BORDER_DISPLAY_STYLE::NO_HATCH; break;
6967 case T_edge: hatchStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE; break;
6968 case T_full: hatchStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL; break;
6969 }
6970
6971 hatchPitch = parseBoardUnits( "hatch pitch" );
6972 NeedRIGHT();
6973 break;
6974
6975 case T_priority:
6976 zone->SetAssignedPriority( parseInt( "zone priority" ) );
6977 NeedRIGHT();
6978 break;
6979
6980 case T_connect_pads:
6981 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
6982 {
6983 if( token == T_LEFT )
6984 token = NextTok();
6985
6986 switch( token )
6987 {
6988 case T_yes:
6989 zone->SetPadConnection( ZONE_CONNECTION::FULL );
6990 break;
6991
6992 case T_no:
6993 zone->SetPadConnection( ZONE_CONNECTION::NONE );
6994 break;
6995
6996 case T_thru_hole_only:
6997 zone->SetPadConnection( ZONE_CONNECTION::THT_THERMAL );
6998 break;
6999
7000 case T_clearance:
7001 zone->SetLocalClearance( parseBoardUnits( "zone clearance" ) );
7002 NeedRIGHT();
7003 break;
7004
7005 default:
7006 Expecting( "yes, no, or clearance" );
7007 }
7008 }
7009
7010 break;
7011
7012 case T_min_thickness:
7013 zone->SetMinThickness( parseBoardUnits( T_min_thickness ) );
7014 NeedRIGHT();
7015 break;
7016
7017 case T_filled_areas_thickness:
7018 // A new zone fill strategy was added in v6, so we need to know if we're parsing
7019 // a zone that was filled before that. Note that the change was implemented as
7020 // a new parameter, so we need to check for the presence of filled_areas_thickness
7021 // instead of just its value.
7022
7023 if( !parseBool() )
7024 isStrokedFill = false;
7025
7026 NeedRIGHT();
7027 break;
7028
7029 case T_fill:
7030 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7031 {
7032 if( token == T_LEFT )
7033 token = NextTok();
7034
7035 switch( token )
7036 {
7037 case T_yes:
7038 zone->SetIsFilled( true );
7039 break;
7040
7041 case T_mode:
7042 token = NextTok();
7043
7044 if( token != T_segment && token != T_hatch && token != T_polygon )
7045 Expecting( "segment, hatch or polygon" );
7046
7047 switch( token )
7048 {
7049 case T_hatch:
7050 zone->SetFillMode( ZONE_FILL_MODE::HATCH_PATTERN );
7051 break;
7052
7053 case T_segment: // deprecated, convert to polygons
7054 case T_polygon:
7055 default:
7056 zone->SetFillMode( ZONE_FILL_MODE::POLYGONS );
7057 break;
7058 }
7059
7060 NeedRIGHT();
7061 break;
7062
7063 case T_hatch_thickness:
7064 zone->SetHatchThickness( parseBoardUnits( T_hatch_thickness ) );
7065 NeedRIGHT();
7066 break;
7067
7068 case T_hatch_gap:
7069 zone->SetHatchGap( parseBoardUnits( T_hatch_gap ) );
7070 NeedRIGHT();
7071 break;
7072
7073 case T_hatch_orientation:
7074 {
7075 EDA_ANGLE orientation( parseDouble( T_hatch_orientation ), DEGREES_T );
7076 zone->SetHatchOrientation( orientation );
7077 NeedRIGHT();
7078 break;
7079 }
7080
7081 case T_hatch_smoothing_level:
7082 zone->SetHatchSmoothingLevel( parseDouble( T_hatch_smoothing_level ) );
7083 NeedRIGHT();
7084 break;
7085
7086 case T_hatch_smoothing_value:
7087 zone->SetHatchSmoothingValue( parseDouble( T_hatch_smoothing_value ) );
7088 NeedRIGHT();
7089 break;
7090
7091 case T_hatch_border_algorithm:
7092 token = NextTok();
7093
7094 if( token != T_hatch_thickness && token != T_min_thickness )
7095 Expecting( "hatch_thickness or min_thickness" );
7096
7097 zone->SetHatchBorderAlgorithm( token == T_hatch_thickness ? 1 : 0 );
7098 NeedRIGHT();
7099 break;
7100
7101 case T_hatch_min_hole_area:
7102 zone->SetHatchHoleMinArea( parseDouble( T_hatch_min_hole_area ) );
7103 NeedRIGHT();
7104 break;
7105
7106 case T_arc_segments:
7107 ignore_unused( parseInt( "arc segment count" ) );
7108 NeedRIGHT();
7109 break;
7110
7111 case T_thermal_gap:
7112 zone->SetThermalReliefGap( parseBoardUnits( T_thermal_gap ) );
7113 NeedRIGHT();
7114 break;
7115
7116 case T_thermal_bridge_width:
7117 zone->SetThermalReliefSpokeWidth( parseBoardUnits( T_thermal_bridge_width ) );
7118 NeedRIGHT();
7119 break;
7120
7121 case T_smoothing:
7122 switch( NextTok() )
7123 {
7124 case T_none:
7125 zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_NONE );
7126 break;
7127
7128 case T_chamfer:
7129 if( !zone->GetIsRuleArea() ) // smoothing has meaning only for filled zones
7130 zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_CHAMFER );
7131
7132 break;
7133
7134 case T_fillet:
7135 if( !zone->GetIsRuleArea() ) // smoothing has meaning only for filled zones
7136 zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_FILLET );
7137
7138 break;
7139
7140 default:
7141 Expecting( "none, chamfer, or fillet" );
7142 }
7143
7144 NeedRIGHT();
7145 break;
7146
7147 case T_radius:
7148 tmp = parseBoardUnits( "corner radius" );
7149
7150 if( !zone->GetIsRuleArea() ) // smoothing has meaning only for filled zones
7151 zone->SetCornerRadius( tmp );
7152
7153 NeedRIGHT();
7154 break;
7155
7156 case T_island_removal_mode:
7157 tmp = parseInt( "island_removal_mode" );
7158
7159 if( tmp >= 0 && tmp <= 2 )
7160 zone->SetIslandRemovalMode( static_cast<ISLAND_REMOVAL_MODE>( tmp ) );
7161
7162 NeedRIGHT();
7163 break;
7164
7165 case T_island_area_min:
7166 {
7167 int area = parseBoardUnits( T_island_area_min );
7168 zone->SetMinIslandArea( area * pcbIUScale.IU_PER_MM );
7169 NeedRIGHT();
7170 break;
7171 }
7172
7173 default:
7174 Expecting( "mode, arc_segments, thermal_gap, thermal_bridge_width, "
7175 "hatch_thickness, hatch_gap, hatch_orientation, "
7176 "hatch_smoothing_level, hatch_smoothing_value, "
7177 "hatch_border_algorithm, hatch_min_hole_area, smoothing, radius, "
7178 "island_removal_mode, or island_area_min" );
7179 }
7180 }
7181
7182 break;
7183
7184 case T_placement:
7185 zone->SetIsRuleArea( true );
7186
7187 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7188 {
7189 if( token == T_LEFT )
7190 token = NextTok();
7191
7192 switch( token )
7193 {
7194 case T_sheetname:
7195 {
7196 zone->SetRuleAreaPlacementSourceType(
7197 RULE_AREA_PLACEMENT_SOURCE_TYPE::SHEETNAME );
7198 NeedSYMBOL();
7199 zone->SetRuleAreaPlacementSource( FromUTF8() );
7200 break;
7201 }
7202 case T_component_class:
7203 {
7204 zone->SetRuleAreaPlacementSourceType(
7205 RULE_AREA_PLACEMENT_SOURCE_TYPE::COMPONENT_CLASS );
7206 NeedSYMBOL();
7207 zone->SetRuleAreaPlacementSource( FromUTF8() );
7208 break;
7209 }
7210 case T_enabled:
7211 {
7212 token = NextTok();
7213
7214 if( token == T_yes )
7215 zone->SetRuleAreaPlacementEnabled( true );
7216 else if( token == T_no )
7217 zone->SetRuleAreaPlacementEnabled( false );
7218 else
7219 Expecting( "yes or no" );
7220
7221 break;
7222 }
7223 default:
7224 {
7225 Expecting( "enabled, sheetname or component_class" );
7226 break;
7227 }
7228 }
7229
7230 NeedRIGHT();
7231 }
7232
7233 break;
7234
7235 case T_keepout:
7236 // "keepout" now means rule area, but the file token stays the same
7237 zone->SetIsRuleArea( true );
7238
7239 // Initialize these two because their tokens won't appear in older files:
7240 zone->SetDoNotAllowPads( false );
7241 zone->SetDoNotAllowFootprints( false );
7242
7243 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7244 {
7245 if( token == T_LEFT )
7246 token = NextTok();
7247
7248 switch( token )
7249 {
7250 case T_tracks:
7251 token = NextTok();
7252
7253 if( token != T_allowed && token != T_not_allowed )
7254 Expecting( "allowed or not_allowed" );
7255
7256 zone->SetDoNotAllowTracks( token == T_not_allowed );
7257 break;
7258
7259 case T_vias:
7260 token = NextTok();
7261
7262 if( token != T_allowed && token != T_not_allowed )
7263 Expecting( "allowed or not_allowed" );
7264
7265 zone->SetDoNotAllowVias( token == T_not_allowed );
7266 break;
7267
7268 case T_copperpour:
7269 token = NextTok();
7270
7271 if( token != T_allowed && token != T_not_allowed )
7272 Expecting( "allowed or not_allowed" );
7273
7274 zone->SetDoNotAllowZoneFills( token == T_not_allowed );
7275 break;
7276
7277 case T_pads:
7278 token = NextTok();
7279
7280 if( token != T_allowed && token != T_not_allowed )
7281 Expecting( "allowed or not_allowed" );
7282
7283 zone->SetDoNotAllowPads( token == T_not_allowed );
7284 break;
7285
7286 case T_footprints:
7287 token = NextTok();
7288
7289 if( token != T_allowed && token != T_not_allowed )
7290 Expecting( "allowed or not_allowed" );
7291
7292 zone->SetDoNotAllowFootprints( token == T_not_allowed );
7293 break;
7294
7295 default:
7296 Expecting( "tracks, vias or copperpour" );
7297 }
7298
7299 NeedRIGHT();
7300 }
7301
7302 break;
7303
7304 case T_polygon:
7305 {
7306 SHAPE_LINE_CHAIN outline;
7307
7308 NeedLEFT();
7309 token = NextTok();
7310
7311 if( token != T_pts )
7312 Expecting( T_pts );
7313
7314 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7315 parseOutlinePoints( outline );
7316
7317 NeedRIGHT();
7318
7319 outline.SetClosed( true );
7320
7321 // Remark: The first polygon is the main outline.
7322 // Others are holes inside the main outline.
7323 zone->AddPolygon( outline );
7324 break;
7325 }
7326
7327 case T_filled_polygon:
7328 {
7329 // "(filled_polygon (pts"
7330 NeedLEFT();
7331 token = NextTok();
7332
7333 if( token == T_layer )
7334 {
7335 filledLayer = parseBoardItemLayer();
7336 NeedRIGHT();
7337 token = NextTok();
7338
7339 if( token != T_LEFT )
7340 Expecting( T_LEFT );
7341
7342 token = NextTok();
7343 }
7344 else
7345 {
7346 // for legacy, single-layer zones
7347 filledLayer = zone->GetFirstLayer();
7348 }
7349
7350 bool island = false;
7351
7352 if( token == T_island )
7353 {
7354 island = true;
7355 NeedRIGHT();
7356 NeedLEFT();
7357 token = NextTok();
7358 }
7359
7360 if( token != T_pts )
7361 Expecting( T_pts );
7362
7363 if( !pts.count( filledLayer ) )
7364 pts[filledLayer] = SHAPE_POLY_SET();
7365
7366 SHAPE_POLY_SET& poly = pts.at( filledLayer );
7367
7368 int idx = poly.NewOutline();
7369 SHAPE_LINE_CHAIN& chain = poly.Outline( idx );
7370
7371 if( island )
7372 zone->SetIsIsland( filledLayer, idx );
7373
7374 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7376
7377 NeedRIGHT();
7378
7379 addedFilledPolygons |= !poly.IsEmpty();
7380 }
7381
7382 break;
7383
7384 case T_fill_segments:
7385 {
7386 // Legacy segment fill
7387
7388 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7389 {
7390 if( token != T_LEFT )
7391 Expecting( T_LEFT );
7392
7393 token = NextTok();
7394
7395 if( token != T_pts )
7396 Expecting( T_pts );
7397
7398 // Legacy zones only had one layer
7399 filledLayer = zone->GetFirstLayer();
7400
7401 SEG fillSegment;
7402
7403 fillSegment.A = parseXY();
7404 fillSegment.B = parseXY();
7405
7406 legacySegs[filledLayer].push_back( fillSegment );
7407
7408 NeedRIGHT();
7409 }
7410
7411 break;
7412 }
7413
7414 case T_name:
7415 NextTok();
7416 zone->SetZoneName( FromUTF8() );
7417 NeedRIGHT();
7418 break;
7419
7420 case T_attr:
7421 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7422 {
7423 if( token == T_LEFT )
7424 token = NextTok();
7425
7426 switch( token )
7427 {
7428 case T_teardrop:
7429 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7430 {
7431 if( token == T_LEFT )
7432 token = NextTok();
7433
7434 switch( token )
7435 {
7436 case T_type:
7437 token = NextTok();
7438
7439 if( token == T_padvia )
7440 zone->SetTeardropAreaType( TEARDROP_TYPE::TD_VIAPAD );
7441 else if( token == T_track_end )
7442 zone->SetTeardropAreaType( TEARDROP_TYPE::TD_TRACKEND );
7443 else
7444 Expecting( "padvia or track_end" );
7445
7446 NeedRIGHT();
7447 break;
7448
7449 default:
7450 Expecting( "type" );
7451 }
7452 }
7453
7454 break;
7455
7456 default:
7457 Expecting( "teardrop" );
7458 }
7459 }
7460 break;
7461
7462 case T_locked:
7463 zone->SetLocked( parseBool() );
7464 NeedRIGHT();
7465 break;
7466
7467 default:
7468 Expecting( "net, layer/layers, tstamp, hatch, priority, connect_pads, min_thickness, "
7469 "fill, polygon, filled_polygon, fill_segments, attr, locked, uuid, or name" );
7470 }
7471 }
7472
7473 if( zone->GetNumCorners() > 2 )
7474 {
7475 if( !zone->IsOnCopperLayer() )
7476 {
7477 //zone->SetFillMode( ZONE_FILL_MODE::POLYGONS );
7478 zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
7479 }
7480
7481 // Set hatch here, after outlines corners are read
7482 zone->SetBorderDisplayStyle( hatchStyle, hatchPitch, true );
7483 }
7484
7485 if( addedFilledPolygons )
7486 {
7487 if( isStrokedFill && !zone->GetIsRuleArea() )
7488 {
7490 {
7491 wxLogWarning(
7492 _( "Legacy zone fill strategy is not supported anymore.\nZone fills will "
7493 "be converted on best-effort basis." ) );
7494
7496 }
7497
7498 if( zone->GetMinThickness() > 0 )
7499 {
7500 for( auto& [layer, polyset] : pts )
7501 {
7502 polyset.InflateWithLinkedHoles( zone->GetMinThickness() / 2,
7503 CORNER_STRATEGY::ROUND_ALL_CORNERS,
7504 ARC_HIGH_DEF / 2 );
7505 }
7506 }
7507 }
7508
7509 for( auto& [layer, polyset] : pts )
7510 zone->SetFilledPolysList( layer, polyset );
7511
7512 zone->CalculateFilledArea();
7513 }
7514 else if( legacySegs.size() > 0 )
7515 {
7516 // No polygons, just segment fill?
7517 // Note RFB: This code might be removed if turns out this never existed for sexpr file
7518 // format or otherwise we should add a test case to the qa folder
7519
7521 {
7522 wxLogWarning( _( "The legacy segment zone fill mode is no longer supported.\n"
7523 "Zone fills will be converted on a best-effort basis." ) );
7524
7526 }
7527
7528
7529 for( const auto& [layer, segments] : legacySegs )
7530 {
7531 SHAPE_POLY_SET layerFill;
7532
7533 if( zone->HasFilledPolysForLayer( layer ) )
7534 layerFill = SHAPE_POLY_SET( *zone->GetFill( layer ) );
7535
7536 for( const auto& seg : segments )
7537 {
7538 SHAPE_POLY_SET segPolygon;
7539
7540 TransformOvalToPolygon( segPolygon, seg.A, seg.B, zone->GetMinThickness(),
7542
7543 layerFill.BooleanAdd( segPolygon );
7544 }
7545
7546
7547 zone->SetFilledPolysList( layer, layerFill );
7548 zone->CalculateFilledArea();
7549 }
7550 }
7551
7552
7553 // Ensure keepout and non copper zones do not have a net
7554 // (which have no sense for these zones)
7555 // the netcode 0 is used for these zones
7556 bool zone_has_net = zone->IsOnCopperLayer() && !zone->GetIsRuleArea();
7557
7558 if( !zone_has_net )
7559 zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
7560
7561 // Ensure the zone net name is valid, and matches the net code, for copper zones
7562 if( zone_has_net
7563 && ( !zone->GetNet() || zone->GetNet()->GetNetname() != netnameFromfile ) )
7564 {
7565 // Can happens which old boards, with nonexistent nets ...
7566 // or after being edited by hand
7567 // We try to fix the mismatch.
7568 NETINFO_ITEM* net = m_board->FindNet( netnameFromfile );
7569
7570 if( net ) // An existing net has the same net name. use it for the zone
7571 {
7572 zone->SetNetCode( net->GetNetCode() );
7573 }
7574 else // Not existing net: add a new net to keep trace of the zone netname
7575 {
7576 int newnetcode = m_board->GetNetCount();
7577 net = new NETINFO_ITEM( m_board, netnameFromfile, newnetcode );
7578 m_board->Add( net, ADD_MODE::INSERT, true );
7579
7580 // Store the new code mapping
7581 pushValueIntoMap( newnetcode, net->GetNetCode() );
7582
7583 // and update the zone netcode
7584 zone->SetNetCode( net->GetNetCode() );
7585 }
7586 }
7587
7588 if( zone->IsTeardropArea() && m_requiredVersion < 20230517 )
7589 m_board->SetLegacyTeardrops( true );
7590
7591 // Clear flags used in zone edition:
7592 zone->SetNeedRefill( false );
7593
7594 return zone.release();
7595}
7596
7597
7599{
7600 wxCHECK_MSG( CurTok() == T_target, nullptr,
7601 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TARGET." ) );
7602
7603 VECTOR2I pt;
7604 T token;
7605
7606 std::unique_ptr<PCB_TARGET> target = std::make_unique<PCB_TARGET>( nullptr );
7607
7608 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
7609 {
7610 if( token == T_LEFT )
7611 token = NextTok();
7612
7613 switch( token )
7614 {
7615 case T_x:
7616 target->SetShape( 1 );
7617 break;
7618
7619 case T_plus:
7620 target->SetShape( 0 );
7621 break;
7622
7623 case T_at:
7624 pt.x = parseBoardUnits( "target x position" );
7625 pt.y = parseBoardUnits( "target y position" );
7626 target->SetPosition( pt );
7627 NeedRIGHT();
7628 break;
7629
7630 case T_size:
7631 target->SetSize( parseBoardUnits( "target size" ) );
7632 NeedRIGHT();
7633 break;
7634
7635 case T_width:
7636 target->SetWidth( parseBoardUnits( "target thickness" ) );
7637 NeedRIGHT();
7638 break;
7639
7640 case T_layer:
7641 target->SetLayer( parseBoardItemLayer() );
7642 NeedRIGHT();
7643 break;
7644
7645 case T_tstamp:
7646 case T_uuid:
7647 NextTok();
7648 const_cast<KIID&>( target->m_Uuid ) = CurStrToKIID();
7649 NeedRIGHT();
7650 break;
7651
7652 default:
7653 Expecting( "x, plus, at, size, width, layer, uuid, or tstamp" );
7654 }
7655 }
7656
7657 return target.release();
7658}
7659
7660
7662{
7663 KIID aId;
7664 std::string idStr( CurStr() );
7665
7666 // Older files did not quote UUIDs
7667 if( *idStr.begin() == '"' && *idStr.rbegin() == '"' )
7668 idStr = idStr.substr( 1, idStr.length() - 1 );
7669
7670 if( m_appendToExisting )
7671 {
7672 aId = KIID();
7673 m_resetKIIDMap.insert( std::make_pair( idStr, aId ) );
7674 }
7675 else
7676 {
7677 aId = KIID( idStr );
7678 }
7679
7680 return aId;
7681}
int color
Definition: DXF_plotter.cpp:60
const char * name
Definition: DXF_plotter.cpp:59
@ ERROR_OUTSIDE
Definition: approximation.h:33
constexpr int ARC_HIGH_DEF
Definition: base_units.h:120
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
@ LT_UNDEFINED
Definition: board.h:160
@ LAYER_CLASS_OTHERS
@ LAYER_CLASS_FAB
@ LAYER_CLASS_COURTYARD
@ LAYER_CLASS_SILK
@ LAYER_CLASS_COPPER
@ LAYER_CLASS_EDGES
#define DEFAULT_LINE_WIDTH
@ ZLO_FORCE_NO_ZONE_CONNECTION
Definition: board_item.h:69
@ ZLO_FORCE_FLASHED
Definition: board_item.h:68
@ BS_EDGE_CONNECTOR_BEVELLED
Definition: board_stackup.h:59
@ BS_EDGE_CONNECTOR_NONE
Definition: board_stackup.h:57
@ BS_EDGE_CONNECTOR_IN_USE
Definition: board_stackup.h:58
BOARD_STACKUP_ITEM_TYPE
Definition: board_stackup.h:43
@ BS_ITEM_TYPE_UNDEFINED
Definition: board_stackup.h:44
@ BS_ITEM_TYPE_COPPER
Definition: board_stackup.h:45
@ BS_ITEM_TYPE_SILKSCREEN
Definition: board_stackup.h:51
@ BS_ITEM_TYPE_DIELECTRIC
Definition: board_stackup.h:46
@ BS_ITEM_TYPE_SOLDERPASTE
Definition: board_stackup.h:48
@ BS_ITEM_TYPE_SOLDERMASK
Definition: board_stackup.h:49
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
BASE_SET & reset(size_t pos)
Definition: base_set.h:143
BASE_SET & set(size_t pos)
Definition: base_set.h:116
Container for design settings for a BOARD object.
DIM_PRECISION m_DimensionPrecision
Number of digits after the decimal.
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)
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()
VECTOR2I 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.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:78
virtual void SetLocked(bool aLocked)
Definition: board_item.h:324
virtual void SetIsKnockout(bool aKnockout)
Definition: board_item.h:321
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:284
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:298
virtual void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const
Invoke a function on all children.
Definition: board_item.h:209
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:211
Manage one layer needed to make a physical board.
Definition: board_stackup.h:96
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 SetColor(const wxString &aColorName, int aDielectricSubLayer=0)
void SetEpsilonR(double aEpsilon, int aDielectricSubLayer=0)
Manage layers needed to make a physical board.
void RemoveAll()
Delete all items in list and clear the list.
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.
void BuildDefaultStackupList(const BOARD_DESIGN_SETTINGS *aSettings, int aActiveCopperLayersCount=0)
Create a default stackup, according to the current BOARD_DESIGN_SETTINGS settings.
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:296
void SetPlotOptions(const PCB_PLOT_PARAMS &aOptions)
Definition: board.h:718
bool m_LegacyDesignSettingsLoaded
True if the legacy board design settings were loaded from a file.
Definition: board.h:379
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:830
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition: board.cpp:2643
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: board.cpp:1061
GAL_SET m_LegacyVisibleItems
Definition: board.h:376
void SetProperties(const std::map< wxString, wxString > &aProps)
Definition: board.h:369
bool SetLayerDescr(PCB_LAYER_ID aIndex, const LAYER &aLayer)
Return the type of the copper layer given by aLayer.
Definition: board.cpp:587
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:2012
const ZONES & Zones() const
Definition: board.h:341
void FixupEmbeddedData()
After loading a file from disk, the footprints do not yet contain the full data for their embedded fi...
Definition: board.cpp:1041
PCB_LAYER_ID GetLayerID(const wxString &aLayerName) const
Return the ID of a layer.
Definition: board.cpp:595
LSET m_LegacyVisibleLayers
Visibility settings stored in board prior to 6.0, only used for loading legacy files.
Definition: board.h:375
int GetCopperLayerCount() const
Definition: board.cpp:782
const FOOTPRINTS & Footprints() const
Definition: board.h:337
const TRACKS & Tracks() const
Definition: board.h:335
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition: board.h:715
bool m_LegacyNetclassesLoaded
True if netclasses were loaded from the file.
Definition: board.h:383
void SetCopperLayerCount(int aCount)
Definition: board.cpp:788
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:615
void SetLegacyTeardrops(bool aFlag)
Definition: board.h:1292
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:1185
wxString GroupsSanityCheck(bool repair=false)
Consistency check of internal m_groups structure.
Definition: board.cpp:2854
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:947
void SetGenerator(const wxString &aGenerator)
Definition: board.h:404
COMPONENT_CLASS_MANAGER & GetComponentClassManager()
Gets the component class manager.
Definition: board.h:1310
unsigned GetNetCount() const
Definition: board.h:927
void SetEnabledLayers(const LSET &aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:850
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition: board.h:722
bool m_LegacyCopperEdgeClearanceLoaded
Definition: board.h:380
const DRAWINGS & Drawings() const
Definition: board.h:339
void SetFileFormatVersionAtLoad(int aVersion)
Definition: board.h:401
const COMPONENT_CLASS * GetNoneComponentClass() const
Returns the unassigned component class.
const KIID m_Uuid
Definition: eda_item.h:494
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:107
virtual EMBEDDED_FILES * GetEmbeddedFiles()
Definition: eda_item.h:449
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:337
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:177
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:167
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:219
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:80
const EDA_ANGLE & GetTextAngle() const
Definition: eda_text.h:134
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition: eda_text.cpp:526
void SetUnresolvedFontName(const wxString &aFontName)
Definition: eda_text.h:236
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:98
void SetTextPos(const VECTOR2I &aPoint)
Definition: eda_text.cpp:571
void SetMirrored(bool isMirrored)
Definition: eda_text.cpp:386
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition: eda_text.cpp:410
void SetBoldFlag(bool aBold)
Set only the bold flag, without changing the font.
Definition: eda_text.cpp:371
virtual void SetVisible(bool aVisible)
Definition: eda_text.cpp:379
void SetLineSpacing(double aLineSpacing)
Definition: eda_text.cpp:518
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.cpp:284
void SetItalicFlag(bool aItalic)
Set only the italic flag, without changing the font.
Definition: eda_text.cpp:320
void SetKeepUpright(bool aKeepUpright)
Definition: eda_text.cpp:418
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:270
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition: eda_text.cpp:292
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition: eda_text.cpp:402
void ParseEmbedded(EMBEDDED_FILES *aFiles)
const std::vector< wxString > * UpdateFontFiles()
Helper function to get a list of fonts for fontconfig to add to the library.
void SetAreFontsEmbedded(bool aEmbedFonts)
EDA_ANGLE GetOrientation() const
Definition: footprint.h:227
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: footprint.cpp:1048
VECTOR2I GetPosition() const override
Definition: footprint.h:224
VECTOR3D m_Offset
3D model offset (mm)
Definition: footprint.h:106
double m_Opacity
Definition: footprint.h:107
VECTOR3D m_Rotation
3D model rotation (degrees)
Definition: footprint.h:105
VECTOR3D m_Scale
3D model scaling factor (dimensionless)
Definition: footprint.h:104
wxString m_Filename
The 3D shape filename in 3D library.
Definition: footprint.h:108
bool m_Show
Include model in rendering.
Definition: footprint.h:109
GAL_SET & set()
Definition: layer_ids.h:408
static GAL_SET DefaultVisible()
Definition: lset.cpp:755
A factory which returns an instance of a PCB_GENERATOR.
PCB_GENERATOR * CreateFromType(const wxString &aTypeStr)
static GENERATORS_MGR & Instance()
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
Definition: kiid.h:49
wxString AsString() const
Definition: kiid.cpp:246
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:49
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition: lib_id.cpp:52
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:49
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:37
static LSET InternalCuMask()
Return a complete set of internal copper layers which is all Cu layers except F_Cu and B_Cu.
Definition: lset.cpp:561
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:572
static int NameToLayer(wxString &aName)
Return the layer number from a layer name.
Definition: lset.cpp:117
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:297
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition: lset.cpp:188
Handle the data for a net.
Definition: netinfo.h:56
const wxString & GetNetname() const
Definition: netinfo.h:114
void SetNetCode(int aNetCode)
Definition: netinfo.h:109
int GetNetCode() const
Definition: netinfo.h:108
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition: netinfo.h:381
static const int ORPHANED
Constant that forces initialization of a netinfo item to the NETINFO_ITEM ORPHANED (typically -1) whe...
Definition: netinfo.h:385
A PADSTACK defines the characteristics of a single or multi-layer pad, in the IPC sense of the word.
Definition: padstack.h:124
std::optional< int > & Clearance(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1181
void AddPrimitive(PCB_SHAPE *aShape, PCB_LAYER_ID aLayer)
Adds a custom shape primitive to the padstack.
Definition: padstack.cpp:1307
MASK_LAYER_PROPS & FrontOuterLayers()
Definition: padstack.h:318
void SetThermalSpokeAngle(EDA_ANGLE aAngle, PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1289
void SetMode(MODE aMode)
Definition: padstack.cpp:830
std::optional< int > & ThermalSpokeWidth(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1246
std::optional< int > & ThermalGap(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1258
void SetAnchorShape(PAD_SHAPE aShape, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1104
@ CUSTOM
Shapes can be defined on arbitrary layers.
@ FRONT_INNER_BACK
Up to three shapes can be defined (F_Cu, inner copper layers, B_Cu)
MODE Mode() const
Definition: padstack.h:293
MASK_LAYER_PROPS & BackOuterLayers()
Definition: padstack.h:321
void SetSize(const VECTOR2I &aSize, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1059
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition: padstack.h:144
static constexpr PCB_LAYER_ID INNER_LAYERS
! The layer identifier to use for "inner layers" on top/inner/bottom padstacks
Definition: padstack.h:147
std::optional< ZONE_CONNECTION > & ZoneConnection(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1234
Definition: pad.h:54
void SetAnchorPadShape(PCB_LAYER_ID aLayer, PAD_SHAPE aShape)
Set the shape of the anchor pad for custom shaped pads.
Definition: pad.h:240
void SetShape(PCB_LAYER_ID aLayer, PAD_SHAPE aShape)
Set the new shape of this pad.
Definition: pad.h:184
void SetDelta(PCB_LAYER_ID aLayer, const VECTOR2I &aSize)
Definition: pad.h:291
void SetOffset(PCB_LAYER_ID aLayer, const VECTOR2I &aOffset)
Definition: pad.h:309
void SetChamferRectRatio(PCB_LAYER_ID aLayer, double aChamferScale)
Has meaning only for chamfered rectangular pads.
Definition: pad.cpp:498
const PADSTACK & Padstack() const
Definition: pad.h:319
void SetSize(PCB_LAYER_ID aLayer, const VECTOR2I &aSize)
Definition: pad.h:257
void SetChamferPositions(PCB_LAYER_ID aLayer, int aPositions)
Has meaning only for chamfered rectangular pads.
Definition: pad.h:701
void SetCustomShapeInZoneOpt(PADSTACK::CUSTOM_SHAPE_ZONE_MODE aOption)
Set the option for the custom pad shape to use as clearance area in copper zones.
Definition: pad.h:229
void SetRoundRectRadiusRatio(PCB_LAYER_ID aLayer, double aRadiusScale)
Has meaning only for rounded rectangle pads.
Definition: pad.cpp:460
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition: page_info.h:59
void SetPortrait(bool aIsPortrait)
Rotate the paper page 90 degrees.
Definition: page_info.cpp:188
static const wxChar Custom[]
"User" defined page type
Definition: page_info.h:82
void SetWidthMM(double aWidthInMM)
Definition: page_info.h:134
void SetHeightMM(double aHeightInMM)
Definition: page_info.h:139
bool SetType(const wxString &aStandardPageDescriptionName, bool aIsPortrait=false)
Set the name of the page type and also the sizes and margins commonly associated with that type name.
Definition: page_info.cpp:121
Abstract dimension API.
For better understanding of the points that make a dimension:
void SetExtensionHeight(int aHeight)
void UpdateHeight(const VECTOR2I &aCrossbarStart, const VECTOR2I &aCrossbarEnd)
Update the stored height basing on points coordinates.
void SetHeight(int aHeight)
Set the distance from the feature points to the crossbar line.
A leader is a dimension-like object pointing to a specific point.
void SetTextBorder(DIM_TEXT_BORDER aBorder)
An orthogonal dimension is like an aligned dimension, but the extension lines are locked to the X or ...
A radial dimension indicates either the radius or diameter of an arc or circle.
void SetLeaderLength(int aLength)
virtual void SetProperties(const STRING_ANY_MAP &aProps)
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:52
wxString m_generatorVersion
Set to the generator version this board requires.
PCB_TABLECELL * parsePCB_TABLECELL(BOARD_ITEM *aParent)
std::unordered_map< std::string, PCB_LAYER_ID > LAYER_ID_MAP
std::vector< int > m_netCodes
net codes mapping for boards being loaded
void parseOutlinePoints(SHAPE_LINE_CHAIN &aPoly)
Parses possible outline points and stores them into aPoly.
std::set< wxString > m_undefinedLayers
set of layers not defined in layers section
void parseZoneLayerProperty(std::map< PCB_LAYER_ID, ZONE_LAYER_PROPERTIES > &aProperties)
PROGRESS_REPORTER * m_progressReporter
optional; may be nullptr
void createOldLayerMapping(std::unordered_map< std::string, std::string > &aMap)
Create a mapping from the (short-lived) bug where layer names were translated.
void parseZoneDefaults(ZONE_SETTINGS &aZoneSettings)
std::unordered_map< std::string, LSET > LSET_MAP
void parseEDA_TEXT(EDA_TEXT *aText)
Parse the common settings for any object derived from EDA_TEXT.
bool m_tooRecent
true if version parses as later than supported
PCB_LAYER_ID lookUpLayer(const LAYER_ID_MAP &aMap)
Parse the current token for the layer definition of a BOARD_ITEM object.
PCB_REFERENCE_IMAGE * parsePCB_REFERENCE_IMAGE(BOARD_ITEM *aParent)
LAYER_ID_MAP m_layerIndices
map layer name to it's index
void parseTextBoxContent(PCB_TEXTBOX *aTextBox)
FOOTPRINT * parseFOOTPRINT(wxArrayString *aInitialComments=nullptr)
void pushValueIntoMap(int aIndex, int aValue)
Add aValue value in netcode mapping (m_netCodes) at aIndex.
void init()
Clear and re-establish m_layerMap with the default layer names.
void parseGROUP(BOARD_ITEM *aParent)
void skipCurrent()
Skip the current token level, i.e search for the RIGHT parenthesis which closes the current descripti...
void parseMargins(int &aLeft, int &aTop, int &aRight, int &aBottom)
PCB_LAYER_ID parseBoardItemLayer()
Parse the layer definition of a BOARD_ITEM object.
LSET parseLayersForCuItemWithSoldermask()
Parse the layers definition of a BOARD_ITEM object that has a single copper layer and optional solder...
void parseGENERATOR(BOARD_ITEM *aParent)
LSET parseBoardItemLayersAsMask()
Parse the layers definition of a BOARD_ITEM object.
std::pair< std::optional< bool >, std::optional< bool > > parseFrontBackOptBool(bool aLegacy=false)
void resolveGroups(BOARD_ITEM *aParent)
Called after parsing a footprint definition or board to build the group membership lists.
void parseDefaultTextDims(BOARD_DESIGN_SETTINGS &aSettings, int aLayer)
std::vector< GROUP_INFO > m_groupInfos
std::optional< bool > parseOptBool()
ZONE * parseZONE(BOARD_ITEM_CONTAINER *aParent)
PCB_TABLE * parsePCB_TABLE(BOARD_ITEM *aParent)
std::vector< GENERATOR_INFO > m_generatorInfos
PCB_TEXTBOX * parsePCB_TEXTBOX(BOARD_ITEM *aParent)
std::chrono::time_point< CLOCK > TIME_PT
PCB_TEXT * parsePCB_TEXT(BOARD_ITEM *aParent, PCB_TEXT *aBaseText=nullptr)
unsigned m_lineCount
for progress reporting
VECTOR2I parseXY()
Parse a coordinate pair (xy X Y) in board units (mm).
void parseTEARDROP_PARAMETERS(TEARDROP_PARAMETERS *tdParams)
int m_requiredVersion
set to the KiCad format version this board requires
PAD * parsePAD(FOOTPRINT *aParent=nullptr)
std::function< bool(wxString aTitle, int aIcon, wxString aMsg, wxString aAction)> m_queryUserCallback
FOOTPRINT * parseFOOTPRINT_unchecked(wxArrayString *aInitialComments=nullptr)
void parseRenderCache(EDA_TEXT *text)
Parse the render cache for any object derived from EDA_TEXT.
TIME_PT m_lastProgressTime
for progress reporting
void parseGROUP_members(GROUP_INFO &aGroupInfo)
bool IsValidBoardHeader()
Partially parse the input and check if it matches expected header.
std::pair< wxString, wxString > parseBoardProperty()
LSET_MAP m_layerMasks
map layer names to their masks
bool parseMaybeAbsentBool(bool aDefaultValue)
Parses a boolean flag inside a list that existed before boolean normalization.
int parseBoardUnits()
Parse the current token as an ASCII numeric string with possible leading whitespace into a double pre...
PCB_DIMENSION_BASE * parseDIMENSION(BOARD_ITEM *aParent)
LSET lookUpLayerSet(const LSET_MAP &aMap)
bool m_appendToExisting
reading into an existing board; reset UUIDs
void parsePCB_TEXT_effects(PCB_TEXT *aText, PCB_TEXT *aBaseText=nullptr)
PCB_SHAPE * parsePCB_SHAPE(BOARD_ITEM *aParent)
void parseDefaults(BOARD_DESIGN_SETTINGS &aSettings)
wxString GetRequiredVersion()
Return a string representing the version of KiCad required to open this file.
The parser for PCB_PLOT_PARAMS.
Parameters and options when plotting/printing a board.
std::optional< bool > GetLegacyPlotViaOnMaskLayer() const
void Parse(PCB_PLOT_PARAMS_PARSER *aParser)
Object to handle a bitmap image that can be inserted in a PCB.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:172
void SetIsProxyItem(bool aIsProxy=true) override
Definition: pcb_shape.cpp:599
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: pcb_shape.h:90
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
void SetBorderEnabled(bool enabled)
void SetMarginTop(int aTop)
Definition: pcb_textbox.h:83
void SetMarginLeft(int aLeft)
Definition: pcb_textbox.h:82
void SetMarginBottom(int aBottom)
Definition: pcb_textbox.h:85
void SetMarginRight(int aRight)
Definition: pcb_textbox.h:84
void Move(const VECTOR2I &aMoveVector) override
Move this object.
int GetLegacyTextMargin() const
void StyleFromSettings(const BOARD_DESIGN_SETTINGS &settings) override
Definition: pcb_text.cpp:300
void Move(const VECTOR2I &aMoveVector) override
Move this object.
Definition: pcb_text.h:92
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
Definition: pcb_text.cpp:370
const PADSTACK & Padstack() const
Definition: pcb_track.h:440
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
virtual void SetCurrentProgress(double aProgress)=0
Set the progress value to aProgress (0..1).
A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is displayed in an editor as a reference fo...
bool ReadImageFile(const wxString &aFullFilename)
Read and store an image file.
double GetImageScale() const
void SetImageScale(double aScale)
Set the image "zoom" value.
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
Represent a set of closed polygons.
void RemoveAllContours()
Remove all outlines & holes (clears) the polygon set.
void BooleanAdd(const SHAPE_POLY_SET &b)
Perform boolean polyset union.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
int AddHole(const SHAPE_LINE_CHAIN &aHole, int aOutline=-1)
Adds a new hole to the given outline (default: last) and returns its index.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
int OutlineCount() const
Return the number of outlines in the set.
A name/value tuple with unique names and wxAny values.
void ParseStroke(STROKE_PARAMS &aStroke)
Simple container to manage line stroke parameters.
Definition: stroke_params.h:94
int GetWidth() const
void SetWidth(int aWidth)
TEARDROP_PARAMETARS is a helper class to handle parameters needed to build teardrops for a board thes...
double m_BestWidthRatio
The height of a teardrop as ratio between height and size of pad/via.
int m_TdMaxLen
max allowed length for teardrops in IU. <= 0 to disable
bool m_AllowUseTwoTracks
True to create teardrops using 2 track segments if the first in too small.
int m_TdMaxWidth
max allowed height for teardrops in IU. <= 0 to disable
double m_BestLengthRatio
The length of a teardrop as ratio between length and size of pad/via.
double m_WidthtoSizeFilterRatio
The ratio (H/D) between the via/pad size and the track width max value to create a teardrop 1....
bool m_TdOnPadsInZones
A filter to exclude pads inside zone fills.
bool m_Enabled
Flag to enable teardrops.
bool m_CurvedEdges
True if the teardrop should be curved.
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition: title_block.h:41
void SetRevision(const wxString &aRevision)
Definition: title_block.h:81
void SetComment(int aIdx, const wxString &aComment)
Definition: title_block.h:101
void SetTitle(const wxString &aTitle)
Definition: title_block.h:58
void SetCompany(const wxString &aCompany)
Definition: title_block.h:91
void SetDate(const wxString &aDate)
Set the date field, and defaults to the current time and date.
Definition: title_block.h:71
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition: utf8.h:72
T y
Definition: vector3.h:64
T z
Definition: vector3.h:65
T x
Definition: vector3.h:63
ZONE_SETTINGS handles zones parameters.
Definition: zone_settings.h:87
std::map< PCB_LAYER_ID, ZONE_LAYER_PROPERTIES > m_layerProperties
Handle a list of polygons defining a copper zone.
Definition: zone.h:74
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: zone.h:134
void SetLayerSetAndRemoveUnusedFills(const LSET &aLayerSet)
Set the zone to be on the aLayerSet layers and only remove the fill polygons from the unused layers,...
Definition: zone.cpp:1597
static int GetDefaultHatchPitch()
Definition: zone.cpp:1261
This file is part of the common library.
void TransformOvalToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a oblong shape to a polygon, using multiple segments.
@ DSN_RIGHT
Definition: dsnlexer.h:68
@ DSN_NUMBER
Definition: dsnlexer.h:67
@ DSN_STRING
Definition: dsnlexer.h:70
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition: eda_angle.h:401
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:403
@ DEGREES_T
Definition: eda_angle.h:31
static constexpr EDA_ANGLE ANGLE_45
Definition: eda_angle.h:402
EDA_UNITS
Definition: eda_units.h:46
@ FP_SMD
Definition: footprint.h:81
@ FP_DNP
Definition: footprint.h:88
@ FP_ALLOW_MISSING_COURTYARD
Definition: footprint.h:87
@ FP_EXCLUDE_FROM_POS_FILES
Definition: footprint.h:82
@ FP_BOARD_ONLY
Definition: footprint.h:84
@ FP_EXCLUDE_FROM_BOM
Definition: footprint.h:83
@ FP_THROUGH_HOLE
Definition: footprint.h:80
@ FP_ALLOW_SOLDERMASK_BRIDGES
Definition: footprint.h:86
void ignore_unused(const T &)
Definition: ignore.h:24
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:39
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
Definition: ki_exception.h:165
#define MIN_VISIBILITY_MASK
Definition: layer_ids.h:627
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition: layer_ids.h:663
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ F_CrtYd
Definition: layer_ids.h:116
@ B_Adhes
Definition: layer_ids.h:103
@ Edge_Cuts
Definition: layer_ids.h:112
@ F_Paste
Definition: layer_ids.h:104
@ Cmts_User
Definition: layer_ids.h:108
@ F_Adhes
Definition: layer_ids.h:102
@ B_Mask
Definition: layer_ids.h:98
@ B_Cu
Definition: layer_ids.h:65
@ F_Mask
Definition: layer_ids.h:97
@ B_Paste
Definition: layer_ids.h:105
@ In15_Cu
Definition: layer_ids.h:80
@ F_Fab
Definition: layer_ids.h:119
@ Margin
Definition: layer_ids.h:113
@ F_SilkS
Definition: layer_ids.h:100
@ B_CrtYd
Definition: layer_ids.h:115
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
@ Rescue
Definition: layer_ids.h:121
@ B_SilkS
Definition: layer_ids.h:101
@ PCB_LAYER_ID_COUNT
Definition: layer_ids.h:171
@ F_Cu
Definition: layer_ids.h:64
@ B_Fab
Definition: layer_ids.h:118
This file contains miscellaneous commonly used macros and functions.
KICOMMON_API bool FetchUnitsFromString(const wxString &aTextValue, EDA_UNITS &aUnits)
Write any unit info found in the string to aUnits.
Definition: eda_units.cpp:88
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition: kicad_algo.h:100
#define MAX_PAGE_SIZE_PCBNEW_MM
Definition: page_info.h:44
#define MIN_PAGE_SIZE_MM
Min and max page sizes for clamping, in mm.
Definition: page_info.h:43
DIM_TEXT_POSITION
Where to place the text on a dimension.
Definition: pcb_dimension.h:62
DIM_UNITS_FORMAT
How to display the units in a dimension's text.
Definition: pcb_dimension.h:40
DIM_UNITS_MODE
Used for storing the units selection in the file because EDA_UNITS alone doesn't cut it.
Definition: pcb_dimension.h:72
DIM_TEXT_BORDER
Frame to show around dimension text.
Definition: pcb_dimension.h:92
DIM_PRECISION
Definition: pcb_dimension.h:47
Class to handle a set of BOARD_ITEMs.
#define SEXPR_BOARD_FILE_VERSION
Current s-expression file format version. 2 was the last legacy format version.
#define LEGACY_ARC_FORMATTING
These were the last to use old arc formatting.
#define LEGACY_NET_TIES
These were the last to use the keywords field to indicate a net-tie.
#define BOARD_FILE_HOST_VERSION
Earlier files than this include the host tag.
constexpr double INT_LIMIT
Pcbnew s-expression file format parser definition.
PGM_BASE & Pgm()
The global program "get" accessor.
Definition: pgm_base.cpp:1071
see class PGM_BASE
int StrPrintf(std::string *result, const char *format,...)
This is like sprintf() but the output is appended to a std::string instead of to a character array.
Definition: richio.cpp:70
double parseDouble(LINE_READER &aReader, const char *aLine, const char **aOutput)
Parses an ASCII point string with possible leading whitespace into a double precision floating point ...
#define DEFAULT_SOLDERMASK_OPACITY
wxString ConvertToNewOverbarNotation(const wxString &aOldStr)
Convert the old ~...~ overbar notation to the new ~{...} one.
wxString From_UTF8(const char *cstring)
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: string_utils.h:403
Container to handle a stock of specific differential pairs each with unique track width,...
const double IU_PER_MM
Definition: base_units.h:76
constexpr int mmToIU(double mm) const
Definition: base_units.h:88
Variant of PARSE_ERROR indicating that a syntax or related error was likely caused by a file generate...
Definition: ki_exception.h:176
Container to hold information pertinent to a layer of a BOARD.
Definition: board.h:175
static LAYER_T ParseType(const char *aType)
Convert a string to a LAYER_T.
Definition: board.cpp:701
void clear()
Definition: board.h:181
LAYER_T m_type
The type of the layer.
Definition: board.h:204
wxString m_name
The canonical name of the layer.
Definition: board.h:202
wxString m_userName
The user defined name of the layer.
Definition: board.h:203
bool m_visible
Definition: board.h:205
int m_number
The layer ID.
Definition: board.h:206
std::optional< bool > has_solder_mask
True if this outer layer has mask (is not tented)
Definition: padstack.h:232
A filename or source description, a problem input line, a line number, a byte offset,...
Definition: ki_exception.h:120
Convert net code using the mapping table if available, otherwise returns unchanged net code if < 0 or...
Container to handle a stock of specific vias each with unique diameter and drill sizes in the BOARD c...
std::optional< VECTOR2I > hatching_offset
Definition: zone_settings.h:51
const SHAPE_LINE_CHAIN chain
constexpr int delta
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:105
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:102
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition: typeinfo.h:92
@ NOT_USED
the 3d code uses this value
Definition: typeinfo.h:79
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition: typeinfo.h:86
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:101
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition: typeinfo.h:104
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695
ISLAND_REMOVAL_MODE
Whether or not to remove isolated islands from a zone.
Definition: zone_settings.h:68
ZONE_BORDER_DISPLAY_STYLE
Zone border styles.
Definition: zone_settings.h:59
ZONE_CONNECTION
How pads are covered by copper in zone.
Definition: zones.h:47