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