KiCad PCB EDA Suite
import_fabmaster.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) 2020 BeagleBoard Foundation
5  * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
6  * Author: Seth Hillbrand <hillbrand@kipro-pcb.com>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 3
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
26 #include "import_fabmaster.h"
27 
28 #include <algorithm>
29 #include <array>
30 #include <iostream>
31 #include <fstream>
32 #include <map>
33 #include <memory>
34 #include <string>
35 #include <sstream>
36 #include <vector>
37 #include <utility>
38 
39 #include <wx/log.h>
40 
41 #include <board.h>
42 #include <board_design_settings.h>
43 #include <board_item.h>
44 #include <convert_to_biu.h>
45 #include <footprint.h>
46 #include <fp_shape.h>
47 #include <pad.h>
48 #include <pad_shapes.h>
49 #include <pcb_shape.h>
50 #include <pcb_text.h>
51 #include <pcb_track.h>
52 #include <zone.h>
53 #include <common.h>
54 #include <geometry/shape_arc.h>
55 #include <kicad_string.h>
56 #include <convert_to_biu.h>
57 #include <math/util.h>
58 #include <wx/filename.h>
59 
60 
61 double FABMASTER::readDouble( const std::string aStr ) const
62 {
63  std::istringstream istr( aStr );
64  istr.imbue( std::locale::classic() );
65 
66  double doubleValue;
67  istr >> doubleValue;
68  return doubleValue;
69 }
70 
71 
72 int FABMASTER::readInt( const std::string aStr ) const
73 {
74  std::istringstream istr( aStr );
75  istr.imbue( std::locale::classic() );
76 
77  int intValue;
78  istr >> intValue;
79  return intValue;
80 }
81 
82 
83 bool FABMASTER::Read( const std::string& aFile )
84 {
85 
86  std::ifstream ifs( aFile, std::ios::in | std::ios::binary );
87 
88  if( !ifs.is_open() )
89  return false;
90 
91  m_filename = aFile;
92 
93  // Read/ignore all bytes in the file to find the size and then go back to the beginning
94  ifs.ignore( std::numeric_limits<std::streamsize>::max() );
95  std::streamsize length = ifs.gcount();
96  ifs.clear();
97  ifs.seekg( 0, std::ios_base::beg );
98 
99  std::string buffer( std::istreambuf_iterator<char>{ ifs }, {} );
100 
101  std::vector < std::string > row;
102 
103  // Reserve an estimate of the number of rows to prevent continual re-allocation
104  // crashing (Looking at you MSVC)
105  row.reserve( length / 100 );
106  std::string cell;
107  cell.reserve( 100 );
108 
109  bool quoted = false;
110 
111  for( auto ch : buffer )
112  {
113  switch( ch )
114  {
115  case '"':
116 
117  if( cell.empty() || cell[0] == '"' )
118  quoted = !quoted;
119 
120  cell += ch;
121  break;
122 
123  case '!':
124  if( !quoted )
125  {
126  row.push_back( cell );
127  cell.clear();
128  }
129  else
130  cell += ch;
131 
132  break;
133 
134  case '\n':
135 
137  if( !cell.empty() )
138  row.push_back( cell );
139 
140  cell.clear();
141  rows.push_back( row );
142  row.clear();
143  quoted = false;
144  break;
145 
146  case '\r':
147  break;
148 
149  default:
150  cell += std::toupper( ch );
151  }
152  }
153 
154  // Handle last line without linebreak
155  if( !cell.empty() || !row.empty() )
156  {
157  row.push_back( cell );
158  cell.clear();
159  rows.push_back( row );
160  row.clear();
161  }
162 
163  return true;
164 }
165 
167 {
168  single_row row;
169  try
170  {
171  row = rows.at( aOffset );
172  }
173  catch( std::out_of_range& )
174  {
175  return UNKNOWN_EXTRACT;
176  }
177 
178  if( row.size() < 3 )
179  return UNKNOWN_EXTRACT;
180 
181  if( row[0].back() != 'A' )
182  return UNKNOWN_EXTRACT;
183 
184  std::string row1 = row[1];
185  std::string row2 = row[2];
186  std::string row3{};
187 
189  row1.erase( std::remove_if( row1.begin(), row1.end(), []( char c ){ return c == '_'; } ),
190  row1.end() );
191  row2.erase( std::remove_if( row2.begin(), row2.end(), []( char c ){ return c == '_'; } ),
192  row2.end() );
193 
194  if( row.size() > 3 )
195  {
196  row3 = row[3];
197  row3.erase( std::remove_if( row3.begin(), row3.end(), []( char c ){ return c == '_'; } ),
198  row3.end() );
199  }
200 
201  if( row1 == "REFDES" && row2 == "COMPCLASS" )
202  return EXTRACT_REFDES;
203 
204  if( row1 == "NETNAME" && row2 == "REFDES" )
205  return EXTRACT_NETS;
206 
207  if( row1 == "CLASS" && row2 == "SUBCLASS" && row3.empty() )
208  return EXTRACT_BASIC_LAYERS;
209 
210  if( row1 == "GRAPHICDATANAME" && row2 == "GRAPHICDATANUMBER" )
211  return EXTRACT_GRAPHICS;
212 
213  if( row1 == "CLASS" && row2 == "SUBCLASS" && row3 == "GRAPHICDATANAME" )
214  return EXTRACT_TRACES;
215 
216  if( row1 == "SYMNAME" && row2 == "PINNAME" )
217  return FABMASTER_EXTRACT_PINS;
218 
219  if( row1 == "SYMNAME" && row2 == "SYMMIRROR" && row3 == "PINNAME" )
220  return EXTRACT_PINS;
221 
222  if( row1 == "VIAX" && row2 == "VIAY" )
223  return EXTRACT_VIAS;
224 
225  if( row1 == "SUBCLASS" && row2 == "PADSHAPENAME" )
226  return EXTRACT_PAD_SHAPES;
227 
228  if( row1 == "PADNAME" )
229  return EXTRACT_PADSTACKS;
230 
231  if( row1 == "LAYERSORT" )
232  return EXTRACT_FULL_LAYERS;
233 
234  wxLogError
235  (
236  wxString::Format( _( "Unknown FABMASTER section %s:%s at row %zu." ), row1.c_str(),
237  row2.c_str(), aOffset ) );
238  return UNKNOWN_EXTRACT;
239 
240 }
241 
242 double FABMASTER::processScaleFactor( size_t aRow )
243 {
244  double retval = 0.0;
245 
246  if( aRow >= rows.size() )
247  return -1.0;
248 
249  if( rows[aRow].size() < 11 )
250  {
251  wxLogError( wxString::Format( _( "Invalid row size in J row %zu. "
252  "Expecting 11 elements but found %zu" ), aRow, rows[aRow].size() ) );
253  return -1.0;
254  }
255 
256  for( int i = 7; i < 10 && retval < 1.0; ++i )
257  {
258  auto units = rows[aRow][i];
259  std::transform(units.begin(), units.end(),units.begin(), ::toupper);
260 
261  if( units == "MILS" )
262  retval = IU_PER_MILS;
263  else if( units == "MILLIMETERS" )
264  retval = IU_PER_MM;
265  else if( units == "MICRONS" )
266  retval = IU_PER_MM * 10.0;
267  else if( units == "INCHES" )
268  retval = IU_PER_MILS * 1000.0;
269  }
270 
271  if( retval < 1.0 )
272  {
273  wxLogError( _( "Could not find units value, defaulting to Mils" ) );
274  retval = IU_PER_MILS;
275  }
276 
277  return retval;
278 }
279 
280 int FABMASTER::getColFromName( size_t aRow, const std::string& aStr )
281 {
282  if( aRow >= rows.size() )
283  return -1;
284 
285  auto header = rows[aRow];
286 
287  for( size_t i = 0; i < header.size(); i++ )
288  {
291  header[i].erase( std::remove_if( header[i].begin(), header[i].end(),
292  []( const char c ){ return c == '_'; } ), header[i].end() );
293 
294  if( header[i] == aStr )
295  return i;
296  }
297 
298  THROW_IO_ERROR( wxString::Format( _( "Could not find column label %s" ), aStr.c_str() ) );
299  return -1;
300 }
301 
302 
303 PCB_LAYER_ID FABMASTER::getLayer( const std::string& aLayerName )
304 {
305  const auto& kicad_layer = layers.find( aLayerName);
306 
307  if( kicad_layer == layers.end() )
308  return UNDEFINED_LAYER;
309  else
310  return static_cast<PCB_LAYER_ID>( kicad_layer->second.layerid );
311 }
312 
313 
314 size_t FABMASTER::processPadStackLayers( size_t aRow )
315 {
316  size_t rownum = aRow + 2;
317 
318  if( rownum >= rows.size() )
319  return -1;
320 
321  const single_row& header = rows[aRow];
322 
323  int pad_name_col = getColFromName( aRow, "PADNAME" );
324  int pad_num_col = getColFromName( aRow, "RECNUMBER" );
325  int pad_lay_col = getColFromName( aRow, "LAYER" );
326  int pad_fix_col = getColFromName( aRow, "FIXFLAG" );
327  int pad_via_col = getColFromName( aRow, "VIAFLAG" );
328  int pad_shape_col = getColFromName( aRow, "PADSHAPE1" );
329  int pad_width_col = getColFromName( aRow, "PADWIDTH" );
330  int pad_height_col = getColFromName( aRow, "PADHGHT" );
331  int pad_xoff_col = getColFromName( aRow, "PADXOFF" );
332  int pad_yoff_col = getColFromName( aRow, "PADYOFF" );
333  int pad_flash_col = getColFromName( aRow, "PADFLASH" );
334  int pad_shape_name_col = getColFromName( aRow, "PADSHAPENAME" );
335 
336  for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
337  {
338  const single_row& row = rows[rownum];
339 
340  if( row.size() != header.size() )
341  {
342  wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
343  "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
344  continue;
345  }
346 
347  auto pad_name = row[pad_name_col];
348  auto pad_num = row[pad_num_col];
349  auto pad_layer = row[pad_lay_col];
350  auto pad_is_fixed = row[pad_fix_col];
351  auto pad_is_via = row[pad_via_col];
352  auto pad_shape = row[pad_shape_col];
353  auto pad_width = row[pad_width_col];
354  auto pad_height = row[pad_height_col];
355  auto pad_xoff = row[pad_xoff_col];
356  auto pad_yoff = row[pad_yoff_col];
357  auto pad_flash = row[pad_flash_col];
358  auto pad_shapename = row[pad_shape_name_col];
359 
360  // This layer setting seems to be unused
361  if( pad_layer == "INTERNAL_PAD_DEF" || pad_layer == "internal_pad_def" )
362  continue;
363 
364  // Skip the technical layers
365  if( pad_layer[0] == '~' )
366  break;
367 
368  auto result = layers.emplace( pad_layer, FABMASTER_LAYER{} );
369  FABMASTER_LAYER& layer = result.first->second;
370 
372  if( layer.id == 0 )
373  {
374  layer.name = pad_layer;
375  layer.id = readInt( pad_num );
376  layer.conductive = true;
377  }
378  }
379 
380  return 0;
381 }
382 
383 
390 size_t FABMASTER::processPadStacks( size_t aRow )
391 {
392  size_t rownum = aRow + 2;
393 
394  if( rownum >= rows.size() )
395  return -1;
396 
397  const single_row& header = rows[aRow];
398  double scale_factor = processScaleFactor( aRow + 1 );
399 
400  if( scale_factor <= 0.0 )
401  return -1;
402 
403  int pad_name_col = getColFromName( aRow, "PADNAME" );
404  int pad_num_col = getColFromName( aRow, "RECNUMBER" );
405  int pad_lay_col = getColFromName( aRow, "LAYER" );
406  int pad_fix_col = getColFromName( aRow, "FIXFLAG" );
407  int pad_via_col = getColFromName( aRow, "VIAFLAG" );
408  int pad_shape_col = getColFromName( aRow, "PADSHAPE1" );
409  int pad_width_col = getColFromName( aRow, "PADWIDTH" );
410  int pad_height_col = getColFromName( aRow, "PADHGHT" );
411  int pad_xoff_col = getColFromName( aRow, "PADXOFF" );
412  int pad_yoff_col = getColFromName( aRow, "PADYOFF" );
413  int pad_flash_col = getColFromName( aRow, "PADFLASH" );
414  int pad_shape_name_col = getColFromName( aRow, "PADSHAPENAME" );
415 
416  for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
417  {
418  const single_row& row = rows[rownum];
419  FM_PAD* pad;
420 
421  if( row.size() != header.size() )
422  {
423  wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
424  "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
425  continue;
426  }
427 
428  auto pad_name = row[pad_name_col];
429  auto pad_num = row[pad_num_col];
430  auto pad_layer = row[pad_lay_col];
431  auto pad_is_fixed = row[pad_fix_col];
432  auto pad_is_via = row[pad_via_col];
433  auto pad_shape = row[pad_shape_col];
434  auto pad_width = row[pad_width_col];
435  auto pad_height = row[pad_height_col];
436  auto pad_xoff = row[pad_xoff_col];
437  auto pad_yoff = row[pad_yoff_col];
438  auto pad_flash = row[pad_flash_col];
439  auto pad_shapename = row[pad_shape_name_col];
440 
441  // This layer setting seems to be unused
442  if( pad_layer == "INTERNAL_PAD_DEF" || pad_layer == "internal_pad_def" )
443  continue;
444 
445  int recnum = KiROUND( readDouble( pad_num ) );
446 
447  auto new_pad = pads.find( pad_name );
448 
449  if( new_pad != pads.end() )
450  pad = &new_pad->second;
451  else
452  {
453  pads[pad_name] = FM_PAD();
454  pad = &pads[pad_name];
455  pad->name = pad_name;
456  }
457 
459  if( pad_layer == "~DRILL" )
460  {
461  int drill_hit;
462  int drill_x;
463  int drill_y;
464 
465  try
466  {
467  drill_hit = KiROUND( std::fabs( readDouble( pad_shape ) * scale_factor ) );
468  drill_x = KiROUND( std::fabs( readDouble( pad_width ) * scale_factor ) );
469  drill_y = KiROUND( std::fabs( readDouble( pad_height ) * scale_factor ) );
470  }
471  catch( ... )
472  {
473  wxLogError( wxString::Format( _( "Expecting drill size value "
474  "but found %s!%s!%s at line %zu" ),
475  pad_shape.c_str(), pad_width.c_str(), pad_height.c_str(), rownum ) );
476  continue;
477  }
478 
479  if( drill_hit == 0 )
480  {
481  pad->drill = false;
482  continue;
483  }
484 
485  pad->drill = true;
486 
489  if( drill_x == drill_y )
490  {
491  pad->drill_size_x = drill_hit;
492  pad->drill_size_y = drill_hit;
493  }
494  else
495  {
496  pad->drill_size_x = drill_x;
497  pad->drill_size_y = drill_y;
498  }
499 
500  if( !pad_shapename.empty() && pad_shapename[0] == 'P' )
501  pad->plated = true;
502 
503  continue;
504  }
505 
506  if( pad_shape.empty() )
507  continue;
508 
509  double w;
510  double h;
511 
512  try
513  {
514  w = readDouble( pad_width ) * scale_factor;
515  h = readDouble( pad_height ) * scale_factor;
516  }
517  catch( ... )
518  {
519  wxLogError( wxString::Format( _( "Expecting pad size values "
520  "but found %s : %s at line %zu" ), pad_width.c_str(), pad_height.c_str(), rownum ) );
521  continue;
522  }
523 
524  if( w <= 0.0 )
525  continue;
526 
527  auto layer = layers.find( pad_layer );
528 
529  if( layer != layers.end() )
530  {
531  if( layer->second.layerid == F_Cu )
532  pad->top = true;
533  else if( layer->second.layerid == B_Cu )
534  pad->bottom = true;
535  }
536 
537  if( w > std::numeric_limits<int>::max() || h > std::numeric_limits<int>::max() )
538  {
539  wxLogError( wxString::Format( _( "Invalid pad size on line %zu" ), rownum ) );
540  continue;
541  }
542 
543  if( pad_layer == "~TSM" || pad_layer == "~BSM" )
544  {
545  if( w > 0.0 && h > 0.0 )
546  {
547  pad->mask_width = KiROUND( w );
548  pad->mask_height = KiROUND( h );
549  }
550  continue;
551  }
552 
553  if( pad_layer == "~TSP" || pad_layer == "~BSP" )
554  {
555  if( w > 0.0 && h > 0.0 )
556  {
557  pad->paste_width = KiROUND( w );
558  pad->paste_height = KiROUND( h );
559  }
560  continue;
561  }
562 
564  if( pad_layer[0] == '~' )
565  continue;
566 
567  try
568  {
569  pad->x_offset = KiROUND( readDouble( pad_xoff ) * scale_factor );
570  pad->y_offset = -KiROUND( readDouble( pad_yoff ) * scale_factor );
571  }
572  catch( ... )
573  {
574  wxLogError( wxString::Format( _( "Expecting pad offset values "
575  "but found %s : %s at line %zu" ), pad_xoff.c_str(), pad_yoff.c_str(), rownum ) );
576  continue;
577  }
578 
579  if( w > 0.0 && h > 0.0 && recnum == 1 )
580  {
581  pad->width = KiROUND( w );
582  pad->height = KiROUND( h );
583  pad->via = ( std::toupper( pad_is_via[0] ) != 'V' );
584 
585  if( pad_shape == "CIRCLE" )
586  {
587  pad->height = pad->width;
588  pad->shape = PAD_SHAPE::CIRCLE;
589  }
590  else if( pad_shape == "RECTANGLE" )
591  {
592  pad->shape = PAD_SHAPE::RECT;
593  }
594  else if( pad_shape == "ROUNDED_RECT" )
595  {
596  pad->shape = PAD_SHAPE::ROUNDRECT;
597  }
598  else if( pad_shape == "SQUARE" )
599  {
600  pad->shape = PAD_SHAPE::RECT;
601  pad->height = pad->width;
602  }
603  else if( pad_shape == "OBLONG" || pad_shape == "OBLONG_X" || pad_shape == "OBLONG_Y" )
604  pad->shape = PAD_SHAPE::OVAL;
605  else if( pad_shape == "OCTAGON" )
606  {
607  pad->shape = PAD_SHAPE::RECT;
608  pad->is_octogon = true;
609  }
610  else if( pad_shape == "SHAPE" )
611  {
612  pad->shape = PAD_SHAPE::CUSTOM;
613  pad->custom_name = pad_shapename;
614  }
615  else
616  {
617  wxLogError( wxString::Format( _( "Unknown pad shape name '%s' on layer '%s' at line %zu" ),
618  pad_shape.c_str(), pad_layer.c_str(), rownum ) );
619  continue;
620  }
621  }
622  }
623 
624  return rownum - aRow;
625 }
626 
627 
628 size_t FABMASTER::processSimpleLayers( size_t aRow )
629 {
630  size_t rownum = aRow + 2;
631 
632  if( rownum >= rows.size() )
633  return -1;
634 
635  auto header = rows[aRow];
636  double scale_factor = processScaleFactor( aRow + 1 );
637 
638  if( scale_factor <= 0.0 )
639  return -1;
640 
641  int layer_class_col = getColFromName( aRow, "CLASS" );
642  int layer_subclass_col = getColFromName( aRow, "SUBCLASS" );
643 
644  if( layer_class_col < 0 || layer_subclass_col < 0 )
645  return -1;
646 
647  for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
648  {
649  const single_row& row = rows[rownum];
650 
651  if( row.size() != header.size() )
652  {
653  wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
654  "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
655  continue;
656  }
657 
658  auto result = layers.emplace( row[layer_subclass_col], FABMASTER_LAYER{} );
659  FABMASTER_LAYER& layer = result.first->second;
660 
661  layer.name = row[layer_subclass_col];
662  layer.positive = true;
663  layer.conductive = false;
664 
665  if( row[layer_class_col] == "ANTI ETCH" )
666  {
667  layer.positive = false;
668  layer.conductive = true;
669  }
670  else if( row[layer_class_col] == "ETCH" )
671  {
672  layer.conductive = true;
673  }
674  }
675 
676  return rownum - aRow;
677 }
678 
679 
681 {
682  bool has_l1 = false;
683  int max_layer = 0;
684  std::string max_layer_name;
685 
686  std::vector<std::pair<std::string, int>> extra_layers
687  {
688  { "ASSEMBLY_TOP", F_Fab },
689  { "ASSEMBLY_BOTTOM", B_Fab },
690  { "PLACE_BOUND_TOP", F_CrtYd },
691  { "PLACE_BOUND_BOTTOM", B_CrtYd },
692  };
693 
694  std::vector<FABMASTER_LAYER*> layer_order;
695 
696  for( auto& el : layers )
697  {
698  FABMASTER_LAYER& layer = el.second;
699  layer.layerid = UNSELECTED_LAYER;
700 
701  if( layer.conductive )
702  {
703  layer_order.push_back( &layer );
704  }
705  else if( layer.name.find( "SILK" ) != std::string::npos &&
706  layer.name.find( "AUTOSILK" ) == std::string::npos ) // Skip the autosilk layer
707  {
708  if( layer.name.find( "B" ) != std::string::npos )
709  layer.layerid = B_SilkS;
710  else
711  layer.layerid = F_SilkS;
712  }
713  else if( layer.name.find( "MASK" ) != std::string::npos ||
714  layer.name.find( "MSK" ) != std::string::npos )
715  {
716  if( layer.name.find( "B" ) != std::string::npos )
717  layer.layerid = B_Mask;
718  else
719  layer.layerid = F_Mask;
720  }
721  else if( layer.name.find( "PAST" ) != std::string::npos )
722  {
723  if( layer.name.find( "B" ) != std::string::npos )
724  layer.layerid = B_Paste;
725  else
726  layer.layerid = F_Paste;
727  }
728  else if( layer.name.find( "NCLEGEND" ) != std::string::npos )
729  layer.layerid = Dwgs_User;
730  else
731  layer.disable = true;
732  }
733 
734  std::sort( layer_order.begin(), layer_order.end(), FABMASTER_LAYER::BY_ID() );
735  int layernum = 0;
736 
737  for( auto layer : layer_order )
738  layer->layerid = layernum++;
739 
742  layer_order.back()->layerid = B_Cu;
743 
744  for( auto& new_pair : extra_layers )
745  {
746  FABMASTER_LAYER new_layer;
747 
748  new_layer.name = new_pair.first;
749  new_layer.layerid = new_pair.second;
750  new_layer.conductive = false;
751 
752  auto result = layers.emplace( new_pair.first, new_layer );
753 
754  if( !result.second )
755  {
756  result.first->second.layerid = new_pair.second;
757  result.first->second.disable = false;
758  }
759  }
760 
761  return true;
762 }
763 
764 
770 size_t FABMASTER::processLayers( size_t aRow )
771 {
772  size_t rownum = aRow + 2;
773 
774  if( rownum >= rows.size() )
775  return -1;
776 
777  auto header = rows[aRow];
778  double scale_factor = processScaleFactor( aRow + 1 );
779 
780  if( scale_factor <= 0.0 )
781  return -1;
782 
783  int layer_sort_col = getColFromName( aRow, "LAYERSORT" );
784  int layer_subclass_col = getColFromName( aRow, "LAYERSUBCLASS" );
785  int layer_art_col = getColFromName( aRow, "LAYERARTWORK" );
786  int layer_use_col = getColFromName( aRow, "LAYERUSE" );
787  int layer_cond_col = getColFromName( aRow, "LAYERCONDUCTOR" );
788  int layer_er_col = getColFromName( aRow, "LAYERDIELECTRICCONSTANT" );
789  int layer_rho_col = getColFromName( aRow, "LAYERELECTRICALCONDUCTIVITY" );
790  int layer_mat_col = getColFromName( aRow, "LAYERMATERIAL" );
791 
792  if( layer_sort_col < 0 || layer_subclass_col < 0 || layer_art_col < 0 || layer_use_col < 0
793  || layer_cond_col < 0 || layer_er_col < 0 || layer_rho_col < 0 || layer_mat_col < 0 )
794  return -1;
795 
796  for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
797  {
798  const single_row& row = rows[rownum];
799 
800  if( row.size() != header.size() )
801  {
802  wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
803  "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
804  continue;
805  }
806 
807  auto layer_sort = row[layer_sort_col];
808  auto layer_subclass = row[layer_subclass_col];
809  auto layer_art = row[layer_art_col];
810  auto layer_use = row[layer_use_col];
811  auto layer_cond = row[layer_cond_col];
812  auto layer_er = row[layer_er_col];
813  auto layer_rho = row[layer_rho_col];
814  auto layer_mat = row[layer_mat_col];
815 
816  if( layer_mat == "AIR" )
817  continue;
818 
819  FABMASTER_LAYER layer;
820 
821  if( layer_subclass.empty() )
822  {
823  if( layer_cond != "NO" )
824  layer.name = "In.Cu" + layer_sort;
825  else
826  layer.name = "Dielectric" + layer_sort;
827  }
828 
829  layer.positive = ( layer_art != "NEGATIVE" );
830 
831  layers.emplace( layer.name, layer );
832  }
833 
834  return rownum - aRow;
835 }
836 
837 
843 size_t FABMASTER::processCustomPads( size_t aRow )
844 {
845  size_t rownum = aRow + 2;
846 
847  if( rownum >= rows.size() )
848  return -1;
849 
850  auto header = rows[aRow];
851  double scale_factor = processScaleFactor( aRow + 1 );
852 
853  if( scale_factor <= 0.0 )
854  return -1;
855 
856  int pad_subclass_col = getColFromName( aRow, "SUBCLASS" );
857  int pad_shape_name_col = getColFromName( aRow, "PADSHAPENAME" );
858  int pad_grdata_name_col = getColFromName( aRow, "GRAPHICDATANAME" );
859  int pad_grdata_num_col = getColFromName( aRow, "GRAPHICDATANUMBER" );
860  int pad_record_tag_col = getColFromName( aRow, "RECORDTAG" );
861  int pad_grdata1_col = getColFromName( aRow, "GRAPHICDATA1" );
862  int pad_grdata2_col = getColFromName( aRow, "GRAPHICDATA2" );
863  int pad_grdata3_col = getColFromName( aRow, "GRAPHICDATA3" );
864  int pad_grdata4_col = getColFromName( aRow, "GRAPHICDATA4" );
865  int pad_grdata5_col = getColFromName( aRow, "GRAPHICDATA5" );
866  int pad_grdata6_col = getColFromName( aRow, "GRAPHICDATA6" );
867  int pad_grdata7_col = getColFromName( aRow, "GRAPHICDATA7" );
868  int pad_grdata8_col = getColFromName( aRow, "GRAPHICDATA8" );
869  int pad_grdata9_col = getColFromName( aRow, "GRAPHICDATA9" );
870  int pad_stack_name_col = getColFromName( aRow, "PADSTACKNAME" );
871  int pad_refdes_col = getColFromName( aRow, "REFDES" );
872  int pad_pin_num_col = getColFromName( aRow, "PINNUMBER" );
873 
874  if( pad_subclass_col < 0 || pad_shape_name_col < 0 || pad_grdata1_col < 0 || pad_grdata2_col < 0
875  || pad_grdata3_col < 0 || pad_grdata4_col < 0 || pad_grdata5_col < 0
876  || pad_grdata6_col < 0 || pad_grdata7_col < 0 || pad_grdata8_col < 0
877  || pad_grdata9_col < 0 || pad_stack_name_col < 0 || pad_refdes_col < 0
878  || pad_pin_num_col < 0 )
879  return -1;
880 
881  for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
882  {
883  const single_row& row = rows[rownum];
884 
885  if( row.size() != header.size() )
886  {
887  wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
888  "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
889 
890  continue;
891  }
892 
893  auto pad_layer = row[pad_subclass_col];
894  auto pad_shape_name = row[pad_shape_name_col];
895  auto pad_record_tag = row[pad_record_tag_col];
896 
897  GRAPHIC_DATA gr_data;
898  gr_data.graphic_dataname = row[pad_grdata_name_col];
899  gr_data.graphic_datanum = row[pad_grdata_num_col];
900  gr_data.graphic_data1 = row[pad_grdata1_col];
901  gr_data.graphic_data2 = row[pad_grdata2_col];
902  gr_data.graphic_data3 = row[pad_grdata3_col];
903  gr_data.graphic_data4 = row[pad_grdata4_col];
904  gr_data.graphic_data5 = row[pad_grdata5_col];
905  gr_data.graphic_data6 = row[pad_grdata6_col];
906  gr_data.graphic_data7 = row[pad_grdata7_col];
907  gr_data.graphic_data8 = row[pad_grdata8_col];
908  gr_data.graphic_data9 = row[pad_grdata9_col];
909 
910  auto pad_stack_name = row[pad_stack_name_col];
911  auto pad_refdes = row[pad_refdes_col];
912  auto pad_pin_num = row[pad_pin_num_col];
913 
914  // N.B. We get the FIGSHAPE records as "FIG_SHAPE name". We only want "name"
915  // and we don't process other pad shape records
916  std::string prefix( "FIG_SHAPE " );
917 
918  if( pad_shape_name.length() <= prefix.length()
919  || !std::equal( prefix.begin(), prefix.end(), pad_shape_name.begin() ) )
920  {
921  continue;
922  }
923 
924  // Custom pads are a series of records with the same record ID but incrementing
925  // Sequence numbers.
926  int id = -1;
927  int seq = -1;
928 
929  if( std::sscanf( pad_record_tag.c_str(), "%d %d", &id, &seq ) != 2 )
930  {
931  wxLogError( wxString::Format( _( "Invalid format for id string \"%s\" "
932  "in custom pad row %zu" ),
933  pad_record_tag.c_str(), rownum ) );
934  continue;
935  }
936 
937  auto name = pad_shape_name.substr( prefix.length() );
938  name += "_" + pad_refdes + "_" + pad_pin_num;
939  auto ret = pad_shapes.emplace( name, FABMASTER_PAD_SHAPE{} );
940 
941  auto& custom_pad = ret.first->second;
942 
943  // If we were able to insert the pad name, then we need to initialize the
944  // record
945  if( ret.second )
946  {
947  custom_pad.name = name;
948  custom_pad.padstack = pad_stack_name;
949  custom_pad.pinnum = pad_pin_num;
950  custom_pad.refdes = pad_refdes;
951  }
952 
953  // At this point we extract the individual graphical elements for processing the complex pad. The
954  // coordinates are in board origin format, so we'll need to fix the offset later when we assign them
955  // to the modules.
956 
957  auto gr_item = std::unique_ptr<GRAPHIC_ITEM>( processGraphic( gr_data, scale_factor ) );
958 
959  if( gr_item )
960  {
961  gr_item->layer = pad_layer;
962  gr_item->refdes = pad_refdes;
963  gr_item->seq = seq;
964  gr_item->subseq = 0;
965 
967  auto pad_it = custom_pad.elements.emplace( id, graphic_element{} );
968  auto retval = pad_it.first->second.insert( std::move(gr_item ) );
969 
970  if( !retval.second )
971  {
972  wxLogError( wxString::Format( _( "Could not insert graphical item %d into padstack \"%s\"" ),
973  seq, pad_stack_name.c_str() ) );
974  }
975  }
976  else
977  {
978  wxLogError( wxString::Format( _( "Unrecognized pad shape primitive \"%s\" in line %zu." ),
979  gr_data.graphic_dataname, rownum ) );
980  }
981  }
982 
983  return rownum - aRow;
984 }
985 
986 
988 {
989  GRAPHIC_LINE* new_line = new GRAPHIC_LINE ;
990 
991  new_line->shape = GR_SHAPE_LINE;
992  new_line->start_x = KiROUND( readDouble( aData.graphic_data1 ) * aScale );
993  new_line->start_y = -KiROUND( readDouble( aData.graphic_data2 ) * aScale );
994  new_line->end_x = KiROUND( readDouble( aData.graphic_data3 ) * aScale );
995  new_line->end_y = -KiROUND( readDouble( aData.graphic_data4 ) * aScale );
996  new_line->width = KiROUND( readDouble( aData.graphic_data5 ) * aScale );
997 
998  return new_line;
999 }
1000 
1002 {
1003  GRAPHIC_ARC* new_arc = new GRAPHIC_ARC ;
1004 
1005  new_arc->shape = GR_SHAPE_ARC;
1006  new_arc->start_x = KiROUND( readDouble( aData.graphic_data1 ) * aScale );
1007  new_arc->start_y = -KiROUND( readDouble( aData.graphic_data2 ) * aScale );
1008  new_arc->end_x = KiROUND( readDouble( aData.graphic_data3 ) * aScale );
1009  new_arc->end_y = -KiROUND( readDouble( aData.graphic_data4 ) * aScale );
1010  new_arc->center_x = KiROUND( readDouble( aData.graphic_data5 ) * aScale );
1011  new_arc->center_y = -KiROUND( readDouble( aData.graphic_data6 ) * aScale );
1012  new_arc->radius = KiROUND( readDouble( aData.graphic_data7 ) * aScale );
1013  new_arc->width = KiROUND( readDouble( aData.graphic_data8 ) * aScale );
1014 
1015  new_arc->clockwise = ( aData.graphic_data9 != "COUNTERCLOCKWISE" );
1016 
1017  double startangle = NormalizeAnglePos( RAD2DECIDEG(
1018  atan2( new_arc->start_y - new_arc->center_y,
1019  new_arc->start_x - new_arc->center_x ) ) );
1020  double endangle = NormalizeAnglePos( RAD2DECIDEG(
1021  atan2( new_arc->end_y - new_arc->center_y,
1022  new_arc->end_x - new_arc->center_x ) ) );
1023  double angle;
1024 
1025  VECTOR2I center( new_arc->center_x, new_arc->center_y );
1026  VECTOR2I start( new_arc->start_x, new_arc->start_y );
1027  VECTOR2I mid( new_arc->start_x, new_arc->start_y );
1028  VECTOR2I end( new_arc->end_x, new_arc->end_y );
1029 
1030  angle = endangle - startangle;
1031 
1032  if( new_arc->clockwise && angle < 0.0 )
1033  angle += 3600.0;
1034  if( !new_arc->clockwise && angle > 0.0 )
1035  angle -= 3600.0;
1036 
1037  if( start == end )
1038  angle = -3600.0;
1039 
1040  RotatePoint( mid, center, -angle / 2.0 );
1041 
1042  new_arc->result = SHAPE_ARC( start, mid, end, 0 );
1043 
1044  return new_arc;
1045 }
1046 
1048 {
1049  GRAPHIC_RECTANGLE* new_rect = new GRAPHIC_RECTANGLE;
1050 
1051  new_rect->shape = GR_SHAPE_RECTANGLE;
1052  new_rect->start_x = KiROUND( readDouble( aData.graphic_data1 ) * aScale );
1053  new_rect->start_y = -KiROUND( readDouble( aData.graphic_data2 ) * aScale );
1054  new_rect->end_x = KiROUND( readDouble( aData.graphic_data3 ) * aScale );
1055  new_rect->end_y = -KiROUND( readDouble( aData.graphic_data4 ) * aScale );
1056  new_rect->fill = aData.graphic_data5 == "1";
1057  new_rect->width = 0;
1058 
1059  return new_rect;
1060 }
1061 
1063 {
1064  GRAPHIC_TEXT* new_text = new GRAPHIC_TEXT;
1065 
1066  new_text->shape = GR_SHAPE_TEXT;
1067  new_text->start_x = KiROUND( readDouble( aData.graphic_data1 ) * aScale );
1068  new_text->start_y = -KiROUND( readDouble( aData.graphic_data2 ) * aScale );
1069  new_text->rotation = KiROUND( readDouble( aData.graphic_data3 ) );
1070  new_text->mirror = ( aData.graphic_data4 == "YES" );
1071 
1072  if( aData.graphic_data5 == "RIGHT" )
1073  new_text->orient = GR_TEXT_HJUSTIFY_RIGHT;
1074  else if( aData.graphic_data5 == "CENTER" )
1075  new_text->orient = GR_TEXT_HJUSTIFY_CENTER;
1076  else
1077  new_text->orient = GR_TEXT_HJUSTIFY_LEFT;
1078 
1079  std::vector<std::string> toks = split( aData.graphic_data6, " \t" );
1080 
1081  if( toks.size() < 8 )
1082  {
1083  // We log the error here but continue in the case of too few tokens
1084  wxLogError( wxString::Format( _( "Invalid token count."
1085  " Expected 8 but found %zu" ), toks.size() ) );
1086  new_text->height = 0;
1087  new_text->width = 0;
1088  new_text->ital = false;
1089  new_text->thickness = 0;
1090  }
1091  else
1092  {
1093  // 0 = size
1094  // 1 = font
1095  new_text->height = KiROUND( readDouble( toks[2] ) * aScale );
1096  new_text->width = KiROUND( readDouble( toks[3] ) * aScale );
1097  new_text->ital = readDouble( toks[4] ) != 0.0;
1098  // 5 = character spacing
1099  // 6 = line spacing
1100  new_text->thickness = KiROUND( readDouble( toks[7] ) * aScale );
1101  }
1102 
1103  new_text->text = aData.graphic_data7;
1104  return new_text;
1105 }
1106 
1107 
1109 {
1110  GRAPHIC_ITEM* retval = nullptr;
1111 
1112  if( aData.graphic_dataname == "LINE" )
1113  retval = processLine( aData, aScale );
1114  else if( aData.graphic_dataname == "ARC" )
1115  retval = processArc( aData, aScale );
1116  else if( aData.graphic_dataname == "RECTANGLE" )
1117  retval = processRectangle( aData, aScale );
1118  else if( aData.graphic_dataname == "TEXT" )
1119  retval = processText( aData, aScale );
1120 
1121  if( retval && !aData.graphic_data10.empty() )
1122  {
1123  if( aData.graphic_data10 == "CONNECT" )
1124  retval->type = GR_TYPE_CONNECT;
1125  else if( aData.graphic_data10 == "NOTCONNECT" )
1126  retval->type = GR_TYPE_NOTCONNECT;
1127  else if( aData.graphic_data10 == "SHAPE" )
1128  retval->type = GR_TYPE_NOTCONNECT;
1129  else if( aData.graphic_data10 == "VOID" )
1130  retval->type = GR_TYPE_NOTCONNECT;
1131  else if( aData.graphic_data10 == "POLYGON" )
1132  retval->type = GR_TYPE_NOTCONNECT;
1133  else
1134  retval->type = GR_TYPE_NONE;
1135  }
1136 
1137  return retval;
1138 }
1139 
1140 
1146 size_t FABMASTER::processGeometry( size_t aRow )
1147 {
1148  size_t rownum = aRow + 2;
1149 
1150  if( rownum >= rows.size() )
1151  return -1;
1152 
1153  const single_row& header = rows[aRow];
1154  double scale_factor = processScaleFactor( aRow + 1 );
1155 
1156  if( scale_factor <= 0.0 )
1157  return -1;
1158 
1159  int geo_name_col = getColFromName( aRow, "GRAPHICDATANAME" );
1160  int geo_num_col = getColFromName( aRow, "GRAPHICDATANUMBER" );
1161  int geo_tag_col = getColFromName( aRow, "RECORDTAG" );
1162  int geo_grdata1_col = getColFromName( aRow, "GRAPHICDATA1" );
1163  int geo_grdata2_col = getColFromName( aRow, "GRAPHICDATA2" );
1164  int geo_grdata3_col = getColFromName( aRow, "GRAPHICDATA3" );
1165  int geo_grdata4_col = getColFromName( aRow, "GRAPHICDATA4" );
1166  int geo_grdata5_col = getColFromName( aRow, "GRAPHICDATA5" );
1167  int geo_grdata6_col = getColFromName( aRow, "GRAPHICDATA6" );
1168  int geo_grdata7_col = getColFromName( aRow, "GRAPHICDATA7" );
1169  int geo_grdata8_col = getColFromName( aRow, "GRAPHICDATA8" );
1170  int geo_grdata9_col = getColFromName( aRow, "GRAPHICDATA9" );
1171  int geo_subclass_col = getColFromName( aRow, "SUBCLASS" );
1172  int geo_sym_name_col = getColFromName( aRow, "SYMNAME" );
1173  int geo_refdes_col = getColFromName( aRow, "REFDES" );
1174 
1175  if( geo_name_col < 0 || geo_num_col < 0 || geo_grdata1_col < 0 || geo_grdata2_col < 0
1176  || geo_grdata3_col < 0 || geo_grdata4_col < 0 || geo_grdata5_col < 0
1177  || geo_grdata6_col < 0 || geo_grdata7_col < 0 || geo_grdata8_col < 0
1178  || geo_grdata9_col < 0 || geo_subclass_col < 0 || geo_sym_name_col < 0
1179  || geo_refdes_col < 0 )
1180  return -1;
1181 
1182  for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1183  {
1184  const single_row& row = rows[rownum];
1185 
1186  if( row.size() != header.size() )
1187  {
1188  wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
1189  "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
1190  continue;
1191  }
1192 
1193  auto geo_tag = row[geo_tag_col];
1194 
1195  GRAPHIC_DATA gr_data;
1196  gr_data.graphic_dataname = row[geo_name_col];
1197  gr_data.graphic_datanum = row[geo_num_col];
1198  gr_data.graphic_data1 = row[geo_grdata1_col];
1199  gr_data.graphic_data2 = row[geo_grdata2_col];
1200  gr_data.graphic_data3 = row[geo_grdata3_col];
1201  gr_data.graphic_data4 = row[geo_grdata4_col];
1202  gr_data.graphic_data5 = row[geo_grdata5_col];
1203  gr_data.graphic_data6 = row[geo_grdata6_col];
1204  gr_data.graphic_data7 = row[geo_grdata7_col];
1205  gr_data.graphic_data8 = row[geo_grdata8_col];
1206  gr_data.graphic_data9 = row[geo_grdata9_col];
1207 
1208  auto geo_refdes = row[geo_refdes_col];
1209 
1210  // Grouped graphics are a series of records with the same record ID but incrementing
1211  // Sequence numbers.
1212  int id = -1;
1213  int seq = -1;
1214  int subseq = 0;
1215 
1216  if( std::sscanf( geo_tag.c_str(), "%d %d %d", &id, &seq, &subseq ) < 2 )
1217  {
1218  wxLogError( wxString::Format( _( "Invalid format for record_tag string \"%s\" "
1219  "in Geometric definition row %zu" ),
1220  geo_tag.c_str(), rownum ) );
1221  continue;
1222  }
1223 
1224  auto gr_item = std::unique_ptr<GRAPHIC_ITEM>( processGraphic( gr_data, scale_factor ) );
1225 
1226  if( !gr_item )
1227  {
1228  wxLogDebug( wxString::Format( _( "Unhandled graphic item '%s' "
1229  "in Geometric definition row %zu" ),
1230  gr_data.graphic_dataname.c_str(), geo_tag.c_str(), rownum ) );
1231  continue;
1232  }
1233 
1234  gr_item->layer = row[geo_subclass_col];
1235  gr_item->seq = seq;
1236  gr_item->subseq = subseq;
1237 
1238  if( geo_refdes.empty() )
1239  {
1240  if( board_graphics.empty() || board_graphics.back().id != id )
1241  {
1242  GEOM_GRAPHIC new_gr;
1243  new_gr.subclass = row[geo_subclass_col];
1244  new_gr.refdes = row[geo_refdes_col];
1245  new_gr.name = row[geo_sym_name_col];
1246  new_gr.id = id;
1247  new_gr.elements = std::make_unique<graphic_element>();
1248  board_graphics.push_back( std::move( new_gr ) );
1249  }
1250 
1251  GEOM_GRAPHIC& graphic = board_graphics.back();
1252  graphic.elements->emplace( std::move( gr_item ) );
1253  }
1254  else
1255  {
1256  auto sym_gr_it = comp_graphics.emplace( geo_refdes,
1257  std::map<int, GEOM_GRAPHIC>{} );
1258  auto map_it = sym_gr_it.first->second.emplace( id, GEOM_GRAPHIC{} );
1259  auto& gr = map_it.first;
1260 
1261  if( map_it.second )
1262  {
1263  gr->second.subclass = row[geo_subclass_col];
1264  gr->second.refdes = row[geo_refdes_col];
1265  gr->second.name = row[geo_sym_name_col];
1266  gr->second.id = id;
1267  gr->second.elements = std::make_unique<graphic_element>();
1268  }
1269 
1270  auto result = gr->second.elements->emplace( std::move( gr_item ) );
1271  }
1272  }
1273 
1274  return rownum - aRow;
1275 }
1276 
1277 
1281 size_t FABMASTER::processVias( size_t aRow )
1282 {
1283  size_t rownum = aRow + 2;
1284 
1285  if( rownum >= rows.size() )
1286  return -1;
1287 
1288  const single_row& header = rows[aRow];
1289  double scale_factor = processScaleFactor( aRow + 1 );
1290 
1291  if( scale_factor <= 0.0 )
1292  return -1;
1293 
1294  int viax_col = getColFromName( aRow, "VIAX" );
1295  int viay_col = getColFromName( aRow, "VIAY" );
1296  int padstack_name_col = getColFromName( aRow, "PADSTACKNAME" );
1297  int net_name_col = getColFromName( aRow, "NETNAME" );
1298  int test_point_col = getColFromName( aRow, "TESTPOINT" );
1299 
1300  if( viax_col < 0 || viay_col < 0 || padstack_name_col < 0 || net_name_col < 0
1301  || test_point_col < 0 )
1302  return -1;
1303 
1304  for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1305  {
1306  const single_row& row = rows[rownum];
1307 
1308  if( row.size() != header.size() )
1309  {
1310  wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
1311  "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
1312  continue;
1313  }
1314 
1315  vias.emplace_back( std::make_unique<FM_VIA>() );
1316  auto& via = vias.back();
1317 
1318  via->x = KiROUND( readDouble( row[viax_col] ) * scale_factor );
1319  via->y = -KiROUND( readDouble( row[viay_col] ) * scale_factor );
1320  via->padstack = row[padstack_name_col];
1321  via->net = row[net_name_col];
1322  via->test_point = ( row[test_point_col] == "YES" );
1323  }
1324 
1325  return rownum - aRow;
1326 }
1327 
1328 
1334 size_t FABMASTER::processTraces( size_t aRow )
1335 {
1336  size_t rownum = aRow + 2;
1337 
1338  if( rownum >= rows.size() )
1339  return -1;
1340 
1341  const single_row& header = rows[aRow];
1342  double scale_factor = processScaleFactor( aRow + 1 );
1343 
1344  if( scale_factor <= 0.0 )
1345  return -1;
1346 
1347  int class_col = getColFromName( aRow, "CLASS" );
1348  int layer_col = getColFromName( aRow, "SUBCLASS" );
1349  int grdata_name_col = getColFromName( aRow, "GRAPHICDATANAME" );
1350  int grdata_num_col = getColFromName( aRow, "GRAPHICDATANUMBER" );
1351  int tag_col = getColFromName( aRow, "RECORDTAG" );
1352  int grdata1_col = getColFromName( aRow, "GRAPHICDATA1" );
1353  int grdata2_col = getColFromName( aRow, "GRAPHICDATA2" );
1354  int grdata3_col = getColFromName( aRow, "GRAPHICDATA3" );
1355  int grdata4_col = getColFromName( aRow, "GRAPHICDATA4" );
1356  int grdata5_col = getColFromName( aRow, "GRAPHICDATA5" );
1357  int grdata6_col = getColFromName( aRow, "GRAPHICDATA6" );
1358  int grdata7_col = getColFromName( aRow, "GRAPHICDATA7" );
1359  int grdata8_col = getColFromName( aRow, "GRAPHICDATA8" );
1360  int grdata9_col = getColFromName( aRow, "GRAPHICDATA9" );
1361  int netname_col = getColFromName( aRow, "NETNAME" );
1362 
1363  if( class_col < 0 || layer_col < 0 || grdata_name_col < 0 || grdata_num_col < 0
1364  || tag_col < 0 || grdata1_col < 0 || grdata2_col < 0 || grdata3_col < 0
1365  || grdata4_col < 0 || grdata5_col < 0 || grdata6_col < 0 || grdata7_col < 0
1366  || grdata8_col < 0 || grdata9_col < 0 || netname_col < 0 )
1367  return -1;
1368 
1369  for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1370  {
1371  const single_row& row = rows[rownum];
1372 
1373  if( row.size() != header.size() )
1374  {
1375  wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
1376  "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
1377  continue;
1378  }
1379 
1380  GRAPHIC_DATA gr_data;
1381  gr_data.graphic_dataname = row[grdata_name_col];
1382  gr_data.graphic_datanum = row[grdata_num_col];
1383  gr_data.graphic_data1 = row[grdata1_col];
1384  gr_data.graphic_data2 = row[grdata2_col];
1385  gr_data.graphic_data3 = row[grdata3_col];
1386  gr_data.graphic_data4 = row[grdata4_col];
1387  gr_data.graphic_data5 = row[grdata5_col];
1388  gr_data.graphic_data6 = row[grdata6_col];
1389  gr_data.graphic_data7 = row[grdata7_col];
1390  gr_data.graphic_data8 = row[grdata8_col];
1391  gr_data.graphic_data9 = row[grdata9_col];
1392 
1393  const std::string& geo_tag = row[tag_col];
1394  // Grouped graphics are a series of records with the same record ID but incrementing
1395  // Sequence numbers.
1396  int id = -1;
1397  int seq = -1;
1398  int subseq = 0;
1399 
1400  if( std::sscanf( geo_tag.c_str(), "%d %d %d", &id, &seq, &subseq ) < 2 )
1401  {
1402  wxLogError( wxString::Format( _( "Invalid format for record_tag string \"%s\" "
1403  "in Traces definition row %zu" ),
1404  geo_tag.c_str(), rownum ) );
1405  continue;
1406  }
1407 
1408  auto gr_item = std::unique_ptr<GRAPHIC_ITEM>( processGraphic( gr_data, scale_factor ) );
1409 
1410  if( !gr_item )
1411  {
1412  wxLogDebug( wxString::Format( _( "Unhandled graphic item '%s' "
1413  "in Traces definition row %zu" ),
1414  gr_data.graphic_dataname.c_str(), rownum ) );
1415  continue;
1416  }
1417 
1418  auto new_trace = std::make_unique<TRACE>();
1419  new_trace->id = id;
1420  new_trace->layer = row[layer_col];
1421  new_trace->netname = row[netname_col];
1422  new_trace->lclass = row[class_col];
1423 
1424  gr_item->layer = row[layer_col];
1425  gr_item->seq = seq;
1426  gr_item->subseq = subseq;
1427 
1428  // Collect the reference designator positions for the footprints later
1429  if( new_trace->lclass == "REF DES" )
1430  {
1431  auto result = refdes.emplace( std::move( new_trace ) );
1432  auto& ref = *result.first;
1433  ref->segment.emplace( std::move( gr_item ) );
1434  }
1435  else if( gr_item->width == 0 )
1436  {
1437  auto result = zones.emplace( std::move( new_trace ) );
1438  auto& zone = *result.first;
1439  auto gr_result = zone->segment.emplace( std::move( gr_item ) );
1440 
1441  if( !gr_result.second )
1442  {
1443  wxLogError( wxString::Format( _( "Duplicate item for ID %d and sequence %d "
1444  "in Traces definition row %zu \n" ), id, seq, rownum ) );
1445  }
1446 
1447  }
1448  else
1449  {
1450  auto result = traces.emplace( std::move( new_trace ) );
1451  auto& trace = *result.first;
1452  auto gr_result = trace->segment.emplace( std::move( gr_item ) );
1453 
1454  if( !gr_result.second )
1455  {
1456  wxLogError( wxString::Format( _( "Duplicate item for ID %d and sequence %d "
1457  "in Traces definition row %zu \n" ), id, seq, rownum ) );
1458  }
1459  }
1460  }
1461 
1462  return rownum - aRow;
1463 }
1464 
1465 
1466 FABMASTER::SYMTYPE FABMASTER::parseSymType( const std::string& aSymType )
1467 {
1468  if( aSymType == "PACKAGE" )
1469  return SYMTYPE_PACKAGE;
1470  else if( aSymType == "DRAFTING")
1471  return SYMTYPE_DRAFTING;
1472  else if( aSymType == "MECHANICAL" )
1473  return SYMTYPE_MECH;
1474  else if( aSymType == "FORMAT" )
1475  return SYMTYPE_FORMAT;
1476 
1477  return SYMTYPE_NONE;
1478 }
1479 
1480 
1481 FABMASTER::COMPCLASS FABMASTER::parseCompClass( const std::string& aCmpClass )
1482 {
1483  if( aCmpClass == "IO" )
1484  return COMPCLASS_IO;
1485  else if( aCmpClass == "IC" )
1486  return COMPCLASS_IC;
1487  else if( aCmpClass == "DISCRETE" )
1488  return COMPCLASS_DISCRETE;
1489 
1490  return COMPCLASS_NONE;
1491 }
1492 
1497 size_t FABMASTER::processFootprints( size_t aRow )
1498 {
1499  size_t rownum = aRow + 2;
1500 
1501  if( rownum >= rows.size() )
1502  return -1;
1503 
1504  const single_row& header = rows[aRow];
1505  double scale_factor = processScaleFactor( aRow + 1 );
1506 
1507  if( scale_factor <= 0.0 )
1508  return -1;
1509 
1510  int refdes_col = getColFromName( aRow, "REFDES" );
1511  int compclass_col = getColFromName( aRow, "COMPCLASS" );
1512  int comppartnum_col = getColFromName( aRow, "COMPPARTNUMBER" );
1513  int compheight_col = getColFromName( aRow, "COMPHEIGHT" );
1514  int compdevlabelcol = getColFromName( aRow, "COMPDEVICELABEL" );
1515  int compinscode_col = getColFromName( aRow, "COMPINSERTIONCODE" );
1516  int symtype_col = getColFromName( aRow, "SYMTYPE" );
1517  int symname_col = getColFromName( aRow, "SYMNAME" );
1518  int symmirror_col = getColFromName( aRow, "SYMMIRROR" );
1519  int symrotate_col = getColFromName( aRow, "SYMROTATE" );
1520  int symx_col = getColFromName( aRow, "SYMX" );
1521  int symy_col = getColFromName( aRow, "SYMY" );
1522  int compvalue_col = getColFromName( aRow, "COMPVALUE" );
1523  int comptol_col = getColFromName( aRow, "COMPTOL" );
1524  int compvolt_col = getColFromName( aRow, "COMPVOLTAGE" );
1525 
1526  if( refdes_col < 0 || compclass_col < 0 || comppartnum_col < 0 || compheight_col < 0
1527  || compdevlabelcol < 0 || compinscode_col < 0 || symtype_col < 0 || symname_col < 0
1528  || symmirror_col < 0 || symrotate_col < 0 || symx_col < 0 || symy_col < 0
1529  || compvalue_col < 0 || comptol_col < 0 || compvolt_col < 0 )
1530  return -1;
1531 
1532  for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1533  {
1534  const single_row& row = rows[rownum];
1535 
1536  if( row.size() != header.size() )
1537  {
1538  wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
1539  "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
1540  continue;
1541  }
1542 
1543  auto cmp = std::make_unique<COMPONENT>();
1544 
1545  cmp->refdes = row[refdes_col];
1546  cmp->cclass = parseCompClass( row[compclass_col] );
1547  cmp->pn = row[comppartnum_col];
1548  cmp->height = row[compheight_col];
1549  cmp->dev_label = row[compdevlabelcol];
1550  cmp->insert_code = row[compinscode_col];
1551  cmp->type = parseSymType( row[symtype_col] );
1552  cmp->name = row[symname_col];
1553  cmp->mirror = ( row[symmirror_col] == "YES" );
1554  cmp->rotate = readDouble( row[symrotate_col] );
1555  cmp->x = KiROUND( readDouble( row[symx_col] ) * scale_factor );
1556  cmp->y = -KiROUND( readDouble( row[symy_col] ) * scale_factor );
1557  cmp->value = row[compvalue_col];
1558  cmp->tol = row[comptol_col];
1559  cmp->voltage = row[compvolt_col];
1560 
1561  auto vec = components.find( cmp->refdes );
1562 
1563  if( vec == components.end() )
1564  {
1565  auto retval = components.insert( std::make_pair( cmp->refdes, std::vector<std::unique_ptr<COMPONENT>>{} ) );
1566 
1567  vec = retval.first;
1568  }
1569 
1570  vec->second.push_back( std::move( cmp ) );
1571  }
1572 
1573  return rownum - aRow;
1574 }
1575 
1576 
1580 size_t FABMASTER::processPins( size_t aRow )
1581 {
1582  size_t rownum = aRow + 2;
1583 
1584  if( rownum >= rows.size() )
1585  return -1;
1586 
1587  const single_row& header = rows[aRow];
1588  double scale_factor = processScaleFactor( aRow + 1 );
1589 
1590  if( scale_factor <= 0.0 )
1591  return -1;
1592 
1593  int symname_col = getColFromName( aRow, "SYMNAME" );
1594  int symmirror_col = getColFromName( aRow, "SYMMIRROR" );
1595  int pinname_col = getColFromName( aRow, "PINNAME" );
1596  int pinnum_col = getColFromName( aRow, "PINNUMBER" );
1597  int pinx_col = getColFromName( aRow, "PINX" );
1598  int piny_col = getColFromName( aRow, "PINY" );
1599  int padstack_col = getColFromName( aRow, "PADSTACKNAME" );
1600  int refdes_col = getColFromName( aRow, "REFDES" );
1601  int pinrot_col = getColFromName( aRow, "PINROTATION" );
1602  int testpoint_col = getColFromName( aRow, "TESTPOINT" );
1603 
1604  if( symname_col < 0 ||symmirror_col < 0 || pinname_col < 0 || pinnum_col < 0 || pinx_col < 0
1605  || piny_col < 0 || padstack_col < 0 || refdes_col < 0 || pinrot_col < 0
1606  || testpoint_col < 0 )
1607  return -1;
1608 
1609  for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1610  {
1611  const single_row& row = rows[rownum];
1612 
1613  if( row.size() != header.size() )
1614  {
1615  wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
1616  "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
1617  continue;
1618  }
1619 
1620  auto pin = std::make_unique<PIN>();
1621 
1622  pin->name = row[symname_col];
1623  pin->mirror = ( row[symmirror_col] == "YES" );
1624  pin->pin_name = row[pinname_col];
1625  pin->pin_number = row[pinnum_col];
1626  pin->pin_x = KiROUND( readDouble( row[pinx_col] ) * scale_factor );
1627  pin->pin_y = -KiROUND( readDouble( row[piny_col] ) * scale_factor );
1628  pin->padstack = row[padstack_col];
1629  pin->refdes = row[refdes_col];
1630  pin->rotation = readDouble( row[pinrot_col] );
1631 
1632  auto map_it = pins.find( pin->refdes );
1633 
1634  if( map_it == pins.end() )
1635  {
1636  auto retval = pins.insert( std::make_pair( pin->refdes, std::set<std::unique_ptr<PIN>, PIN::BY_NUM>{} ) );
1637  map_it = retval.first;
1638  }
1639 
1640  map_it->second.insert( std::move( pin ) );
1641  }
1642 
1643  return rownum - aRow;
1644 }
1645 
1646 
1650 size_t FABMASTER::processNets( size_t aRow )
1651 {
1652  size_t rownum = aRow + 2;
1653 
1654  if( rownum >= rows.size() )
1655  return -1;
1656 
1657  const single_row& header = rows[aRow];
1658  double scale_factor = processScaleFactor( aRow + 1 );
1659 
1660  if( scale_factor <= 0.0 )
1661  return -1;
1662 
1663  int netname_col = getColFromName( aRow, "NETNAME" );
1664  int refdes_col = getColFromName( aRow, "REFDES" );
1665  int pinnum_col = getColFromName( aRow, "PINNUMBER" );
1666  int pinname_col = getColFromName( aRow, "PINNAME" );
1667  int pingnd_col = getColFromName( aRow, "PINGROUND" );
1668  int pinpwr_col = getColFromName( aRow, "PINPOWER" );
1669 
1670  if( netname_col < 0 || refdes_col < 0 || pinnum_col < 0 || pinname_col < 0 || pingnd_col < 0
1671  || pinpwr_col < 0 )
1672  return -1;
1673 
1674  for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1675  {
1676  const single_row& row = rows[rownum];
1677 
1678  if( row.size() != header.size() )
1679  {
1680  wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
1681  "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
1682  continue;
1683  }
1684 
1685  NETNAME new_net;
1686  new_net.name = row[netname_col];
1687  new_net.refdes = row[refdes_col];
1688  new_net.pin_num = row[pinnum_col];
1689  new_net.pin_name = row[pinname_col];
1690  new_net.pin_gnd = ( row[pingnd_col] == "YES" );
1691  new_net.pin_pwr = ( row[pinpwr_col] == "YES" );
1692 
1693  pin_nets.emplace( std::make_pair( new_net.refdes, new_net.pin_num ), new_net );
1694  netnames.insert( row[netname_col] );
1695  }
1696 
1697  return rownum - aRow;
1698 }
1699 
1700 
1702 {
1703 
1704  for( size_t i = 0; i < rows.size(); )
1705  {
1706  auto type = detectType( i );
1707 
1708  switch( type )
1709  {
1710  case EXTRACT_PADSTACKS:
1711  {
1714  processPadStackLayers( i );
1715  assignLayers();
1716  int retval = processPadStacks( i );
1717 
1718  i += std::max( retval, 1 );
1719  break;
1720  }
1721 
1722  case EXTRACT_FULL_LAYERS:
1723  {
1724  int retval = processLayers( i );
1725 
1726  i += std::max( retval, 1 );
1727  break;
1728  }
1729 
1730  case EXTRACT_BASIC_LAYERS:
1731  {
1732  int retval = processSimpleLayers( i );
1733 
1734  i += std::max( retval, 1 );
1735  break;
1736  }
1737 
1738  case EXTRACT_VIAS:
1739  {
1740  int retval = processVias( i );
1741 
1742  i += std::max( retval, 1 );
1743  break;
1744  }
1745 
1746  case EXTRACT_TRACES:
1747  {
1748  int retval = processTraces( i );
1749 
1750  i += std::max( retval, 1 );
1751  break;
1752  }
1753 
1754  case EXTRACT_REFDES:
1755  {
1756  int retval = processFootprints( i );
1757 
1758  i += std::max( retval, 1 );
1759  break;
1760  }
1761 
1762  case EXTRACT_NETS:
1763  {
1764  int retval = processNets( i );
1765 
1766  i += std::max( retval, 1 );
1767  break;
1768  }
1769 
1770  case EXTRACT_GRAPHICS:
1771  {
1772  int retval = processGeometry( i );
1773 
1774  i += std::max( retval, 1 );
1775  break;
1776  }
1777 
1778  case EXTRACT_PINS:
1779  {
1780  int retval = processPins( i );
1781 
1782  i += std::max( retval, 1 );
1783  break;
1784  }
1785 
1786  case EXTRACT_PAD_SHAPES:
1787  {
1788  int retval = processCustomPads( i );
1789 
1790  i += std::max( retval, 1 );
1791  break;
1792  }
1793 
1794  default:
1795  ++i;
1796  break;
1797  }
1798 
1799  }
1800 
1801  return true;
1802 }
1803 
1804 
1806 {
1807 
1808  for( auto& zone : zones )
1809  {
1810  if( IsCopperLayer( getLayer( zone->layer ) ) || zone->layer == "ALL" )
1811  loadZone( aBoard, zone );
1812  else
1813  {
1814  if( zone->layer == "OUTLINE" || zone->layer == "DESIGN_OUTLINE" )
1815  {
1816  loadOutline( aBoard, zone );
1817  }
1818  else
1819  {
1820  loadPolygon( aBoard, zone );
1821  }
1822  }
1823  }
1824 
1835  std::set<ZONE*> zones_to_delete;
1836 
1837  for( auto zone : aBoard->Zones() )
1838  {
1840  if( zone->GetNetCode() > 0 )
1841  {
1842  zones_to_delete.insert( zone );
1843  }
1844  }
1845 
1846  for( auto zone1 : aBoard->Zones() )
1847  {
1849  if( zone1->GetNetCode() > 0 )
1850  continue;
1851 
1852  SHAPE_LINE_CHAIN& outline1 = zone1->Outline()->Outline( 0 );
1853  std::vector<size_t> overlaps( aBoard->GetNetInfo().GetNetCount() + 1, 0 );
1854  std::vector<std::vector<ZONE*>> possible_deletions( overlaps.size() );
1855 
1856  for( auto zone2 : aBoard->Zones() )
1857  {
1858  if( zone2->GetNetCode() <= 0 )
1859  continue;
1860 
1861  SHAPE_LINE_CHAIN& outline2 = zone2->Outline()->Outline( 0 );
1862 
1863  if( zone1->GetLayer() != zone2->GetLayer() )
1864  continue;
1865 
1866  if( !outline1.BBox().Intersects( outline2.BBox() ) )
1867  continue;
1868 
1869  for( auto& pt1 : outline1.CPoints() )
1870  {
1872  if( outline2.PointOnEdge( pt1, 1 ) )
1873  overlaps[ zone2->GetNetCode() ]++;
1874  }
1875 
1876  for( auto& pt2 : outline2.CPoints() )
1877  {
1880  if( outline1.PointOnEdge( pt2, 1 ) )
1881  overlaps[ zone2->GetNetCode() ]++;
1882  }
1883  }
1884 
1885  size_t max_net = 0;
1886  size_t max_net_id = 0;
1887 
1888  for( size_t el = 1; el < overlaps.size(); ++el )
1889  {
1890  if( overlaps[el] > max_net )
1891  {
1892  max_net = overlaps[el];
1893  max_net_id = el;
1894  }
1895  }
1896 
1897  if( max_net > 0 )
1898  zone1->SetNetCode( max_net_id );
1899  }
1900 
1901  for( auto zone : zones_to_delete )
1902  {
1903  aBoard->Remove( zone );
1904  delete zone;
1905  }
1906 
1907  return true;
1908 }
1909 
1910 
1912 {
1913  const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
1914  const auto& ds = aBoard->GetDesignSettings();
1915 
1916  for( auto& mod : components )
1917  {
1918  bool has_multiple = mod.second.size() > 1;
1919 
1920  for( int i = 0; i < mod.second.size(); ++i )
1921  {
1922  auto& src = mod.second[i];
1923 
1924  FOOTPRINT* fp = new FOOTPRINT( aBoard );
1925 
1926  wxString mod_ref = src->name;
1927  wxString lib_ref = m_filename.GetName();
1928 
1929  if( has_multiple )
1930  mod_ref.Append( wxString::Format( "_%d", i ) );
1931 
1932  ReplaceIllegalFileNameChars( lib_ref, '_' );
1933  ReplaceIllegalFileNameChars( mod_ref, '_' );
1934 
1935  wxString key = !lib_ref.empty() ? lib_ref + ":" + mod_ref : mod_ref;
1936 
1937  LIB_ID fpID;
1938  fpID.Parse( key, true );
1939  fp->SetFPID( fpID );
1940 
1941  fp->SetPosition( wxPoint( src->x, src->y ) );
1942  fp->SetOrientationDegrees( -src->rotate );
1943 
1944  // KiCad netlisting requires parts to have non-digit + digit annotation.
1945  // If the reference begins with a number, we prepend 'UNK' (unknown) for the source designator
1946  wxString reference = src->refdes;
1947 
1948  if( !std::isalpha( src->refdes[0] ) )
1949  reference.Prepend( "UNK" );
1950 
1951  fp->SetReference( reference );
1952 
1953  fp->SetValue( src->value );
1954  fp->Value().SetLayer( F_Fab );
1955  fp->Value().SetVisible( false );
1956 
1957  for( auto& ref : refdes )
1958  {
1959  const GRAPHIC_TEXT *lsrc =
1960  static_cast<const GRAPHIC_TEXT*>( ( *( ref->segment.begin() ) ).get() );
1961 
1962  if( lsrc->text == src->refdes )
1963  {
1964  FP_TEXT* txt = nullptr;
1965  PCB_LAYER_ID layer = getLayer( ref->layer );
1966 
1967  if( !IsPcbLayer( layer ) )
1968  {
1969  printf("The layer %s is not mapped?\n", ref->layer.c_str() );
1970  continue;
1971  }
1972 
1973  if( layer == F_SilkS || layer == B_SilkS )
1974  txt = &( fp->Reference() );
1975  else
1976  txt = new FP_TEXT( fp );
1977 
1978  if( src->mirror )
1979  {
1980  txt->SetLayer( FlipLayer( layer ) );
1981  txt->SetTextPos( wxPoint( lsrc->start_x, 2 * src->y - ( lsrc->start_y - lsrc->height / 2 ) ) );
1982  }
1983  else
1984  {
1985  txt->SetLayer( layer );
1986  txt->SetTextPos( wxPoint( lsrc->start_x, lsrc->start_y - lsrc->height / 2 ) );
1987  }
1988 
1989  txt->SetText( lsrc->text );
1990  txt->SetItalic( lsrc->ital );
1991  txt->SetTextThickness( lsrc->thickness );
1992  txt->SetTextHeight( lsrc->height );
1993  txt->SetTextWidth( lsrc->width );
1994  txt->SetHorizJustify( lsrc->orient );
1995  txt->SetLocalCoord();
1996 
1997  if( txt != &fp->Reference() )
1998  fp->Add( txt, ADD_MODE::APPEND );
1999  }
2000  }
2001 
2004  fp->SetLayer( F_Cu );
2005 
2006  auto gr_it = comp_graphics.find( src->refdes );
2007 
2008  if( gr_it == comp_graphics.end() )
2009  {
2010  continue;
2011  //TODO: Error
2012  }
2013 
2014  for( auto& gr_ref : gr_it->second )
2015  {
2016  auto& graphic = gr_ref.second;
2017 
2018  for( auto& seg : *graphic.elements )
2019  {
2020  PCB_LAYER_ID layer = Dwgs_User;
2021 
2022  if( IsPcbLayer( getLayer( seg->layer ) ) )
2023  layer = getLayer( seg->layer );
2024 
2025  switch( seg->shape )
2026  {
2027 
2028  case GR_SHAPE_LINE:
2029  {
2030  const GRAPHIC_LINE* lsrc = static_cast<const GRAPHIC_LINE*>( seg.get() );
2031 
2032  FP_SHAPE* line = new FP_SHAPE( fp, PCB_SHAPE_TYPE::SEGMENT );
2033 
2034  if( src->mirror )
2035  {
2036  line->SetLayer( FlipLayer( layer ) );
2037  line->SetStart( wxPoint( lsrc->start_x, 2 * src->y - lsrc->start_y ) );
2038  line->SetEnd( wxPoint( lsrc->end_x, 2 * src->y - lsrc->end_y ) );
2039  }
2040  else
2041  {
2042  line->SetLayer( layer );
2043  line->SetStart( wxPoint( lsrc->start_x, lsrc->start_y ) );
2044  line->SetEnd( wxPoint( lsrc->end_x, lsrc->end_y ) );
2045  }
2046 
2047  line->SetWidth( lsrc->width );
2048  line->SetLocalCoord();
2049 
2050  if( lsrc->width == 0 )
2051  line->SetWidth( ds.GetLineThickness( line->GetLayer() ) );
2052 
2053  fp->Add( line, ADD_MODE::APPEND );
2054  break;
2055  }
2056  case GR_SHAPE_ARC:
2057  {
2058  const GRAPHIC_ARC* lsrc = static_cast<const GRAPHIC_ARC*>( seg.get() );
2059 
2060  FP_SHAPE* arc = new FP_SHAPE( fp, PCB_SHAPE_TYPE::ARC );
2061 
2062  if( src->mirror )
2063  {
2064  arc->SetLayer( FlipLayer( layer ) );
2065  arc->SetCenter( wxPoint( lsrc->center_x, 2 * src->y - lsrc->center_y ) );
2066  arc->SetArcStart( wxPoint( lsrc->end_x, 2 * src->y - lsrc->end_y ) );
2067  arc->SetAngle( lsrc->result.GetCentralAngle() * 10.0 );
2068  }
2069  else
2070  {
2071  arc->SetLayer( layer );
2072  arc->SetCenter( wxPoint( lsrc->center_x, lsrc->center_y ) );
2073  arc->SetArcStart( wxPoint( lsrc->end_x, lsrc->end_y ) );
2074  arc->SetAngle( -lsrc->result.GetCentralAngle() * 10.0 );
2075  }
2076 
2077  arc->SetWidth( lsrc->width );
2078  arc->SetLocalCoord();
2079 
2080  if( lsrc->width == 0 )
2081  arc->SetWidth( ds.GetLineThickness( arc->GetLayer() ) );
2082 
2083  fp->Add( arc, ADD_MODE::APPEND );
2084  break;
2085  }
2086  case GR_SHAPE_RECTANGLE:
2087  {
2088  const GRAPHIC_RECTANGLE *lsrc =
2089  static_cast<const GRAPHIC_RECTANGLE*>( seg.get() );
2090 
2091  FP_SHAPE* rect = new FP_SHAPE( fp, PCB_SHAPE_TYPE::RECT );
2092 
2093  if( src->mirror )
2094  {
2095  rect->SetLayer( FlipLayer( layer ) );
2096  rect->SetStart( wxPoint( lsrc->start_x, 2 * src->y - lsrc->start_y ) );
2097  rect->SetEnd( wxPoint( lsrc->end_x, 2 * src->y - lsrc->end_y ) );
2098  }
2099  else
2100  {
2101  rect->SetLayer( layer );
2102  rect->SetStart( wxPoint( lsrc->start_x, lsrc->start_y ) );
2103  rect->SetEnd( wxPoint( lsrc->end_x, lsrc->end_y ) );
2104  }
2105 
2106  rect->SetWidth( ds.GetLineThickness( rect->GetLayer() ) );
2107  rect->SetLocalCoord();
2108 
2109  fp->Add( rect, ADD_MODE::APPEND );
2110  break;
2111  }
2112  case GR_SHAPE_TEXT:
2113  {
2114  const GRAPHIC_TEXT *lsrc =
2115  static_cast<const GRAPHIC_TEXT*>( seg.get() );
2116 
2117  FP_TEXT* txt = new FP_TEXT( fp );
2118 
2119  if( src->mirror )
2120  {
2121  txt->SetLayer( FlipLayer( layer ) );
2122  txt->SetTextPos( wxPoint( lsrc->start_x, 2 * src->y - ( lsrc->start_y - lsrc->height / 2 ) ) );
2123  }
2124  else
2125  {
2126  txt->SetLayer( layer );
2127  txt->SetTextPos( wxPoint( lsrc->start_x, lsrc->start_y - lsrc->height / 2 ) );
2128  }
2129 
2130  txt->SetText( lsrc->text );
2131  txt->SetItalic( lsrc->ital );
2132  txt->SetTextThickness( lsrc->thickness );
2133  txt->SetTextHeight( lsrc->height );
2134  txt->SetTextWidth( lsrc->width );
2135  txt->SetHorizJustify( lsrc->orient );
2136  txt->SetLocalCoord();
2137 
2138  // FABMASTER doesn't have visibility flags but layers that are not silk should be hidden
2139  // by default to prevent clutter.
2140  if( txt->GetLayer() != F_SilkS && txt->GetLayer() != B_SilkS )
2141  txt->SetVisible( false );
2142 
2143  fp->Add( txt, ADD_MODE::APPEND );
2144  break;
2145  }
2146  default:
2147  continue;
2148  }
2149  }
2150  }
2151 
2152  auto pin_it = pins.find( src->refdes );
2153 
2154  if( pin_it != pins.end() )
2155  {
2156  for( auto& pin : pin_it->second )
2157  {
2158  auto pin_net_it = pin_nets.find( std::make_pair( pin->refdes, pin->pin_number ) );
2159  auto padstack = pads.find( pin->padstack );
2160  std::string netname = "";
2161 
2162  if( pin_net_it != pin_nets.end() )
2163  netname = pin_net_it->second.name;
2164 
2165  auto net_it = netinfo.find( netname );
2166 
2167  PAD* newpad = new PAD( fp );
2168 
2169  if( net_it != netinfo.end() )
2170  newpad->SetNet( net_it->second );
2171  else
2172  newpad->SetNetCode( 0 );
2173 
2174  newpad->SetX( pin->pin_x );
2175 
2176  if( src->mirror )
2177  newpad->SetY( 2 * src->y - pin->pin_y );
2178  else
2179  newpad->SetY( pin->pin_y );
2180 
2181  newpad->SetName( pin->pin_number );
2182 
2183  if( padstack == pads.end() )
2184  {
2186  delete newpad;
2187  continue;
2188  }
2189  else
2190  {
2191  auto& pad = padstack->second;
2192 
2193  newpad->SetShape( pad.shape );
2194 
2195  if( pad.shape == PAD_SHAPE::CUSTOM )
2196  {
2197  // Choose the smaller dimension to ensure the base pad
2198  // is fully hidden by the custom pad
2199  int pad_size = std::min( pad.width, pad.height );
2200 
2201  newpad->SetSize( wxSize( pad_size / 2, pad_size / 2 ) );
2202 
2203  std::string custom_name = pad.custom_name + "_" + pin->refdes + "_" + pin->pin_number;
2204  auto custom_it = pad_shapes.find( custom_name );
2205 
2206  if( custom_it != pad_shapes.end() )
2207  {
2208 
2209  SHAPE_POLY_SET poly_outline;
2210  int last_subseq = 0;
2211  int hole_idx = -1;
2212 
2213  poly_outline.NewOutline();
2214 
2215  // Custom pad shapes have a group of elements
2216  // that are a list of graphical polygons
2217  for( const auto& el : (*custom_it).second.elements )
2218  {
2219  // For now, we are only processing the custom pad for the top layer
2220  // TODO: Use full padstacks when implementing in KiCad
2221  PCB_LAYER_ID primary_layer = src->mirror ? B_Cu : F_Cu;
2222 
2223  if( getLayer( ( *( el.second.begin() ) )->layer ) != primary_layer )
2224  continue;
2225 
2226  for( const auto& seg : el.second )
2227  {
2228  if( seg->subseq > 0 || seg->subseq != last_subseq )
2229  {
2230  poly_outline.Polygon(0).back().SetClosed( true );
2231  hole_idx = poly_outline.AddHole( SHAPE_LINE_CHAIN{} );
2232  }
2233 
2234  if( seg->shape == GR_SHAPE_LINE )
2235  {
2236  const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
2237 
2238  if( poly_outline.VertexCount( 0, hole_idx ) == 0 )
2239  poly_outline.Append( src->start_x, src->start_y, 0, hole_idx );
2240 
2241  poly_outline.Append( src->end_x, src->end_y, 0, hole_idx );
2242  }
2243  else if( seg->shape == GR_SHAPE_ARC )
2244  {
2245  const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
2246  SHAPE_LINE_CHAIN& chain = poly_outline.Hole( 0, hole_idx );
2247 
2248  chain.Append( src->result );
2249  }
2250  }
2251  }
2252 
2253  if( poly_outline.OutlineCount() < 1
2254  || poly_outline.Outline( 0 ).PointCount() < 3 )
2255  {
2256  wxLogError( wxString::Format(
2257  _( "Invalid custom pad named '%s'. Replacing with circular pad." ),
2258  custom_name.c_str() ) );
2259  newpad->SetShape( PAD_SHAPE::CIRCLE );
2260  }
2261  else
2262  {
2263  poly_outline.Fracture( SHAPE_POLY_SET::POLYGON_MODE::PM_FAST );
2264 
2265  poly_outline.Move( -newpad->GetPosition() );
2266 
2267  if( src->mirror )
2268  {
2269  poly_outline.Mirror( false, true, VECTOR2I( 0, ( pin->pin_y - src->y ) ) );
2270  poly_outline.Rotate( ( -src->rotate + pin->rotation ) * M_PI / 180.0 );
2271  }
2272  else
2273  {
2274  poly_outline.Rotate( ( src->rotate - pin->rotation ) * M_PI / 180.0 );
2275  }
2276 
2277  newpad->AddPrimitivePoly( poly_outline, 0, true );
2278  }
2279 
2280  SHAPE_POLY_SET mergedPolygon;
2281  newpad->MergePrimitivesAsPolygon( &mergedPolygon, UNDEFINED_LAYER );
2282 
2283  if( mergedPolygon.OutlineCount() > 1 )
2284  {
2285  wxLogError( wxString::Format(
2286  _( "Invalid custom pad named '%s'. Replacing with circular pad." ),
2287  custom_name.c_str() ) );
2288  newpad->SetShape( PAD_SHAPE::CIRCLE );
2289  }
2290  }
2291  else
2292  {
2293  wxLogError( wxString::Format( _( "Could not find custom pad named %s" ),
2294  custom_name.c_str() ) );
2295  }
2296  }
2297  else
2298  newpad->SetSize( wxSize( pad.width, pad.height ) );
2299 
2300  if( pad.drill )
2301  {
2302  if( pad.plated )
2303  {
2304  newpad->SetAttribute( PAD_ATTRIB::PTH );
2305  newpad->SetLayerSet( PAD::PTHMask() );
2306  }
2307  else
2308  {
2309  newpad->SetAttribute( PAD_ATTRIB::NPTH );
2310  newpad->SetLayerSet( PAD::UnplatedHoleMask() );
2311  }
2312 
2313  if( pad.drill_size_x == pad.drill_size_y )
2315  else
2317 
2318  newpad->SetDrillSize( wxSize( pad.drill_size_x, pad.drill_size_y ) );
2319  }
2320  else
2321  {
2322  newpad->SetAttribute( PAD_ATTRIB::SMD );
2323 
2324  if( pad.top )
2325  newpad->SetLayerSet( PAD::SMDMask() );
2326  else if( pad.bottom )
2327  newpad->SetLayerSet( FlipLayerMask( PAD::SMDMask() ) );
2328  }
2329  }
2330 
2331  newpad->SetLocalCoord();
2332 
2333  if( src->mirror )
2334  newpad->SetOrientation( ( -src->rotate + pin->rotation ) * 10.0 );
2335  else
2336  newpad->SetOrientation( ( src->rotate - pin->rotation ) * 10.0 );
2337 
2338  fp->Add( newpad, ADD_MODE::APPEND );
2339  }
2340  }
2341 
2342  if( src->mirror )
2343  {
2344  fp->SetOrientationDegrees( 180.0 - src->rotate );
2345  fp->Flip( fp->GetPosition(), true );
2346  }
2347 
2348  aBoard->Add( fp, ADD_MODE::APPEND );
2349  }
2350  }
2351 
2352  return true;
2353 }
2354 
2355 
2357 {
2358  LSET layer_set;
2359 
2361  layer_set |= LSET::AllTechMask() | LSET::UserMask();
2362 
2363  for( auto& layer : layers )
2364  {
2365  if( layer.second.layerid >= PCBNEW_LAYER_ID_START )
2366  layer_set.set( layer.second.layerid );
2367  }
2368 
2369  aBoard->SetEnabledLayers( layer_set );
2370 
2371  for( auto& layer : layers )
2372  {
2373  if( layer.second.conductive )
2374  {
2375  aBoard->SetLayerName( static_cast<PCB_LAYER_ID>( layer.second.layerid ),
2376  layer.second.name );
2377  }
2378  }
2379 
2380  return true;
2381 }
2382 
2383 
2385 {
2386  const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
2387  const auto& ds = aBoard->GetDesignSettings();
2388 
2389  for( auto& via : vias )
2390  {
2391  auto net_it = netinfo.find( via->net );
2392  auto padstack = pads.find( via->padstack );
2393 
2394  PCB_VIA* new_via = new PCB_VIA( aBoard );
2395 
2396  new_via->SetPosition( wxPoint( via->x, via->y ) );
2397 
2398  if( net_it != netinfo.end() )
2399  new_via->SetNet( net_it->second );
2400 
2401  if( padstack == pads.end() )
2402  {
2403  new_via->SetDrillDefault();
2404 
2405  if( !ds.m_ViasDimensionsList.empty() )
2406  {
2407  new_via->SetWidth( ds.m_ViasDimensionsList[0].m_Diameter );
2408  new_via->SetDrill( ds.m_ViasDimensionsList[0].m_Drill );
2409  }
2410  else
2411  {
2412  new_via->SetDrillDefault();
2413  new_via->SetWidth( ds.m_ViasMinSize );
2414  }
2415  }
2416  else
2417  {
2418  new_via->SetDrill( padstack->second.drill_size_x );
2419  new_via->SetWidth( padstack->second.width );
2420  }
2421 
2422  aBoard->Add( new_via, ADD_MODE::APPEND );
2423  }
2424 
2425  return true;
2426 }
2427 
2428 
2430 {
2431  for( auto& net : netnames )
2432  {
2433  NETINFO_ITEM *newnet = new NETINFO_ITEM( aBoard, net );
2434  aBoard->Add( newnet, ADD_MODE::APPEND );
2435  }
2436 
2437  return true;
2438 }
2439 
2440 
2441 bool FABMASTER::loadEtch( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine)
2442 {
2443  const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
2444  auto net_it = netinfo.find( aLine->netname );
2445 
2446  int last_subseq = 0;
2447  ZONE* new_zone = nullptr;
2448 
2449  for( const auto& seg : aLine->segment )
2450  {
2451  PCB_LAYER_ID layer = getLayer( seg->layer );
2452 
2453  if( IsCopperLayer( layer ) )
2454  {
2455  if( seg->shape == GR_SHAPE_LINE )
2456  {
2457  const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
2458 
2459  PCB_TRACK* trk = new PCB_TRACK( aBoard );
2460 
2461  trk->SetLayer( layer );
2462  trk->SetStart( wxPoint( src->start_x, src->start_y ) );
2463  trk->SetEnd( wxPoint( src->end_x, src->end_y ) );
2464  trk->SetWidth( src->width );
2465 
2466  if( net_it != netinfo.end() )
2467  trk->SetNet( net_it->second );
2468 
2469  aBoard->Add( trk, ADD_MODE::APPEND );
2470  }
2471  else if( seg->shape == GR_SHAPE_ARC )
2472  {
2473  const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
2474 
2475  PCB_ARC* trk = new PCB_ARC( aBoard, &src->result );
2476  trk->SetLayer( layer );
2477  trk->SetWidth( src->width );
2478 
2479  if( net_it != netinfo.end() )
2480  trk->SetNet( net_it->second );
2481 
2482  aBoard->Add( trk, ADD_MODE::APPEND );
2483  }
2484  }
2485  else
2486  {
2487  wxLogError( wxString::Format( _( "Expecting etch data to be on copper layer. "
2488  "Row found on layer '%s'" ), seg->layer.c_str() ) );
2489  }
2490  }
2491 
2492  return true;
2493 }
2494 
2495 
2497 {
2498  SHAPE_POLY_SET poly_outline;
2499  int last_subseq = 0;
2500  int hole_idx = -1;
2501 
2502  poly_outline.NewOutline();
2503 
2504  for( const auto& seg : aElement )
2505  {
2506  if( seg->subseq > 0 || seg->subseq != last_subseq )
2507  hole_idx = poly_outline.AddHole( SHAPE_LINE_CHAIN{} );
2508 
2509  if( seg->shape == GR_SHAPE_LINE )
2510  {
2511  const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
2512 
2513  if( poly_outline.VertexCount( 0, hole_idx ) == 0 )
2514  poly_outline.Append( src->start_x, src->start_y, 0, hole_idx );
2515 
2516  poly_outline.Append( src->end_x, src->end_y, 0, hole_idx );
2517  }
2518  else if( seg->shape == GR_SHAPE_ARC )
2519  {
2520  const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
2521  SHAPE_LINE_CHAIN& chain = poly_outline.Hole( 0, hole_idx );
2522 
2523  chain.Append( src->result );
2524  }
2525  }
2526 
2527  poly_outline.Fracture( SHAPE_POLY_SET::POLYGON_MODE::PM_FAST );
2528  return poly_outline;
2529 }
2530 
2531 
2532 bool FABMASTER::loadPolygon( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine)
2533 {
2534  if( aLine->segment.size() < 3 )
2535  return false;
2536 
2537  PCB_LAYER_ID layer = Cmts_User;
2538 
2539  auto new_layer = getLayer( aLine->layer );
2540 
2541  if( IsPcbLayer( new_layer ) )
2542  layer = new_layer;
2543 
2544  SHAPE_POLY_SET poly_outline = loadShapePolySet( aLine->segment );
2545 
2546  if( poly_outline.OutlineCount() < 1 || poly_outline.COutline( 0 ).PointCount() < 3 )
2547  return false;
2548 
2549  PCB_SHAPE* new_poly = new PCB_SHAPE( aBoard );
2550 
2551  new_poly->SetShape( PCB_SHAPE_TYPE::POLYGON );
2552  new_poly->SetLayer( layer );
2553 
2554  // Polygons on the silk layer are filled but other layers are not/fill doesn't make sense
2555  if( layer == F_SilkS || layer == B_SilkS )
2556  {
2557  new_poly->SetFilled( true );
2558  new_poly->SetWidth( 0 );
2559  }
2560  else
2561  {
2562  new_poly->SetWidth( ( *( aLine->segment.begin() ) )->width );
2563 
2564  if( new_poly->GetWidth() == 0 )
2565  new_poly->SetWidth( aBoard->GetDesignSettings().GetLineThickness( layer ) );
2566  }
2567 
2568  new_poly->SetPolyShape( poly_outline );
2569  aBoard->Add( new_poly, ADD_MODE::APPEND );
2570 
2571  return true;
2572 
2573 }
2574 
2575 
2576 bool FABMASTER::loadZone( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine)
2577 {
2578  if( aLine->segment.size() < 3 )
2579  return false;
2580 
2581  int last_subseq = 0;
2582  int hole_idx = -1;
2583  SHAPE_POLY_SET* zone_outline = nullptr;
2584  ZONE* zone = nullptr;
2585 
2586  const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
2587  auto net_it = netinfo.find( aLine->netname );
2588  PCB_LAYER_ID layer = Cmts_User;
2589  auto new_layer = getLayer( aLine->layer );
2590 
2591  if( IsPcbLayer( new_layer ) )
2592  layer = new_layer;
2593 
2594  zone = new ZONE( aBoard );
2595  zone_outline = new SHAPE_POLY_SET;
2596 
2597  if( net_it != netinfo.end() )
2598  zone->SetNet( net_it->second );
2599 
2600  if( aLine->layer == "ALL" )
2601  zone->SetLayerSet( aBoard->GetLayerSet() & LSET::AllCuMask() );
2602  else
2603  zone->SetLayer( layer );
2604 
2605  zone->SetIsRuleArea( false );
2606  zone->SetDoNotAllowTracks( false );
2607  zone->SetDoNotAllowVias( false );
2608  zone->SetDoNotAllowPads( false );
2609  zone->SetDoNotAllowFootprints( false );
2610  zone->SetDoNotAllowCopperPour( false );
2611 
2612  if( aLine->lclass == "ROUTE KEEPOUT")
2613  {
2614  zone->SetIsRuleArea( true );
2615  zone->SetDoNotAllowTracks( true );
2616  }
2617  else if( aLine->lclass == "VIA KEEPOUT")
2618  {
2619  zone->SetIsRuleArea( true );
2620  zone->SetDoNotAllowVias( true );
2621  }
2622 
2623  zone->SetPriority( 50 );
2624  zone->SetLocalClearance( 0 );
2626 
2627  zone_outline->NewOutline();
2628 
2629 
2630  for( const auto& seg : aLine->segment )
2631  {
2632  if( seg->subseq > 0 && seg->subseq != last_subseq )
2633  {
2635  if( aLine->lclass == "BOUNDARY" )
2636  break;
2637 
2638  hole_idx = zone_outline->AddHole( SHAPE_LINE_CHAIN{} );
2639  last_subseq = seg->subseq;
2640  last_subseq = seg->subseq;
2641  }
2642 
2643  if( seg->shape == GR_SHAPE_LINE )
2644  {
2645  const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
2646 
2647  if( zone_outline->VertexCount( 0, hole_idx ) == 0 )
2648  zone_outline->Append( src->start_x, src->start_y, 0, hole_idx );
2649 
2650  zone_outline->Append( src->end_x, src->end_y, 0, hole_idx );
2651  }
2652  else if( seg->shape == GR_SHAPE_ARC )
2653  {
2654  const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
2655  zone_outline->Hole( 0, hole_idx ).Append( src->result );
2656  }
2657  }
2658 
2659  if( zone_outline->Outline( 0 ).PointCount() >= 3 )
2660  {
2661  zone->SetOutline( zone_outline );
2662  aBoard->Add( zone, ADD_MODE::APPEND );
2663  }
2664  else
2665  {
2666  delete( zone_outline );
2667  delete( zone );
2668  }
2669 
2670  return true;
2671 }
2672 
2673 
2674 bool FABMASTER::loadOutline( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine)
2675 {
2676  PCB_LAYER_ID layer;
2677 
2678  if( aLine->lclass == "BOARD GEOMETRY" )
2679  layer = Edge_Cuts;
2680  else if( aLine->lclass == "DRAWING FORMAT" )
2681  layer = Dwgs_User;
2682  else
2683  layer = Cmts_User;
2684 
2685  for( auto& seg : aLine->segment )
2686  {
2687  switch( seg->shape )
2688  {
2689 
2690  case GR_SHAPE_LINE:
2691  {
2692  const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
2693 
2694  PCB_SHAPE* line = new PCB_SHAPE( aBoard );
2695  line->SetShape( PCB_SHAPE_TYPE::SEGMENT );
2696  line->SetLayer( layer );
2697  line->SetStart( wxPoint( src->start_x, src->start_y ) );
2698  line->SetEnd( wxPoint( src->end_x, src->end_y ) );
2699  line->SetWidth( src->width );
2700 
2701  if( line->GetWidth() == 0 )
2702  line->SetWidth( aBoard->GetDesignSettings().GetLineThickness( layer ) );
2703 
2704  aBoard->Add( line, ADD_MODE::APPEND );
2705  break;
2706  }
2707  case GR_SHAPE_ARC:
2708  {
2709  const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
2710 
2711  PCB_SHAPE* arc = new PCB_SHAPE( aBoard );
2712  arc->SetShape( PCB_SHAPE_TYPE::ARC );
2713  arc->SetLayer( layer );
2714  arc->SetCenter( wxPoint( src->center_x, src->center_y ) );
2715  arc->SetArcStart( wxPoint( src->start_x, src->start_y ) );
2716  arc->SetAngle( src->result.GetCentralAngle() * 10.0 );
2717  arc->SetWidth( src->width );
2718 
2719  if( arc->GetWidth() == 0 )
2720  arc->SetWidth( aBoard->GetDesignSettings().GetLineThickness( layer ) );
2721 
2722  aBoard->Add( arc, ADD_MODE::APPEND );
2723  break;
2724  }
2725  case GR_SHAPE_RECTANGLE:
2726  {
2727  const GRAPHIC_RECTANGLE *src =
2728  static_cast<const GRAPHIC_RECTANGLE*>( seg.get() );
2729 
2730  PCB_SHAPE* rect = new PCB_SHAPE( aBoard );
2731  rect->SetShape( PCB_SHAPE_TYPE::RECT );
2732  rect->SetLayer( layer );
2733  rect->SetStart( wxPoint( src->start_x, src->start_y ) );
2734  rect->SetEnd( wxPoint( src->end_x, src->end_y ) );
2735  rect->SetWidth( aBoard->GetDesignSettings().GetLineThickness( layer ) );
2736 
2737  aBoard->Add( rect, ADD_MODE::APPEND );
2738  break;
2739  }
2740  case GR_SHAPE_TEXT:
2741  {
2742  const GRAPHIC_TEXT *src =
2743  static_cast<const GRAPHIC_TEXT*>( seg.get() );
2744 
2745  PCB_TEXT* txt = new PCB_TEXT( aBoard );
2746  txt->SetLayer( layer );
2747  txt->SetTextPos( wxPoint( src->start_x, src->start_y - src->height / 2 ) );
2748  txt->SetText( src->text );
2749  txt->SetItalic( src->ital );
2750  txt->SetTextThickness( src->thickness );
2751  txt->SetTextHeight( src->height );
2752  txt->SetTextWidth( src->width );
2753  txt->SetHorizJustify( src->orient );
2754 
2755  aBoard->Add( txt, ADD_MODE::APPEND );
2756  break;
2757  }
2758  default:
2759  return false;
2760  }
2761  }
2762 
2763  return true;
2764 }
2765 
2766 
2768 {
2769 
2770  for( auto& geom : board_graphics )
2771  {
2772  PCB_LAYER_ID layer;
2773 
2774  // The pin numbers are not useful for us outside of the footprints
2775  if( geom.subclass == "PIN_NUMBER" )
2776  continue;
2777 
2778  layer = getLayer( geom.subclass );
2779 
2780  if( !IsPcbLayer( layer ) )
2781  layer = Cmts_User;
2782 
2783  if( !geom.elements->empty() )
2784  {
2786  if( ( *( geom.elements->begin() ) )->width == 0 )
2787  {
2788  SHAPE_POLY_SET poly_outline = loadShapePolySet( *( geom.elements ) );
2789 
2790  if( poly_outline.OutlineCount() < 1 || poly_outline.COutline( 0 ).PointCount() < 3 )
2791  continue;
2792 
2793  PCB_SHAPE* new_poly = new PCB_SHAPE( aBoard );
2794 
2795  new_poly->SetShape( PCB_SHAPE_TYPE::POLYGON );
2796  new_poly->SetLayer( layer );
2797  new_poly->SetPolyShape( poly_outline );
2798  new_poly->SetWidth( 0 );
2799 
2800  if( layer == F_SilkS || layer == B_SilkS )
2801  new_poly->SetFilled( true );
2802 
2803  aBoard->Add( new_poly, ADD_MODE::APPEND );
2804  }
2805  }
2806 
2807  for( auto& seg : *geom.elements )
2808  {
2809  switch( seg->shape )
2810  {
2811 
2812  case GR_SHAPE_LINE:
2813  {
2814  const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
2815 
2816  PCB_SHAPE* line = new PCB_SHAPE( aBoard );
2817  line->SetShape( PCB_SHAPE_TYPE::SEGMENT );
2818  line->SetLayer( layer );
2819  line->SetStart( wxPoint( src->start_x, src->start_y ) );
2820  line->SetEnd( wxPoint( src->end_x, src->end_y ) );
2821  line->SetWidth( src->width );
2822 
2823  aBoard->Add( line, ADD_MODE::APPEND );
2824  break;
2825  }
2826  case GR_SHAPE_ARC:
2827  {
2828  const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
2829 
2830  PCB_SHAPE* arc = new PCB_SHAPE( aBoard );
2831  arc->SetShape( PCB_SHAPE_TYPE::ARC );
2832  arc->SetLayer( layer );
2833  arc->SetCenter( wxPoint( src->center_x, src->center_y ) );
2834  arc->SetArcStart( wxPoint( src->start_x, src->start_y ) );
2835  arc->SetAngle( src->result.GetCentralAngle() * 10.0 );
2836  arc->SetWidth( src->width );
2837 
2838  aBoard->Add( arc, ADD_MODE::APPEND );
2839  break;
2840  }
2841  case GR_SHAPE_RECTANGLE:
2842  {
2843  const GRAPHIC_RECTANGLE *src =
2844  static_cast<const GRAPHIC_RECTANGLE*>( seg.get() );
2845 
2846  PCB_SHAPE* rect = new PCB_SHAPE( aBoard );
2847  rect->SetShape( PCB_SHAPE_TYPE::RECT );
2848  rect->SetLayer( layer );
2849  rect->SetStart( wxPoint( src->start_x, src->start_y ) );
2850  rect->SetEnd( wxPoint( src->end_x, src->end_y ) );
2851  rect->SetWidth( 0 );
2852  aBoard->Add( rect, ADD_MODE::APPEND );
2853  break;
2854  }
2855  case GR_SHAPE_TEXT:
2856  {
2857  const GRAPHIC_TEXT *src =
2858  static_cast<const GRAPHIC_TEXT*>( seg.get() );
2859 
2860  PCB_TEXT* txt = new PCB_TEXT( aBoard );
2861  txt->SetLayer( layer );
2862  txt->SetTextPos( wxPoint( src->start_x, src->start_y - src->height / 2 ) );
2863  txt->SetText( src->text );
2864  txt->SetItalic( src->ital );
2865  txt->SetTextThickness( src->thickness );
2866  txt->SetTextHeight( src->height );
2867  txt->SetTextWidth( src->width );
2868  txt->SetHorizJustify( src->orient );
2869  aBoard->Add( txt, ADD_MODE::APPEND );
2870  break;
2871  }
2872  default:
2873  return false;
2874  }
2875  }
2876  }
2877 
2878  return true;
2879 
2880 }
2881 
2882 
2884 {
2885  std::vector<ZONE*> zones = aBoard->Zones();
2886 
2887  std::sort( zones.begin(), zones.end(),
2888  [&]( const ZONE* a, const ZONE* b ) {
2889  if( a->GetLayer() == b->GetLayer() )
2890  return a->GetBoundingBox().GetArea() > b->GetBoundingBox().GetArea();
2891 
2892  return a->GetLayer() < b->GetLayer();
2893  } );
2894 
2895  PCB_LAYER_ID layer = UNDEFINED_LAYER;
2896  unsigned int priority = 0;
2897 
2898  for( ZONE* zone : zones )
2899  {
2900  if( zone->GetLayer() != layer )
2901  {
2902  layer = zone->GetLayer();
2903  priority = 0;
2904  }
2905 
2906  zone->SetPriority( priority );
2907  priority += 10;
2908  }
2909 
2910  return true;
2911 }
2912 
2913 
2915 {
2916  aBoard->SetFileName( m_filename.GetFullPath() );
2917 
2918  loadNets( aBoard );
2919  loadLayers( aBoard );
2920  loadVias( aBoard );
2921  loadFootprints( aBoard );
2922  loadZones( aBoard );
2923  loadGraphics( aBoard );
2924 
2925  for( auto& track : traces )
2926  {
2927  if( track->lclass == "ETCH" )
2928  {
2929  loadEtch( aBoard, track);
2930  }
2931  else if( track->layer == "OUTLINE" )
2932  {
2933  loadOutline( aBoard, track );
2934  }
2935  }
2936 
2937  orderZones( aBoard );
2938 
2939  return true;
2940 }
void SetReference(const wxString &aReference)
Definition: footprint.h:430
SHAPE_ARC result
! KiCad-style arc representation
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:750
size_t processSimpleLayers(size_t aRow)
bool Read(const std::string &aFile)
std::vector< std::string > single_row
LSET FlipLayerMask(LSET aMask, int aCopperLayersCount)
Calculate the mask layer when flipping a footprint.
Definition: lset.cpp:567
int OutlineCount() const
Return the number of vertices in a given outline/hole.
void SetAttribute(PAD_ATTRIB aAttribute)
Definition: pad.cpp:563
std::map< std::string, std::map< int, GEOM_GRAPHIC > > comp_graphics
ZONES & Zones()
Definition: board.h:239
void SetDoNotAllowTracks(bool aEnable)
Definition: zone.h:742
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:192
void SetDrill(int aDrill)
Function SetDrill sets the drill value for vias.
Definition: pcb_track.h:455
std::string name
! LAYER_SUBCLASS
static constexpr double IU_PER_MM
Mock up a conversion function.
std::map< std::string, std::vector< std::unique_ptr< COMPONENT > > > components
void SetFilled(bool aFlag)
Definition: pcb_shape.h:73
bool ital
! GRAPHIC_DATA_6[4] != 0.0
void SetLayerSet(LSET aLayerSet) override
Definition: zone.cpp:246
bool loadGraphics(BOARD *aBoard)
int readInt(const std::string aStr) const
void SetItalic(bool isItalic)
Definition: eda_text.h:186
PCB_LAYER_ID FlipLayer(PCB_LAYER_ID aLayerId, int aCopperLayersCount)
Definition: lset.cpp:521
int GetWidth() const
Definition: pcb_shape.h:97
GRAPHIC_LINE * processLine(const GRAPHIC_DATA &aData, double aScale)
Smd pad, appears on the solder paste layer (default)
int VertexCount(int aOutline=-1, int aHole=-1) const
Returns the number of holes in a given outline.
void SetTextPos(const wxPoint &aPoint)
Definition: eda_text.h:253
void SetVisible(bool aVisible)
Definition: eda_text.h:192
PCB_LAYER_ID getLayer(const std::string &aLayerName)
int thickness
! GRAPHIC_DATA_6[6]
std::unique_ptr< graphic_element > elements
void SetName(const wxString &aName)
Set the pad name (sometimes called pad number, although it can be an array reference like AA12).
Definition: pad.h:129
int end_x
! GRAPHIC_DATA_3
bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
double RAD2DECIDEG(double rad)
Definition: trigo.h:236
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the aIndex-th subpolygon in the set.
std::string subclass
! SUBCLASS
int start_y
! GRAPHIC_DATA_2
bool ReplaceIllegalFileNameChars(std::string *aName, int aReplaceChar)
Checks aName for illegal file name characters.
Definition: string.cpp:834
void SetPosition(const wxPoint &aPoint) override
Definition: pcb_track.h:393
bool loadEtch(BOARD *aBoard, const std::unique_ptr< TRACE > &aLine)
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:684
bool PointOnEdge(const VECTOR2I &aP, int aAccuracy=0) const
Check if point aP lies on an edge or vertex of the line chain.
bool loadZone(BOARD *aBoard, const std::unique_ptr< FABMASTER::TRACE > &aLine)
void SetOrientationDegrees(double aOrientation)
Definition: footprint.h:180
std::map< std::string, FABMASTER_LAYER > layers
void Rotate(double aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
void SetSize(const wxSize &aSize)
Definition: pad.h:228
std::string refdes
! REFDES
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:228
std::vector< std::unique_ptr< FM_VIA > > vias
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
static LSET AllTechMask()
Return a mask holding all technical layers (no CU layer) on both side.
Definition: lset.cpp:820
VECTOR2< int > VECTOR2I
Definition: vector2d.h:623
int center_x
! GRAPHIC_DATA_5
bool loadNets(BOARD *aBoard)
int PointCount() const
Function PointCount()
void SetPriority(unsigned aPriority)
Definition: zone.h:117
A!LAYER_SORT!LAYER_SUBCLASS!LAYER_ARTWORK!LAYER_USE!LAYER_CONDUCTOR!LAYER_DIELECTRIC_CONSTANT !...
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:588
std::set< std::unique_ptr< TRACE >, TRACE::BY_ID > traces
bool LoadBoard(BOARD *aBoard)
void Mirror(bool aX=true, bool aY=false, const VECTOR2I &aRef={ 0, 0 })
Mirror the line points about y or x (or both)
void Append(int aX, int aY, bool aAllowDuplication=false)
Function Append()
Plated through hole pad.
FP_TEXT & Value()
read/write accessors:
Definition: footprint.h:457
const NETNAMES_MAP & NetsByName() const
Return the name map, at least for python.
Definition: netinfo.h:358
void SetIsRuleArea(bool aEnable)
Definition: zone.h:739
polygon (not yet used for tracks, but could be in microwave apps)
size_t processFootprints(size_t aRow)
A!REFDES!COMP_CLASS!COMP_PART_NUMBER!COMP_HEIGHT!COMP_DEVICE_LABEL!COMP_INSERTION_CODE!...
FP_TEXT & Reference()
Definition: footprint.h:458
int GetLineThickness(PCB_LAYER_ID aLayer) const
Return the default graphic segment thickness from the layer class for the given layer.
bool Intersects(const BOX2< Vec > &aRect) const
Function Intersects.
Definition: box2.h:226
int end_y
! GRAPHIC_DATA_4 ! width is GRAPHIC_DATA_5
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT) override
Adds an item to the container.
Definition: board.cpp:606
section_type detectType(size_t aOffset)
std::vector< GEOM_GRAPHIC > board_graphics
GRAPHIC_ITEM * processGraphic(const GRAPHIC_DATA &aData, double aScale)
Specialty functions for processing graphical data rows into the internal database.
like PAD_PTH, but not plated
PCB_LAYER_ID
A quick note on layer IDs:
bool loadFootprints(BOARD *aBoard)
LSET is a set of PCB_LAYER_IDs.
pads are covered by copper
bool loadVias(BOARD *aBoard)
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:114
const EDA_RECT GetBoundingBox() const override
Definition: zone.cpp:322
int height
! GRAPHIC_DATA_6[2]
std::map< std::pair< std::string, std::string >, NETNAME > pin_nets
size_t processGeometry(size_t aRow)
A!GRAPHIC_DATA_NAME!GRAPHIC_DATA_NUMBER!RECORD_TAG!GRAPHIC_DATA_1!GRAPHIC_DATA_2!GRAPHIC_DATA_3!...
void Move(const VECTOR2I &aVector) override
void SetDrillDefault()
Function SetDrillDefault sets the drill value for vias to the default value UNDEFINED_DRILL_DIAMETER.
Definition: pcb_track.h:476
const std::vector< VECTOR2I > & CPoints() const
void SetLocalCoord()
< Set relative coordinates.
Definition: pad.cpp:548
void SetDoNotAllowPads(bool aEnable)
Definition: zone.h:743
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
virtual PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: zone.cpp:218
double rotation
! GRAPHIC_DATA_3
segment with non rounded ends
void SetOutline(SHAPE_POLY_SET *aOutline)
Definition: zone.h:320
std::set< std::unique_ptr< GRAPHIC_ITEM >, GRAPHIC_ITEM::SEQ_CMP > graphic_element
void SetDrillSize(const wxSize &aSize)
Definition: pad.h:238
void Flip(const wxPoint &aCentre, bool aFlipLeftRight) override
Flip this object, i.e.
Definition: footprint.cpp:1371
size_t processVias(size_t aRow)
A!VIA_X!VIA_Y!PAD_STACK_NAME!NET_NAME!TEST_POINT!
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Changes the name of the layer given by aLayer.
Definition: board.cpp:376
double readDouble(const std::string aStr) const
Reads the double/integer value from a std string independent of the user locale.
static LSET PTHMask()
layer set for a through hole pad
Definition: pad.cpp:151
size_t processPadStacks(size_t aRow)
A!PADNAME!RECNUMBER!LAYER!FIXFLAG!VIAFLAG!PADSHAPE1!PADWIDTH!PADHGHT! PADXOFF!PADYOFF!...
#define _(s)
int center_y
! GRAPHIC_DATA_6
GRAPHIC_RECTANGLE * processRectangle(const GRAPHIC_DATA &aData, double aScale)
void SetDoNotAllowVias(bool aEnable)
Definition: zone.h:741
std::string refdes
!< NET_NAME
std::string pin_num
!< REFDES
GRAPHIC_TYPE type
! Type of graphic item
std::set< std::unique_ptr< TRACE >, TRACE::BY_ID > zones
Handle a list of polygons defining a copper zone.
Definition: zone.h:57
SHAPE_POLY_SET loadShapePolySet(const graphic_element &aLine)
void SetFileName(const wxString &aFileName)
Definition: board.h:226
double GetArea() const
Return the area of the rectangle.
Definition: eda_rect.cpp:483
void SetValue(const wxString &aValue)
Definition: footprint.h:451
int getColFromName(size_t aRow, const std::string &aStr)
void MergePrimitivesAsPolygon(SHAPE_POLY_SET *aMergedPolygon, PCB_LAYER_ID aLayer, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Merge all basic shapes to a SHAPE_POLY_SET.
int NewOutline()
Creates a new hole in a given outline.
int radius
! GRAPHIC_DATA_7 ! width is GRAPHIC_DATA_8
void Fracture(POLYGON_MODE aFastMode)
Convert a single outline slitted ("fractured") polygon into a set ouf outlines with holes.
int AddHole(const SHAPE_LINE_CHAIN &aHole, int aOutline=-1)
Return the area of this poly set.
void SetDrillShape(PAD_DRILL_SHAPE_T aShape)
Definition: pad.h:350
std::string text
! GRAPHIC_DATA_7
std::map< std::string, std::set< std::unique_ptr< PIN >, PIN::BY_NUM > > pins
std::set< std::string > netnames
int id
! RECORD_TAG[0]
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
void SetTextWidth(int aWidth)
Definition: eda_text.h:247
size_t processPins(size_t aRow)
A!SYM_NAME!SYM_MIRROR!PIN_NAME!PIN_NUMBER!PIN_X!PIN_Y!PAD_STACK_NAME!REFDES!PIN_ROTATION!...
void SetFPID(const LIB_ID &aFPID)
Definition: footprint.h:186
std::unordered_map< std::string, FM_PAD > pads
void SetWidth(int aWidth)
Definition: pcb_track.h:101
size_t processCustomPads(size_t aRow)
A!SUBCLASS!PAD_SHAPE_NAME!GRAPHIC_DATA_NAME!GRAPHIC_DATA_NUMBER!RECORD_TAG!GRAPHIC_DATA_1!...
GRAPHIC_ARC * processArc(const GRAPHIC_DATA &aData, double aScale)
Handle the data for a net.
Definition: netinfo.h:64
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition: lib_id.cpp:122
void SetPadConnection(ZONE_CONNECTION aPadConnection)
Definition: zone.h:239
std::unordered_map< std::string, FABMASTER_PAD_SHAPE > pad_shapes
void SetLayerSet(LSET aLayers) override
Definition: pad.h:364
void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
int layerid
! pcbnew layer (assigned)
wxFileName m_filename
int start_x
! GRAPHIC_DATA_1
EDA_TEXT_HJUSTIFY_T orient
! GRAPHIC_DATA_5
bool conductive
! LAYER_CONDUCTOR
const char * name
Definition: DXF_plotter.cpp:59
wxPoint GetPosition() const override
Definition: pad.h:174
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
void SetHorizJustify(EDA_TEXT_HJUSTIFY_T aType)
Definition: eda_text.h:208
void SetDoNotAllowCopperPour(bool aEnable)
Definition: zone.h:740
SHAPE_LINE_CHAIN.
int end_y
! GRAPHIC_DATA_4
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
T NormalizeAnglePos(T Angle)
Normalize angle to be in the 0.0 .
Definition: trigo.h:279
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
void AddPrimitivePoly(const SHAPE_POLY_SET &aPoly, int aThickness, bool aFilled)
Has meaning only for custom shape pads.
bool disable
! if true, prevent the layer elements from being used
void SetY(int y)
Definition: pad.h:219
double GetCentralAngle() const
Definition: shape_arc.cpp:398
usual segment : line with rounded ends
void SetTextHeight(int aHeight)
Definition: eda_text.h:250
void SetWidth(int aWidth)
Definition: pcb_shape.h:96
void SetLocalCoord()
Definition: fp_text.cpp:209
#define IU_PER_MILS
Definition: plotter.cpp:137
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:70
size_t processNets(size_t aRow)
A!NET_NAME!REFDES!PIN_NUMBER!PIN_NAME!PIN_GROUND!PIN_POWER!
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: board.cpp:708
The common library.
size_t processLayers(size_t aRow)
A!LAYER_SORT!LAYER_SUBCLASS!LAYER_ARTWORK!LAYER_USE!LAYER_CONDUCTOR!LAYER_DIELECTRIC_CONSTANT!...
wxPoint GetPosition() const override
Definition: footprint.h:177
unsigned GetNetCount() const
Definition: netinfo.h:339
bool IsCopperLayer(LAYER_NUM aLayerId)
Tests whether a layer is a copper layer.
void SetShape(PAD_SHAPE aShape)
Set the new shape of this pad.
Definition: pad.h:157
COMPCLASS parseCompClass(const std::string &aCompClass)
bool loadPolygon(BOARD *aBoard, const std::unique_ptr< FABMASTER::TRACE > &aLine)
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: zone.cpp:238
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.h:166
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition: pcb_shape.h:247
bool orderZones(BOARD *aBoard)
Sets zone priorities based on zone BB size.
static LSET UnplatedHoleMask()
layer set for a mechanical unplated through hole pad
Definition: pad.cpp:172
void SetLocalClearance(int aClearance)
Definition: zone.h:158
int width
! Various sections depending on type
bool IsPcbLayer(LAYER_NUM aLayer)
Test whether a layer is a valid layer for Pcbnew.
size_t processTraces(size_t aRow)
A!CLASS!SUBCLASS!GRAPHIC_DATA_NAME!GRAPHIC_DATA_NUMBER!RECORD_TAG!GRAPHIC_DATA_1!GRAPHIC_DATA_2!...
POLYGON & Polygon(int aIndex)
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT) override
Removes an item from the container.
Definition: footprint.cpp:472
std::string pin_name
!< PIN_NUMBER
Definition: pad.h:57
static LSET UserMask()
Definition: lset.cpp:834
void SetPosition(const wxPoint &aPos) override
Definition: footprint.cpp:1442
void SetShape(PCB_SHAPE_TYPE aShape)
Definition: pcb_shape.h:109
std::string name
! SYM_NAME
Arcs (with rounded ends)
bool pin_gnd
!< PIN_NAME
int end_x
! GRAPHIC_DATA_3
SYMTYPE parseSymType(const std::string &aSymType)
bool mirror
! GRAPHIC_DATA_4
GRAPHIC_SHAPE shape
! Shape of the graphic_item
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition: pad.cpp:158
bool clockwise
! GRAPHIC_DATA_9
bool loadZones(BOARD *aBoard)
Loads sections of the database into the board.
bool positive
! LAYER_ARTWORK (either POSITIVE or NEGATIVE)
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
void SetEnabledLayers(LSET aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:485
bool loadLayers(BOARD *aBoard)
void SetOrientation(double aAngle)
Set the rotation angle of the pad.
Definition: pad.cpp:582
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:176
std::set< std::unique_ptr< TRACE >, TRACE::BY_ID > refdes
static std::vector< std::string > split(const std::string &aStr, const std::string &aDelim)
Split the input string into a vector of output strings.
Definition: kicad_string.h:290
bool loadOutline(BOARD *aBoard, const std::unique_ptr< TRACE > &aLine)
double processScaleFactor(size_t aRow)
Processes data from text vectors into internal database for further ordering.
void SetDoNotAllowFootprints(bool aEnable)
Definition: zone.h:744
size_t processPadStackLayers(size_t aRow)
bool pin_pwr
!< PIN_GND
void SetX(int x)
Definition: pad.h:220
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Add a new vertex to the contour indexed by aOutline and aHole (defaults to the outline of the last po...
A!SUBCLASS!PAD_SHAPE_NAME!GRAPHIC_DATA_NAME!GRAPHIC_DATA_NUMBER!RECORD_TAG!GRAPHIC_DATA_1!...
GRAPHIC_TEXT * processText(const GRAPHIC_DATA &aData, double aScale)
std::deque< single_row > rows