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