KiCad PCB EDA Suite
gendrill_Excellon_writer.cpp
Go to the documentation of this file.
1 
6 /*
7  * This program source code file is part of KiCad, a free EDA CAD application.
8  *
9  * Copyright (C) 2017 Jean_Pierre Charras <jp.charras at wanadoo.fr>
10  * Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
11  * Copyright (C) 1992-2017 KiCad Developers, see change_log.txt for contributors.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, you may find one here:
25  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
26  * or you may search the http://www.gnu.org website for the version 2 license,
27  * or you may write to the Free Software Foundation, Inc.,
28  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
29  */
30 
37 #include <fctsys.h>
38 
39 #include <vector>
40 
41 #include <plot_common.h>
42 #include <trigo.h>
43 #include <macros.h>
44 #include <kicad_string.h>
45 #include <wxPcbStruct.h>
46 #include <pgm_base.h>
47 #include <build_version.h>
48 
49 #include <class_board.h>
50 #include <class_module.h>
51 #include <class_track.h>
52 
53 #include <pcbplot.h>
54 #include <pcbnew.h>
57 #include <reporter.h>
58 #include <collectors.h>
59 
60 // Comment/uncomment this to write or not a comment
61 // in drill file when PTH and NPTH are merged to flag
62 // tools used for PTH and tools used for NPTH
63 // #define WRITE_PTH_NPTH_COMMENT
64 
65 
67 {
68  m_file = NULL;
69  m_pcb = aPcb;
71  m_conversionUnits = 0.0001;
72  m_unitsDecimal = true;
73  m_mirror = false;
74  m_merge_PTH_NPTH = false;
75  m_minimalHeader = false;
76  m_ShortHeader = false;
78  m_pageInfo = NULL;
79 }
80 
81 
82 void EXCELLON_WRITER::CreateDrillandMapFilesSet( const wxString& aPlotDirectory,
83  bool aGenDrill, bool aGenMap,
84  REPORTER * aReporter )
85 {
86  wxFileName fn;
87  wxString msg;
88 
89  std::vector<DRILL_LAYER_PAIR> hole_sets = getUniqueLayerPairs();
90 
91  // append a pair representing the NPTH set of holes, for separate drill files.
92  if( !m_merge_PTH_NPTH )
93  hole_sets.push_back( DRILL_LAYER_PAIR( F_Cu, B_Cu ) );
94 
95  for( std::vector<DRILL_LAYER_PAIR>::const_iterator it = hole_sets.begin();
96  it != hole_sets.end(); ++it )
97  {
98  DRILL_LAYER_PAIR pair = *it;
99  // For separate drill files, the last layer pair is the NPTH drill file.
100  bool doing_npth = m_merge_PTH_NPTH ? false : ( it == hole_sets.end() - 1 );
101 
102  buildHolesList( pair, doing_npth );
103 
104  // The file is created if it has holes, or if it is the non plated drill file
105  // to be sure the NPTH file is up to date in separate files mode.
106  if( getHolesCount() > 0 || doing_npth )
107  {
108  fn = drillFileName( pair, doing_npth, m_merge_PTH_NPTH );
109  fn.SetPath( aPlotDirectory );
110 
111  if( aGenDrill )
112  {
113  wxString fullFilename = fn.GetFullPath();
114 
115  FILE* file = wxFopen( fullFilename, wxT( "w" ) );
116 
117  if( file == NULL )
118  {
119  if( aReporter )
120  {
121  msg.Printf( _( "** Unable to create %s **\n" ), GetChars( fullFilename ) );
122  aReporter->Report( msg );
123  }
124  break;
125  }
126  else
127  {
128  if( aReporter )
129  {
130  msg.Printf( _( "Create file %s\n" ), GetChars( fullFilename ) );
131  aReporter->Report( msg );
132  }
133  }
134 
135  createDrillFile( file );
136  }
137 
138  if( aGenMap )
139  {
140  fn.SetExt( wxEmptyString ); // Will be added by GenDrillMap
141  wxString fullfilename = fn.GetFullPath() + wxT( "-drl_map" );
142  fullfilename << wxT(".") << GetDefaultPlotExtension( m_mapFileFmt );
143 
144  bool success = GenDrillMapFile( fullfilename, m_mapFileFmt );
145 
146  if( ! success )
147  {
148  if( aReporter )
149  {
150  msg.Printf( _( "** Unable to create %s **\n" ), GetChars( fullfilename ) );
151  aReporter->Report( msg );
152  }
153 
154  return;
155  }
156  else
157  {
158  if( aReporter )
159  {
160  msg.Printf( _( "Create file %s\n" ), GetChars( fullfilename ) );
161  aReporter->Report( msg );
162  }
163  }
164  }
165  }
166  }
167 }
168 
169 
171 {
172  m_file = aFile;
173 
174  int diam, holes_count;
175  int x0, y0, xf, yf, xc, yc;
176  double xt, yt;
177  char line[1024];
178 
179  LOCALE_IO dummy; // Use the standard notation for double numbers
180 
182 
183  holes_count = 0;
184 
185 #ifdef WRITE_PTH_NPTH_COMMENT
186  // if PTH_ and NPTH are merged write a comment in drill file at the
187  // beginning of NPTH section
188  bool writePTHcomment = m_merge_PTH_NPTH;
189  bool writeNPTHcomment = m_merge_PTH_NPTH;
190 #endif
191 
192  /* Write the tool list */
193  for( unsigned ii = 0; ii < m_toolListBuffer.size(); ii++ )
194  {
195  DRILL_TOOL& tool_descr = m_toolListBuffer[ii];
196 
197 #ifdef WRITE_PTH_NPTH_COMMENT
198  if( writePTHcomment && !tool_descr.m_Hole_NotPlated )
199  {
200  writePTHcomment = false;
201  fprintf( m_file, ";TYPE=PLATED\n" );
202  }
203 
204  if( writeNPTHcomment && tool_descr.m_Hole_NotPlated )
205  {
206  writeNPTHcomment = false;
207  fprintf( m_file, ";TYPE=NON_PLATED\n" );
208  }
209 #endif
210 
211  fprintf( m_file, "T%dC%.3f\n", ii + 1, tool_descr.m_Diameter * m_conversionUnits );
212  }
213 
214  fputs( "%\n", m_file ); // End of header info
215  fputs( "G90\n", m_file ); // Absolute mode
216  fputs( "G05\n", m_file ); // Drill mode
217 
218  // Units :
219  if( !m_minimalHeader )
220  {
221  if( m_unitsDecimal )
222  fputs( "M71\n", m_file ); /* M71 = metric mode */
223  else
224  fputs( "M72\n", m_file ); /* M72 = inch mode */
225  }
226 
227  /* Read the hole file and generate lines for normal holes (oblong
228  * holes will be created later) */
229  int tool_reference = -2;
230 
231  for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ )
232  {
233  HOLE_INFO& hole_descr = m_holeListBuffer[ii];
234 
235  if( hole_descr.m_Hole_Shape )
236  continue; // oblong holes will be created later
237 
238  if( tool_reference != hole_descr.m_Tool_Reference )
239  {
240  tool_reference = hole_descr.m_Tool_Reference;
241  fprintf( m_file, "T%d\n", tool_reference );
242  }
243 
244  x0 = hole_descr.m_Hole_Pos.x - m_offset.x;
245  y0 = hole_descr.m_Hole_Pos.y - m_offset.y;
246 
247  if( !m_mirror )
248  y0 *= -1;
249 
250  xt = x0 * m_conversionUnits;
251  yt = y0 * m_conversionUnits;
252  writeCoordinates( line, xt, yt );
253 
254  fputs( line, m_file );
255  holes_count++;
256  }
257 
258  /* Read the hole file and generate lines for normal holes (oblong holes
259  * will be created later) */
260  tool_reference = -2; // set to a value not used for
261  // m_holeListBuffer[ii].m_Tool_Reference
262  for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ )
263  {
264  HOLE_INFO& hole_descr = m_holeListBuffer[ii];
265 
266  if( hole_descr.m_Hole_Shape == 0 )
267  continue; // wait for oblong holes
268 
269  if( tool_reference != hole_descr.m_Tool_Reference )
270  {
271  tool_reference = hole_descr.m_Tool_Reference;
272  fprintf( m_file, "T%d\n", tool_reference );
273  }
274 
275  diam = std::min( hole_descr.m_Hole_Size.x, hole_descr.m_Hole_Size.y );
276 
277  if( diam == 0 )
278  continue;
279 
280  /* Compute the hole coordinates: */
281  xc = x0 = xf = hole_descr.m_Hole_Pos.x - m_offset.x;
282  yc = y0 = yf = hole_descr.m_Hole_Pos.y - m_offset.y;
283 
284  /* Compute the start and end coordinates for the shape */
285  if( hole_descr.m_Hole_Size.x < hole_descr.m_Hole_Size.y )
286  {
287  int delta = ( hole_descr.m_Hole_Size.y - hole_descr.m_Hole_Size.x ) / 2;
288  y0 -= delta;
289  yf += delta;
290  }
291  else
292  {
293  int delta = ( hole_descr.m_Hole_Size.x - hole_descr.m_Hole_Size.y ) / 2;
294  x0 -= delta;
295  xf += delta;
296  }
297 
298  RotatePoint( &x0, &y0, xc, yc, hole_descr.m_Hole_Orient );
299  RotatePoint( &xf, &yf, xc, yc, hole_descr.m_Hole_Orient );
300 
301  if( !m_mirror )
302  {
303  y0 *= -1;
304  yf *= -1;
305  }
306 
307  xt = x0 * m_conversionUnits;
308  yt = y0 * m_conversionUnits;
309  writeCoordinates( line, xt, yt );
310 
311  /* remove the '\n' from end of line, because we must add the "G85"
312  * command to the line: */
313  for( int kk = 0; line[kk] != 0; kk++ )
314  {
315  if( line[kk] == '\n' || line[kk] =='\r' )
316  line[kk] = 0;
317  }
318 
319  fputs( line, m_file );
320  fputs( "G85", m_file ); // add the "G85" command
321 
322  xt = xf * m_conversionUnits;
323  yt = yf * m_conversionUnits;
324  writeCoordinates( line, xt, yt );
325 
326  fputs( line, m_file );
327  fputs( "G05\n", m_file );
328  holes_count++;
329  }
330 
332 
333  return holes_count;
334 }
335 
336 
337 void EXCELLON_WRITER::SetFormat( bool aMetric,
338  ZEROS_FMT aZerosFmt,
339  int aLeftDigits,
340  int aRightDigits )
341 {
342  m_unitsDecimal = aMetric;
343  m_zeroFormat = aZerosFmt;
344 
345  /* Set conversion scale depending on drill file units */
346  if( m_unitsDecimal )
347  m_conversionUnits = 1.0 / IU_PER_MM; // EXCELLON units = mm
348  else
349  m_conversionUnits = 0.001 / IU_PER_MILS; // EXCELLON units = INCHES
350 
351  // Set the zero counts. if aZerosFmt == DECIMAL_FORMAT, these values
352  // will be set, but not used.
353  if( aLeftDigits <= 0 )
354  aLeftDigits = m_unitsDecimal ? 3 : 2;
355 
356  if( aRightDigits <= 0 )
357  aRightDigits = m_unitsDecimal ? 3 : 4;
358 
359  m_precision.m_lhs = aLeftDigits;
360  m_precision.m_rhs = aRightDigits;
361 }
362 
363 
364 void EXCELLON_WRITER::writeCoordinates( char* aLine, double aCoordX, double aCoordY )
365 {
366  wxString xs, ys;
367  int xpad = m_precision.m_lhs + m_precision.m_rhs;
368  int ypad = xpad;
369 
370  switch( m_zeroFormat )
371  {
372  default:
373  case DECIMAL_FORMAT:
374  /* In Excellon files, resolution is 1/1000 mm or 1/10000 inch (0.1 mil)
375  * Although in decimal format, Excellon specifications do not specify
376  * clearly the resolution. However it seems to be 1/1000mm or 0.1 mil
377  * like in non decimal formats, so we trunk coordinates to 3 or 4 digits in mantissa
378  * Decimal format just prohibit useless leading 0:
379  * 0.45 or .45 is right, but 00.54 is incorrect.
380  */
381  if( m_unitsDecimal )
382  {
383  // resolution is 1/1000 mm
384  xs.Printf( wxT( "%.3f" ), aCoordX );
385  ys.Printf( wxT( "%.3f" ), aCoordY );
386  }
387  else
388  {
389  // resolution is 1/10000 inch
390  xs.Printf( wxT( "%.4f" ), aCoordX );
391  ys.Printf( wxT( "%.4f" ), aCoordY );
392  }
393 
394  //Remove useless trailing 0
395  while( xs.Last() == '0' )
396  xs.RemoveLast();
397 
398  while( ys.Last() == '0' )
399  ys.RemoveLast();
400 
401  sprintf( aLine, "X%sY%s\n", TO_UTF8( xs ), TO_UTF8( ys ) );
402  break;
403 
404  case SUPPRESS_LEADING:
405  for( int i = 0; i< m_precision.m_rhs; i++ )
406  {
407  aCoordX *= 10; aCoordY *= 10;
408  }
409 
410  sprintf( aLine, "X%dY%d\n", KiROUND( aCoordX ), KiROUND( aCoordY ) );
411  break;
412 
413  case SUPPRESS_TRAILING:
414  {
415  for( int i = 0; i < m_precision.m_rhs; i++ )
416  {
417  aCoordX *= 10;
418  aCoordY *= 10;
419  }
420 
421  if( aCoordX < 0 )
422  xpad++;
423 
424  if( aCoordY < 0 )
425  ypad++;
426 
427  xs.Printf( wxT( "%0*d" ), xpad, KiROUND( aCoordX ) );
428  ys.Printf( wxT( "%0*d" ), ypad, KiROUND( aCoordY ) );
429 
430  size_t j = xs.Len() - 1;
431 
432  while( xs[j] == '0' && j )
433  xs.Truncate( j-- );
434 
435  j = ys.Len() - 1;
436 
437  while( ys[j] == '0' && j )
438  ys.Truncate( j-- );
439 
440  sprintf( aLine, "X%sY%s\n", TO_UTF8( xs ), TO_UTF8( ys ) );
441  break;
442  }
443 
444  case KEEP_ZEROS:
445  for( int i = 0; i< m_precision.m_rhs; i++ )
446  {
447  aCoordX *= 10; aCoordY *= 10;
448  }
449 
450  if( aCoordX < 0 )
451  xpad++;
452 
453  if( aCoordY < 0 )
454  ypad++;
455 
456  xs.Printf( wxT( "%0*d" ), xpad, KiROUND( aCoordX ) );
457  ys.Printf( wxT( "%0*d" ), ypad, KiROUND( aCoordY ) );
458  sprintf( aLine, "X%sY%s\n", TO_UTF8( xs ), TO_UTF8( ys ) );
459  break;
460  }
461 }
462 
463 
465 {
466  fputs( "M48\n", m_file ); // The beginning of a header
467 
468  if( !m_minimalHeader )
469  {
470  // The next 2 lines in EXCELLON files are comments:
471  wxString msg;
472  msg << wxT("KiCad") << wxT( " " ) << GetBuildVersion();
473 
474  fprintf( m_file, ";DRILL file {%s} date %s\n", TO_UTF8( msg ), TO_UTF8( DateAndTime() ) );
475  msg = wxT( ";FORMAT={" );
476 
477  // Print precision:
480  else
481  msg << wxT( "-:-" ); // in decimal format the precision is irrelevant
482 
483  msg << wxT( "/ absolute / " );
484  msg << ( m_unitsDecimal ? wxT( "metric" ) : wxT( "inch" ) );
485 
486  /* Adding numbers notation format.
487  * this is same as m_Choice_Zeros_Format strings, but NOT translated
488  * because some EXCELLON parsers do not like non ASCII values
489  * so we use ONLY English (ASCII) strings.
490  * if new options are added in m_Choice_Zeros_Format, they must also
491  * be added here
492  */
493  msg << wxT( " / " );
494 
495  const wxString zero_fmt[4] =
496  {
497  wxT( "decimal" ),
498  wxT( "suppress leading zeros" ),
499  wxT( "suppress trailing zeros" ),
500  wxT( "keep zeros" )
501  };
502 
503  msg << zero_fmt[m_zeroFormat];
504  msg << wxT( "}\n" );
505  fputs( TO_UTF8( msg ), m_file );
506  fputs( "FMAT,2\n", m_file ); // Use Format 2 commands (version used since 1979)
507  }
508 
509  fputs( m_unitsDecimal ? "METRIC" : "INCH", m_file );
510 
511  switch( m_zeroFormat )
512  {
513  case SUPPRESS_LEADING:
514  case DECIMAL_FORMAT:
515  fputs( ",TZ\n", m_file );
516  break;
517 
518  case SUPPRESS_TRAILING:
519  fputs( ",LZ\n", m_file );
520  break;
521 
522  case KEEP_ZEROS:
523  fputs( ",TZ\n", m_file ); // TZ is acceptable when all zeros are kept
524  break;
525  }
526 }
527 
528 
530 {
531  //add if minimal here
532  fputs( "T0\nM30\n", m_file );
533  fclose( m_file );
534 }
535 
536 
537 
538 /* Helper function for sorting hole list.
539  * Compare function used for sorting holes type type (plated then not plated)
540  * then by increasing diameter value and X value
541  */
542 static bool CmpHoleSettings( const HOLE_INFO& a, const HOLE_INFO& b )
543 {
545  return b.m_Hole_NotPlated;
546 
547  if( a.m_Hole_Diameter != b.m_Hole_Diameter )
548  return a.m_Hole_Diameter < b.m_Hole_Diameter;
549 
550  if( a.m_Hole_Pos.x != b.m_Hole_Pos.x )
551  return a.m_Hole_Pos.x < b.m_Hole_Pos.x;
552 
553  return a.m_Hole_Pos.y < b.m_Hole_Pos.y;
554 }
555 
556 
558  bool aGenerateNPTH_list )
559 {
560  HOLE_INFO new_hole;
561 
562  m_holeListBuffer.clear();
563  m_toolListBuffer.clear();
564 
565  wxASSERT( aLayerPair.first < aLayerPair.second ); // fix the caller
566 
567  // build hole list for vias
568  if( ! aGenerateNPTH_list ) // vias are always plated !
569  {
570  for( VIA* via = GetFirstVia( m_pcb->m_Track ); via; via = GetFirstVia( via->Next() ) )
571  {
572  int hole_sz = via->GetDrillValue();
573 
574  if( hole_sz == 0 ) // Should not occur.
575  continue;
576 
577  new_hole.m_Tool_Reference = -1; // Flag value for Not initialized
578  new_hole.m_Hole_Orient = 0;
579  new_hole.m_Hole_Diameter = hole_sz;
580  new_hole.m_Hole_NotPlated = false;
581  new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;
582 
583  new_hole.m_Hole_Shape = 0; // hole shape: round
584  new_hole.m_Hole_Pos = via->GetStart();
585 
586  via->LayerPair( &new_hole.m_Hole_Top_Layer, &new_hole.m_Hole_Bottom_Layer );
587 
588  // LayerPair() returns params with m_Hole_Bottom_Layer > m_Hole_Top_Layer
589  // Remember: top layer = 0 and bottom layer = 31 for through hole vias
590  // Any captured via should be from aLayerPair.first to aLayerPair.second exactly.
591  if( new_hole.m_Hole_Top_Layer != aLayerPair.first ||
592  new_hole.m_Hole_Bottom_Layer != aLayerPair.second )
593  continue;
594 
595  m_holeListBuffer.push_back( new_hole );
596  }
597  }
598 
599  if( aLayerPair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
600  {
601  // add holes for thru hole pads
602  for( MODULE* module = m_pcb->m_Modules; module; module = module->Next() )
603  {
604  for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() )
605  {
606  if( !m_merge_PTH_NPTH )
607  {
608  if( !aGenerateNPTH_list && pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED )
609  continue;
610 
611  if( aGenerateNPTH_list && pad->GetAttribute() != PAD_ATTRIB_HOLE_NOT_PLATED )
612  continue;
613  }
614 
615  if( pad->GetDrillSize().x == 0 )
616  continue;
617 
618  new_hole.m_Hole_NotPlated = (pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED);
619  new_hole.m_Tool_Reference = -1; // Flag is: Not initialized
620  new_hole.m_Hole_Orient = pad->GetOrientation();
621  new_hole.m_Hole_Shape = 0; // hole shape: round
622  new_hole.m_Hole_Diameter = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y );
623  new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;
624 
625  if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE )
626  new_hole.m_Hole_Shape = 1; // oval flag set
627 
628  new_hole.m_Hole_Size = pad->GetDrillSize();
629  new_hole.m_Hole_Pos = pad->GetPosition(); // hole position
630  new_hole.m_Hole_Bottom_Layer = B_Cu;
631  new_hole.m_Hole_Top_Layer = F_Cu; // pad holes are through holes
632  m_holeListBuffer.push_back( new_hole );
633  }
634  }
635  }
636 
637  // Sort holes per increasing diameter value
638  sort( m_holeListBuffer.begin(), m_holeListBuffer.end(), CmpHoleSettings );
639 
640  // build the tool list
641  int last_hole = -1; // Set to not initialized (this is a value not used
642  // for m_holeListBuffer[ii].m_Hole_Diameter)
643  bool last_notplated_opt = false;
644 
645  DRILL_TOOL new_tool( 0, false );
646  unsigned jj;
647 
648  for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ )
649  {
650  if( m_holeListBuffer[ii].m_Hole_Diameter != last_hole ||
651  m_holeListBuffer[ii].m_Hole_NotPlated != last_notplated_opt )
652  {
653  new_tool.m_Diameter = m_holeListBuffer[ii].m_Hole_Diameter;
654  new_tool.m_Hole_NotPlated = m_holeListBuffer[ii].m_Hole_NotPlated;
655  m_toolListBuffer.push_back( new_tool );
656  last_hole = new_tool.m_Diameter;
657  last_notplated_opt = new_tool.m_Hole_NotPlated;
658  }
659 
660  jj = m_toolListBuffer.size();
661 
662  if( jj == 0 )
663  continue; // Should not occurs
664 
665  m_holeListBuffer[ii].m_Tool_Reference = jj; // Tool value Initialized (value >= 1)
666 
667  m_toolListBuffer.back().m_TotalCount++;
668 
669  if( m_holeListBuffer[ii].m_Hole_Shape )
670  m_toolListBuffer.back().m_OvalCount++;
671  }
672 }
673 
674 
675 std::vector<DRILL_LAYER_PAIR> EXCELLON_WRITER::getUniqueLayerPairs() const
676 {
677  wxASSERT( m_pcb );
678 
679  static const KICAD_T interesting_stuff_to_collect[] = {
680  PCB_VIA_T,
681  EOT
682  };
683 
684  PCB_TYPE_COLLECTOR vias;
685 
686  vias.Collect( m_pcb, interesting_stuff_to_collect );
687 
688  std::set< DRILL_LAYER_PAIR > unique;
689 
690  DRILL_LAYER_PAIR layer_pair;
691 
692  for( int i = 0; i < vias.GetCount(); ++i )
693  {
694  VIA* v = (VIA*) vias[i];
695 
696  v->LayerPair( &layer_pair.first, &layer_pair.second );
697 
698  // only make note of blind buried.
699  // thru hole is placed unconditionally as first in fetched list.
700  if( layer_pair != DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
701  {
702  unique.insert( layer_pair );
703  }
704  }
705 
706  std::vector<DRILL_LAYER_PAIR> ret;
707 
708  ret.push_back( DRILL_LAYER_PAIR( F_Cu, B_Cu ) ); // always first in returned list
709 
710  for( std::set< DRILL_LAYER_PAIR >::const_iterator it = unique.begin(); it != unique.end(); ++it )
711  ret.push_back( *it );
712 
713  return ret;
714 }
715 
716 
717 const std::string EXCELLON_WRITER::layerName( LAYER_ID aLayer ) const
718 {
719  // Generic names here.
720  switch( aLayer )
721  {
722  case F_Cu:
723  return "front";
724  case B_Cu:
725  return "back";
726  default:
727  return StrPrintf( "inner%d", aLayer );
728  }
729 }
730 
731 
732 const std::string EXCELLON_WRITER::layerPairName( DRILL_LAYER_PAIR aPair ) const
733 {
734  std::string ret = layerName( aPair.first );
735  ret += '-';
736  ret += layerName( aPair.second );
737 
738  return ret;
739 }
740 
741 
742 const wxString EXCELLON_WRITER::drillFileName( DRILL_LAYER_PAIR aPair, bool aNPTH,
743  bool aMerge_PTH_NPTH ) const
744 {
745  wxASSERT( m_pcb );
746 
747  wxString extend;
748 
749  if( aNPTH )
750  extend = "-NPTH";
751  else if( aPair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
752  {
753  if( !aMerge_PTH_NPTH )
754  extend = "-PTH";
755  // if merged, extend with nothing
756  }
757  else
758  {
759  extend += '-';
760  extend += layerPairName( aPair );
761  }
762 
763  wxFileName fn = m_pcb->GetFileName();
764 
765  fn.SetName( fn.GetName() + extend );
766  fn.SetExt( DrillFileExtension );
767 
768  wxString ret = fn.GetFullName(); // show me in debugger
769 
770  return ret;
771 }
static bool CmpHoleSettings(const HOLE_INFO &a, const HOLE_INFO &b)
int GetCount() const
Function GetCount returns the number of objects in the list.
std::vector< DRILL_TOOL > m_toolListBuffer
const wxString DrillFileExtension
static int KiROUND(double v)
KiROUND rounds a floating point number to an int using "round halfway cases away from zero"...
Definition: common.h:107
like PAD_STANDARD, but not plated mechanical use only, no connection allowed
Definition: pad_shapes.h:63
Class LOCALE_IO is a class that can be instantiated within a scope in which you are expecting excepti...
Definition: common.h:174
Class BOARD to handle a board.
wxString GetDefaultPlotExtension(PlotFormat aFormat)
Returns the default plot extension for a format.
MODULE * Next() const
Definition: class_module.h:99
const wxString drillFileName(DRILL_LAYER_PAIR aPair, bool aNPTH, bool aMerge_PTH_NPTH) const
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:317
void buildHolesList(DRILL_LAYER_PAIR aLayerPair, bool aGenerateNPTH_list)
Function BuildHolesList Create the list of holes and tools for a given board The list is sorted by in...
Class REPORTER is a pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:61
std::pair< LAYER_ID, LAYER_ID > DRILL_LAYER_PAIR
static const int delta[8][2]
Definition: solve.cpp:112
const std::string layerName(LAYER_ID aLayer) const
search types array terminator (End Of Types)
Definition: typeinfo.h:94
KICAD_T
Enum KICAD_T is the set of class identification values, stored in EDA_ITEM::m_StructType.
Definition: typeinfo.h:90
Functions relatives to tracks, vias and segments used to fill zones.
This file contains miscellaneous commonly used macros and functions.
void SetFormat(bool aMetric, ZEROS_FMT aZerosFmt=DECIMAL_FORMAT, int aLeftDigits=0, int aRightDigits=0)
Function SetFormat Initialize internal parameters to match the given format.
Board plot function definition file.
int StrPrintf(std::string *result, const char *format,...)
Function StrPrintf is like sprintf() but the output is appended to a std::string instead of to a char...
Definition: richio.cpp:75
LAYER_ID m_Hole_Bottom_Layer
#define TO_UTF8(wxstring)
Macro TO_UTF8 converts a wxString to a UTF8 encoded C string for all wxWidgets build modes...
Definition: macros.h:47
int createDrillFile(FILE *aFile)
Function CreateDrillFile Creates an Excellon drill file.
void writeCoordinates(char *aLine, double aCoordX, double aCoordY)
wxString GetBuildVersion()
Function GetBuildVersion Return the build version string.
const wxString & GetFileName() const
Definition: class_board.h:237
D_PAD * Next() const
Definition: class_pad.h:106
The common library.
Common plot library Plot settings, and plotting engines (Postscript, Gerber, HPGL and DXF) ...
const PAGE_INFO * m_pageInfo
void Collect(BOARD_ITEM *aBoard, const KICAD_T aScanList[])
Function Collect scans a BOARD_ITEM using this class's Inspector method, which does the collection...
Definition: collectors.cpp:488
void CreateDrillandMapFilesSet(const wxString &aPlotDirectory, bool aGenDrill, bool aGenMap, REPORTER *aReporter=NULL)
Function CreateDrillandMapFilesSet Creates the full set of Excellon drill file for the board filename...
DRILL_PRECISION m_precision
void LayerPair(LAYER_ID *top_layer, LAYER_ID *bottom_layer) const
Function LayerPair Return the 2 layers used by the via (the via actually uses all layers between thes...
bool GenDrillMapFile(const wxString &aFullFileName, PlotFormat aFormat)
Function GenDrillMapFile Plot a map of drill marks for holes.
static const wxChar * GetChars(const wxString &s)
Function GetChars returns a wxChar* to the actual wxChar* data within a wxString, and is helpful for ...
Definition: macros.h:92
see class PGM_BASE
static LIB_PART * dummy()
Used when a LIB_PART is not found in library to draw a dummy shape This component is a 400 mils squar...
Class BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:166
std::vector< HOLE_INFO > m_holeListBuffer
LAYER_ID
Enum LAYER_ID is the set of PCB layers.
DLIST< MODULE > m_Modules
Definition: class_board.h:243
std::vector< DRILL_LAYER_PAIR > getUniqueLayerPairs() const
Get unique layer pairs by examining the micro and blind_buried vias.
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_UNDEFINED)=0
Function Report is a pure virtual function to override in the derived object.
Classes used in drill files, map files and report files generation.
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:108
DLIST< TRACK > m_Track
Definition: class_board.h:244
Module description (excepted pads)
Class PCB_TYPE_COLLECTOR merely gathers up all BOARD_ITEMs of a given set of KICAD_T type(s)...
Definition: collectors.h:593
const std::string layerPairName(DRILL_LAYER_PAIR aPair) const
wxString DateAndTime()
Function DateAndTime.
Definition: string.cpp:229
#define min(a, b)
Definition: auxiliary.h:85
VIA * GetFirstVia(TRACK *aTrk, const TRACK *aStopPoint=NULL)
Scan a track list for the first VIA o NULL if not found (or NULL passed)
Definition: class_track.h:493