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