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