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