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