KiCad PCB EDA Suite
Loading...
Searching...
No Matches
gendrill_writer_base.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) 2017 Jean_Pierre Charras <jp.charras at wanadoo.fr>
5 * Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
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 2
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, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <board.h>
24#include <footprint.h>
25#include <pad.h>
26#include <pcb_track.h>
27#include <collectors.h>
28#include <macros.h>
29#include <reporter.h>
30#include <string_utils.h>
34#include <pcbplot.h>
35#include <pcb_painter.h>
36#include <pcb_shape.h>
37#include <fmt.h>
38#include <wx/ffile.h>
39#include <reporter.h>
40
41#include <set>
42
46
47
48/* Helper function for sorting hole list.
49 * Compare function used for sorting holes type type:
50 * plated then not plated
51 * then by increasing diameter value
52 * then by attribute type (vias, pad, mechanical)
53 * then by X then Y position
54 */
55static bool cmpHoleSorting( const HOLE_INFO& a, const HOLE_INFO& b )
56{
58 return b.m_Hole_NotPlated;
59
62
63 // At this point (same diameter, same plated type), group by attribute
64 // type (via, pad, mechanical, although currently only not plated pads are mechanical)
67
68 // At this point (same diameter, same type), sort by X then Y position.
69 // This is optimal for drilling and make the file reproducible as long as holes
70 // have not changed, even if the data order has changed.
71 if( a.m_Hole_Pos.x != b.m_Hole_Pos.x )
72 return a.m_Hole_Pos.x < b.m_Hole_Pos.x;
73
74 return a.m_Hole_Pos.y < b.m_Hole_Pos.y;
75}
76
77
78void GENDRILL_WRITER_BASE::buildHolesList( const DRILL_SPAN& aSpan, bool aGenerateNPTH_list )
79{
80 m_holeListBuffer.clear();
81 m_toolListBuffer.clear();
82
83 DRILL_QUERY query;
84 query.m_Span = aSpan;
85 query.m_NonPlatedOnly = aGenerateNPTH_list;
87
89
90 // Sort holes per increasing diameter value (and for each dimater, by position)
91 sort( m_holeListBuffer.begin(), m_holeListBuffer.end(), cmpHoleSorting );
92
93 // build the tool list
94 int last_hole = -1; // Set to not initialized (this is a value not used
95 // for m_holeListBuffer[ii].m_Hole_Diameter)
96 bool last_notplated_opt = false;
98
99 DRILL_TOOL new_tool( 0, false );
100 unsigned jj;
101
102 for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ )
103 {
104 if( m_holeListBuffer[ii].m_Hole_Diameter != last_hole
105 || m_holeListBuffer[ii].m_Hole_NotPlated != last_notplated_opt
107 || m_holeListBuffer[ii].m_HoleAttribute != last_attribute
108#endif
109 )
110 {
111 new_tool.m_Diameter = m_holeListBuffer[ii].m_Hole_Diameter;
112 new_tool.m_Hole_NotPlated = m_holeListBuffer[ii].m_Hole_NotPlated;
113 new_tool.m_HoleAttribute = m_holeListBuffer[ii].m_HoleAttribute;
114 m_toolListBuffer.push_back( new_tool );
115 last_hole = new_tool.m_Diameter;
116 last_notplated_opt = new_tool.m_Hole_NotPlated;
117 last_attribute = new_tool.m_HoleAttribute;
118 }
119
120 jj = m_toolListBuffer.size();
121
122 if( jj == 0 )
123 continue; // Should not occurs
124
125 m_holeListBuffer[ii].m_Tool_Reference = jj; // Tool value Initialized (value >= 1)
126
127 m_toolListBuffer.back().m_TotalCount++;
128
129 if( m_holeListBuffer[ii].m_Hole_Shape )
130 m_toolListBuffer.back().m_OvalCount++;
131
132 if( m_holeListBuffer[ii].m_IsBackdrill )
133 {
134 m_toolListBuffer.back().m_IsBackdrill = true;
135
136 if( m_holeListBuffer[ii].m_StubLength.has_value() )
137 {
138 int stub = *m_holeListBuffer[ii].m_StubLength;
139
140 if( !m_toolListBuffer.back().m_MinStubLength.has_value()
141 || stub < *m_toolListBuffer.back().m_MinStubLength )
142 {
143 m_toolListBuffer.back().m_MinStubLength = stub;
144 }
145
146 if( !m_toolListBuffer.back().m_MaxStubLength.has_value()
147 || stub > *m_toolListBuffer.back().m_MaxStubLength )
148 {
149 m_toolListBuffer.back().m_MaxStubLength = stub;
150 }
151 }
152 }
153
158 || m_holeListBuffer[ii].m_IsBackdrill )
159 m_toolListBuffer.back().m_HasPostMachining = true;
160 }
161}
162
163
164std::vector<DRILL_SPAN> GENDRILL_WRITER_BASE::getUniqueLayerPairs() const
165{
166 wxASSERT( m_pcb );
167
169
170 vias.Collect( m_pcb, { PCB_VIA_T } );
171
172 std::set<DRILL_SPAN> unique;
173
174 for( int i = 0; i < vias.GetCount(); ++i )
175 {
176 PCB_VIA* via = static_cast<PCB_VIA*>( vias[i] );
177 PCB_LAYER_ID top_layer;
178 PCB_LAYER_ID bottom_layer;
179
180 via->LayerPair( &top_layer, &bottom_layer );
181
182 if( DRILL_LAYER_PAIR( top_layer, bottom_layer ) != DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
183 unique.emplace( top_layer, bottom_layer, false, false );
184
185 auto addBackdrillSpan = [&]( const PADSTACK::DRILL_PROPS& aDrill )
186 {
187 if( aDrill.start == UNDEFINED_LAYER || aDrill.end == UNDEFINED_LAYER )
188 return;
189
190 if( aDrill.size.x <= 0 && aDrill.size.y <= 0 )
191 return;
192
193 unique.emplace( aDrill.start, aDrill.end, true, false );
194 };
195
196 addBackdrillSpan( via->Padstack().SecondaryDrill() );
197 addBackdrillSpan( via->Padstack().TertiaryDrill() );
198 }
199
200 std::vector<DRILL_SPAN> ret;
201
202 ret.emplace_back( F_Cu, B_Cu, false, false );
203
204 for( const DRILL_SPAN& span : unique )
205 {
206 if( span.m_IsBackdrill || span.Pair() != DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
207 ret.push_back( span );
208 }
209
210 return ret;
211}
212
213
214const std::string GENDRILL_WRITER_BASE::layerName( PCB_LAYER_ID aLayer ) const
215{
216 // Generic names here.
217 switch( aLayer )
218 {
219 case F_Cu:
220 return "front";
221 case B_Cu:
222 return "back";
223 default:
224 {
225 // aLayer use even values, and the first internal layer (In1) is B_Cu + 2.
226 int ly_id = ( aLayer - B_Cu ) / 2;
227 return fmt::format( "in{}", ly_id );
228 }
229 }
230}
231
232
234{
235 std::string ret = layerName( aPair.first );
236 ret += '-';
237 ret += layerName( aPair.second );
238
239 return ret;
240}
241
242
243const wxString GENDRILL_WRITER_BASE::getDrillFileName( const DRILL_SPAN& aSpan, bool aNPTH,
244 bool aMerge_PTH_NPTH ) const
245{
246 wxASSERT( m_pcb );
247
248 wxString extend;
249
250 auto layerIndex = [&]( PCB_LAYER_ID aLayer )
251 {
252 int conventional_layer_num = 1;
253
254 for( PCB_LAYER_ID layer : LSET::AllCuMask( m_pcb->GetCopperLayerCount() ).UIOrder() )
255 {
256 if( layer == aLayer )
257 return conventional_layer_num;
258
259 conventional_layer_num++;
260 }
261
262 return conventional_layer_num;
263 };
264
265 if( aSpan.m_IsBackdrill )
266 {
267 extend.Printf( wxT( "_Backdrills_Drill_%d_%d" ),
268 layerIndex( aSpan.DrillStartLayer() ),
269 layerIndex( aSpan.DrillEndLayer() ) );
270 }
271 else if( aNPTH )
272 {
273 extend = wxT( "-NPTH" );
274 }
275 else if( aSpan.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
276 {
277 if( !aMerge_PTH_NPTH )
278 extend = wxT( "-PTH" );
279 // if merged, extend with nothing
280 }
281 else
282 {
283 extend += '-';
284 extend += layerPairName( aSpan.Pair() );
285 }
286
287 wxFileName fn = m_pcb->GetFileName();
288
289 fn.SetName( fn.GetName() + extend );
290 fn.SetExt( m_drillFileExtension );
291
292 wxString ret = fn.GetFullName();
293
294 return ret;
295}
296
297
299 IPC4761_FEATURES aFeature ) const
300{
301 wxASSERT( m_pcb );
302
303 wxString extend;
304
305 switch( aFeature )
306 {
308 extend << wxT( "-filling-" );
309 extend << layerPairName( aSpan.Pair() );
310 break;
312 extend << wxT( "-capping-" );
313 extend << layerPairName( aSpan.Pair() );
314 break;
316 extend << wxT( "-covering-" );
317 extend << layerName( aSpan.Pair().second );
318 break;
320 extend << wxT( "-covering-" );
321 extend << layerName( aSpan.Pair().first );
322 break;
324 extend << wxT( "-plugging-" );
325 extend << layerName( aSpan.Pair().second );
326 break;
328 extend << wxT( "-plugging-" );
329 extend << layerName( aSpan.Pair().first );
330 break;
332 extend << wxT( "-tenting-" );
333 extend << layerName( aSpan.Pair().second );
334 break;
336 extend << wxT( "-tenting-" );
337 extend << layerName( aSpan.Pair().first );
338 break;
339 }
340
341 wxFileName fn = m_pcb->GetFileName();
342
343 fn.SetName( fn.GetName() + extend );
344 fn.SetExt( m_drillFileExtension );
345
346 wxString ret = fn.GetFullName();
347
348 return ret;
349}
350
351
352bool GENDRILL_WRITER_BASE::CreateMapFilesSet( const wxString& aPlotDirectory, REPORTER * aReporter )
353{
354 wxFileName fn;
355 wxString msg;
356
357 std::vector<DRILL_SPAN> hole_sets = getUniqueLayerPairs();
358
359 if( !m_merge_PTH_NPTH )
360 hole_sets.emplace_back( F_Cu, B_Cu, false, true );
361
362 for( std::vector<DRILL_SPAN>::const_iterator it = hole_sets.begin(); it != hole_sets.end(); ++it )
363 {
364 const DRILL_SPAN& span = *it;
365 bool doing_npth = span.m_IsNonPlatedFile;
366
367 buildHolesList( span, doing_npth );
368
369 if( getHolesCount() > 0 || doing_npth || span.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
370 {
372 fn.SetPath( aPlotDirectory );
373
374 fn.SetExt( wxEmptyString ); // Will be added by GenDrillMap
375 wxString fullfilename = fn.GetFullPath() + wxT( "-drl_map" );
376 fullfilename << wxT(".") << GetDefaultPlotExtension( m_mapFileFmt );
377
378 bool success = genDrillMapFile( fullfilename, m_mapFileFmt );
379
380 if( ! success )
381 {
382 if( aReporter )
383 {
384 msg.Printf( _( "Failed to create file '%s'." ), fullfilename );
385 aReporter->Report( msg, RPT_SEVERITY_ERROR );
386 }
387
388 return false;
389 }
390 else
391 {
392 if( aReporter )
393 {
394 msg.Printf( _( "Created file '%s'." ), fullfilename );
395 aReporter->Report( msg, RPT_SEVERITY_ACTION );
396 }
397
398 AddCreatedFile( fullfilename );
399 }
400 }
401 }
402
403 return true;
404}
405
406
408 TYPE_FILE aHoleType,
409 bool aCompatNCdrill ) const
410{
411// Build a wxString containing the .FileFunction attribute for drill files.
412// %TF.FileFunction,Plated[NonPlated],layer1num,layer2num,PTH[NPTH][Blind][Buried],Drill[Route][Mixed]*%
413 wxString text;
414
415 if( aCompatNCdrill )
416 text = wxT( "; #@! " );
417 else
418 text = wxT( "%" );
419
420 text << wxT( "TF.FileFunction," );
421
422 if( aSpan.m_IsBackdrill || aHoleType == NPTH_FILE )
423 text << wxT( "NonPlated," );
424 else if( aHoleType == MIXED_FILE ) // only for Excellon format
425 text << wxT( "MixedPlating," );
426 else
427 text << wxT( "Plated," );
428
429 int layer1 = aSpan.Pair().first;
430 int layer2 = aSpan.Pair().second;
431
432 // In Gerber files, layers num are 1 to copper layer count instead of F_Cu to B_Cu
433 // (0 to copper layer count-1)
434 // Note also for a n copper layers board, gerber layers num are 1 ... n
435 //
436 // Copper layers use even values, so the layer id in file is
437 // (Copper layer id) /2 + 1 if layer is not B_Cu
438 if( layer1 == F_Cu )
439 layer1 = 1;
440 else if( layer1 == B_Cu )
441 layer1 = m_pcb->GetCopperLayerCount();
442 else
443 layer1 = ( ( layer1 - B_Cu ) / 2 ) + 1;
444
445 if( layer2 == F_Cu )
446 layer2 = 1;
447 else if( layer2 == B_Cu )
448 layer2 = m_pcb->GetCopperLayerCount();
449 else
450 layer2 = ( ( layer2 - B_Cu ) / 2) + 1;
451
452 // Ensure layer order is from top (smaller layer number) to bottom (bigger layer number)
453 if( layer1 > layer2 )
454 std::swap( layer1, layer2 );
455
456 text << layer1 << wxT( "," ) << layer2;
457
458 // Now add PTH or NPTH or Blind or Buried attribute
459 int toplayer = 1;
460 int bottomlayer = m_pcb->GetCopperLayerCount();
461
462 if( aSpan.m_IsBackdrill )
463 text << wxT( ",Blind" );
464 else if( aHoleType == NPTH_FILE )
465 text << wxT( ",NPTH" );
466 else if( aHoleType == MIXED_FILE ) // only for Excellon format
467 ; // write nothing
468 else if( layer1 == toplayer && layer2 == bottomlayer )
469 text << wxT( ",PTH" );
470 else if( layer1 == toplayer || layer2 == bottomlayer )
471 text << wxT( ",Blind" );
472 else
473 text << wxT( ",Buried" );
474
475 // In NC drill file, these previous parameters should be enough:
476 if( aCompatNCdrill )
477 return text;
478
479
480 // Now add Drill or Route or Mixed:
481 // file containing only round holes have Drill attribute
482 // file containing only oblong holes have Routed attribute
483 // file containing both holes have Mixed attribute
484 bool hasOblong = false;
485 bool hasDrill = false;
486
487 for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ )
488 {
489 const HOLE_INFO& hole_descr = m_holeListBuffer[ii];
490
491 if( hole_descr.m_Hole_Shape ) // m_Hole_Shape not 0 is an oblong hole)
492 hasOblong = true;
493 else
494 hasDrill = true;
495 }
496
497 if( hasOblong && hasDrill )
498 text << wxT( ",Mixed" );
499 else if( hasDrill )
500 text << wxT( ",Drill" );
501 else if( hasOblong )
502 text << wxT( ",Rout" );
503
504 // else: empty file.
505
506 // End of .FileFunction attribute:
507 text << wxT( "*%" );
508
509 return text;
510}
511
512
513/* Conversion utilities - these will be used often in there... */
514inline double diameter_in_inches( double ius )
515{
516 return ius * 0.001 / pcbIUScale.IU_PER_MILS;
517}
518
519
520inline double diameter_in_mm( double ius )
521{
522 return ius / pcbIUScale.IU_PER_MM;
523}
524
525
526// return a pen size to plot markers and having a readable shape
527// clamped to be >= MIN_SIZE_MM to avoid too small line width
528static int getMarkerBestPenSize( int aMarkerDiameter )
529{
530 int bestsize = aMarkerDiameter / 10;
531
532 const double MIN_SIZE_MM = 0.1;
533 bestsize = std::max( bestsize, pcbIUScale.mmToIU( MIN_SIZE_MM ) );
534
535 return bestsize;
536}
537
538
539// return a pen size to plot outlines for oval holes
541{
542 const double SKETCH_LINE_WIDTH_MM = 0.1;
543 return pcbIUScale.mmToIU( SKETCH_LINE_WIDTH_MM );
544}
545
546
547// return a default pen size to plot items with no specific line thickness
549{
550 const double DEFAULT_LINE_WIDTH_MM = 0.2;
551 return pcbIUScale.mmToIU( DEFAULT_LINE_WIDTH_MM );
552}
553
554
555bool GENDRILL_WRITER_BASE::genDrillMapFile( const wxString& aFullFileName, PLOT_FORMAT aFormat )
556{
557 // Remark:
558 // Hole list must be created before calling this function, by buildHolesList(),
559 // for the right holes set (PTH, NPTH, buried/blind vias ...)
560
561 double scale = 1.0;
562 VECTOR2I offset = GetOffset();
563 PLOTTER* plotter = nullptr;
565 int bottom_limit = 0; // Y coord limit of page. 0 mean do not use
566
567 PCB_PLOT_PARAMS plot_opts; // starts plotting with default options
568
569 const PAGE_INFO& page_info = m_pageInfo ? *m_pageInfo : dummy;
570
571 // Calculate dimensions and center of PCB. The Edge_Cuts layer must be visible
572 // to calculate the board edges bounding box
573 LSET visibleLayers = m_pcb->GetVisibleLayers();
574 m_pcb->SetVisibleLayers( visibleLayers | LSET( { Edge_Cuts } ) );
575 BOX2I bbbox = m_pcb->GetBoardEdgesBoundingBox();
576 m_pcb->SetVisibleLayers( visibleLayers );
577
578 // Some formats cannot be used to generate a document like the map files
579 // Currently HPGL (old format not very used)
580
581 if( aFormat == PLOT_FORMAT::HPGL )
582 aFormat = PLOT_FORMAT::PDF;
583
584 // Calculate the scale for the format type, scale 1 in HPGL, drawing on
585 // an A4 sheet in PS, + text description of symbols
586 switch( aFormat )
587 {
589 plotter = new GERBER_PLOTTER();
590 plotter->SetViewport( offset, pcbIUScale.IU_PER_MILS / 10, scale, false );
591 plotter->SetGerberCoordinatesFormat( 5 ); // format x.5 unit = mm
592 break;
593
594 default: wxASSERT( false ); KI_FALLTHROUGH;
595
596 case PLOT_FORMAT::PDF:
598 case PLOT_FORMAT::SVG:
599 {
600 VECTOR2I pageSizeIU = page_info.GetSizeIU( pcbIUScale.IU_PER_MILS );
601
602 // Reserve a 10 mm margin around the page.
603 int margin = pcbIUScale.mmToIU( 10 );
604
605 // Calculate a scaling factor to print the board on the sheet
606 double Xscale = double( pageSizeIU.x - ( 2 * margin ) ) / bbbox.GetWidth();
607
608 // We should print the list of drill sizes, so reserve room for it
609 // 60% height for board 40% height for list
610 int ypagesize_for_board = KiROUND( pageSizeIU.y * 0.6 );
611 double Yscale = double( ypagesize_for_board - margin ) / bbbox.GetHeight();
612
613 scale = std::min( Xscale, Yscale );
614
615 // Experience shows the scale should not to large, because texts
616 // create problem (can be to big or too small).
617 // So the scale is clipped at 3.0;
618 scale = std::min( scale, 3.0 );
619
620 offset.x = KiROUND( double( bbbox.Centre().x ) - ( pageSizeIU.x / 2.0 ) / scale );
621 offset.y = KiROUND( double( bbbox.Centre().y ) - ( ypagesize_for_board / 2.0 ) / scale );
622
623 // bottom_limit is used to plot the legend (drill diameters)
624 // texts are scaled differently for scale > 1.0 and <= 1.0
625 // so the limit is scaled differently.
626 bottom_limit = ( pageSizeIU.y - margin ) / std::min( scale, 1.0 );
627
628 if( aFormat == PLOT_FORMAT::SVG )
629 plotter = new SVG_PLOTTER;
630 else if( aFormat == PLOT_FORMAT::PDF )
631 plotter = new PDF_PLOTTER;
632 else
633 plotter = new PS_PLOTTER;
634
635 plotter->SetPageSettings( page_info );
636 plotter->SetViewport( offset, pcbIUScale.IU_PER_MILS / 10, scale, false );
637 break;
638 }
639
640 case PLOT_FORMAT::DXF:
641 {
642 DXF_PLOTTER* dxf_plotter = new DXF_PLOTTER;
643
645
646 plotter = dxf_plotter;
647 plotter->SetPageSettings( page_info );
648 plotter->SetViewport( offset, pcbIUScale.IU_PER_MILS / 10, scale, false );
649 break;
650 }
651 }
652
653 plotter->SetCreator( wxT( "PCBNEW" ) );
654 plotter->SetColorMode( false );
655
656 KIGFX::PCB_RENDER_SETTINGS renderSettings;
657 renderSettings.SetDefaultPenWidth( getDefaultPenSize() );
658
659 plotter->SetRenderSettings( &renderSettings );
660
661 if( !plotter->OpenFile( aFullFileName ) )
662 {
663 delete plotter;
664 return false;
665 }
666
667 plotter->ClearHeaderLinesList();
668
669 // For the Gerber X2 format we need to set the "FileFunction" to Drillmap
670 // and set a few other options.
671 if( plotter->GetPlotterType() == PLOT_FORMAT::GERBER )
672 {
673 GERBER_PLOTTER* gbrplotter = static_cast<GERBER_PLOTTER*>( plotter );
674 gbrplotter->DisableApertMacros( false );
675 gbrplotter->UseX2format( true ); // Mandatory
676 gbrplotter->UseX2NetAttributes( false ); // net attributes have no meaning here
677
678 // Attributes are added using X2 format
679 AddGerberX2Header( gbrplotter, m_pcb, false );
680
681 wxString text;
682
683 // Add the TF.FileFunction
684 text = "%TF.FileFunction,Drillmap*%";
685 gbrplotter->AddLineToHeader( text );
686
687 // Add the TF.FilePolarity
688 text = wxT( "%TF.FilePolarity,Positive*%" );
689 gbrplotter->AddLineToHeader( text );
690 }
691
692 plotter->StartPlot( wxT( "1" ) );
693
694 // Draw items on edge layer.
695 // Not all, only items useful for drill map, i.e. board outlines.
696 BRDITEMS_PLOTTER itemplotter( plotter, m_pcb, plot_opts );
697
698 // Use attributes of a drawing layer (we are not really draw the Edge.Cuts layer)
699 itemplotter.SetLayerSet( { Dwgs_User } );
700
701 for( BOARD_ITEM* item : m_pcb->Drawings() )
702 {
703 if( item->GetLayer() != Edge_Cuts )
704 continue;
705
706 switch( item->Type() )
707 {
708 case PCB_SHAPE_T:
709 {
710 PCB_SHAPE dummy_shape( *static_cast<PCB_SHAPE*>( item ) );
711 dummy_shape.SetLayer( Dwgs_User );
712 dummy_shape.SetParentGroup( nullptr ); // Remove group association, not needed for plotting
713 itemplotter.PlotShape( &dummy_shape );
714 }
715 break;
716
717 default: break;
718 }
719 }
720
721 // Plot edge cuts in footprints
722 for( const FOOTPRINT* footprint : m_pcb->Footprints() )
723 {
724 for( BOARD_ITEM* item : footprint->GraphicalItems() )
725 {
726 if( item->GetLayer() != Edge_Cuts )
727 continue;
728
729 switch( item->Type() )
730 {
731 case PCB_SHAPE_T:
732 {
733 PCB_SHAPE dummy_shape( *static_cast<PCB_SHAPE*>( item ) );
734 dummy_shape.SetLayer( Dwgs_User );
735 dummy_shape.SetParentGroup( nullptr ); // Remove group association, not needed for plotting
736 itemplotter.PlotShape( &dummy_shape );
737 }
738 break;
739
740 default: break;
741 }
742 }
743 }
744
745 int plotX, plotY, TextWidth;
746 int intervalle = 0;
747 char line[1024];
748 wxString msg;
749 int textmarginaftersymbol = pcbIUScale.mmToIU( 2 );
750
751 // Set Drill Symbols width
752 plotter->SetCurrentLineWidth( -1 );
753
754 // Plot board outlines and drill map
755 plotDrillMarks( plotter );
756
757 // Print a list of symbols used.
758 int charSize = pcbIUScale.mmToIU( 2 ); // text size in IUs
759
760 // real char scale will be 1/scale, because the global plot scale is scale
761 // for scale < 1.0 ( plot bigger actual size)
762 // Therefore charScale = 1.0 / scale keep the initial charSize
763 // (for scale < 1 we use the global scaling factor: the board must be plotted
764 // smaller than the actual size)
765 double charScale = std::min( 1.0, 1.0 / scale );
766
767 TextWidth = KiROUND( ( charSize * charScale ) / 10.0 ); // Set text width (thickness)
768 intervalle = KiROUND( charSize * charScale ) + TextWidth;
769
770 // Trace information.
771 plotX = KiROUND( bbbox.GetX() + textmarginaftersymbol * charScale );
772 plotY = bbbox.GetBottom() + intervalle;
773
774 // Plot title "Info"
775 wxString Text = wxT( "Drill Map:" );
776
777 TEXT_ATTRIBUTES attrs;
778 attrs.m_StrokeWidth = TextWidth;
780 attrs.m_Size = KiROUND( charSize * charScale, charSize * charScale );
783 attrs.m_Multiline = false;
784
785 plotter->PlotText( VECTOR2I( plotX, plotY ), COLOR4D::UNSPECIFIED, Text, attrs, nullptr /* stroke font */,
787
788 // For some formats (PS, PDF SVG) we plot the drill size list on more than one column
789 // because the list must be contained inside the printed page
790 // (others formats do not have a defined page size)
791 int max_line_len = 0; // The max line len in iu of the currently plotted column
792
793 for( unsigned ii = 0; ii < m_toolListBuffer.size(); ii++ )
794 {
795 DRILL_TOOL& tool = m_toolListBuffer[ii];
796
797 if( tool.m_TotalCount == 0 )
798 continue;
799
800 plotY += intervalle;
801
802 // Ensure there are room to plot the line
803 if( bottom_limit && ( plotY + intervalle > bottom_limit ) )
804 {
805 plotY = bbbox.GetBottom() + intervalle;
806 plotX += max_line_len + pcbIUScale.mmToIU( 10 ); //column_width;
807 max_line_len = 0;
808 }
809
810 int plot_diam = KiROUND( tool.m_Diameter );
811
812 // For markers plotted with the comment, keep marker size <= text height
813 plot_diam = std::min( plot_diam, KiROUND( charSize * charScale ) );
814 int x = KiROUND( plotX - textmarginaftersymbol * charScale - plot_diam / 2.0 );
815 int y = KiROUND( plotY + charSize * charScale );
816
817 plotter->SetCurrentLineWidth( getMarkerBestPenSize( plot_diam ) );
818 plotter->Marker( VECTOR2I( x, y ), plot_diam, ii );
819 plotter->SetCurrentLineWidth( -1 );
820
821 // List the diameter of each drill in mm and inches.
822 snprintf( line, sizeof( line ), "%3.3fmm / %2.4f\" ", diameter_in_mm( tool.m_Diameter ),
824
825 msg = From_UTF8( line );
826 wxString extraInfo;
827
829 extraInfo += wxT( ", castellated" );
831 extraInfo += wxT( ", press-fit" );
832
833 if( tool.m_IsBackdrill )
834 {
835 if( tool.m_MinStubLength.has_value() )
836 {
837 double minStub = pcbIUScale.IUTomm( *tool.m_MinStubLength );
838
839 if( tool.m_MaxStubLength.has_value() && tool.m_MaxStubLength != tool.m_MinStubLength )
840 {
841 double maxStub = pcbIUScale.IUTomm( *tool.m_MaxStubLength );
842 extraInfo += wxString::Format( wxT( ", backdrill stub %.3f-%.3fmm" ), minStub, maxStub );
843 }
844 else
845 {
846 extraInfo += wxString::Format( wxT( ", backdrill stub %.3fmm" ), minStub );
847 }
848 }
849 else
850 {
851 extraInfo += wxT( ", backdrill" );
852 }
853 }
854
855 if( tool.m_HasPostMachining )
856 extraInfo += wxT( ", post-machined" );
857
858 wxString counts;
859
860 if( ( tool.m_TotalCount == 1 ) && ( tool.m_OvalCount == 0 ) )
861 counts.Printf( wxT( "(1 hole%s)" ), extraInfo );
862 else if( tool.m_TotalCount == 1 )
863 counts.Printf( wxT( "(1 slot%s)" ), extraInfo );
864 else if( tool.m_OvalCount == 0 )
865 counts.Printf( wxT( "(%d holes%s)" ), tool.m_TotalCount, extraInfo );
866 else if( tool.m_OvalCount == 1 )
867 counts.Printf( wxT( "(%d holes + 1 slot%s)" ), tool.m_TotalCount - 1, extraInfo );
868 else
869 counts.Printf( wxT( "(%d holes + %d slots%s)" ), tool.m_TotalCount - tool.m_OvalCount, tool.m_OvalCount,
870 extraInfo );
871
872 msg += counts;
873
874 // Backdrills carry the non-plated flag but are already called out as backdrills above
875 if( tool.m_Hole_NotPlated && !tool.m_IsBackdrill )
876 msg += wxT( " (not plated)" );
877
878 plotter->PlotText( VECTOR2I( plotX, y ), COLOR4D::UNSPECIFIED, msg, attrs, nullptr /* stroke font */,
880
881 intervalle = KiROUND( ( ( charSize * charScale ) + TextWidth ) * 1.2 );
882
883 if( intervalle < ( plot_diam + ( 1 * pcbIUScale.IU_PER_MM / scale ) + TextWidth ) )
884 intervalle = plot_diam + ( 1 * pcbIUScale.IU_PER_MM / scale ) + TextWidth;
885
886 // Evaluate the text horizontal size, to know the maximal column size
887 // This is a rough value, but ok to create a new column to plot next texts
888 int text_len = msg.Len() * ( ( charSize * charScale ) + TextWidth );
889 max_line_len = std::max( max_line_len, text_len + plot_diam );
890 }
891
892 plotter->EndPlot();
893 delete plotter;
894
895 return true;
896}
897
898
899bool GENDRILL_WRITER_BASE::GenDrillReportFile( const wxString& aFullFileName, REPORTER* aReporter )
900{
901 wxFFile out( aFullFileName, "wb" );
902
903 if( !out.IsOpened() )
904 {
905 if( aReporter )
906 {
907 wxString msg = wxString::Format( _( "Error creating drill report file '%s'" ),
908 aFullFileName );
909 aReporter->Report( msg, RPT_SEVERITY_ERROR );
910 }
911
912 return false;
913 }
914
915 FILE* outFp = out.fp();
916
917 static const char separator[] =
918 " =============================================================\n";
919
920 wxASSERT( m_pcb );
921
922 unsigned totalHoleCount;
923 wxFileName brdFilename( m_pcb->GetFileName() );
924
925 std::vector<DRILL_SPAN> hole_sets = getUniqueLayerPairs();
926
927 bool writeError = false;
928
929 try
930 {
931 fmt::print( outFp, "Drill report for {}\n", TO_UTF8( brdFilename.GetFullName() ) );
932 fmt::print( outFp, "Created on {}\n\n", TO_UTF8( GetISO8601CurrentDateTime() ) );
933
934 // Output the cu layer stackup, so layer name references make sense.
935 fmt::print( outFp, "Copper Layer Stackup:\n" );
936 fmt::print( outFp, "{}", separator );
937
938 int conventional_layer_num = 1;
939
940 for( PCB_LAYER_ID layer : LSET::AllCuMask( m_pcb->GetCopperLayerCount() ).UIOrder() )
941 {
942 fmt::print( outFp, " L{:<2}: {:<25} {}\n", conventional_layer_num++,
943 TO_UTF8( m_pcb->GetLayerName( layer ) ),
944 layerName( layer ).c_str() ); // generic layer name
945 }
946
947 fmt::print( outFp, "\n\n" );
948
949 /* output hole lists:
950 * 1 - through holes
951 * 2 - for partial holes only: by layer starting and ending pair
952 * 3 - Non Plated through holes
953 */
954
955 bool buildNPTHlist = false; // First pass: build PTH list only
956
957 // in this loop are plated only:
958 for( unsigned pair_ndx = 0; pair_ndx < hole_sets.size(); ++pair_ndx )
959 {
960 const DRILL_SPAN& span = hole_sets[pair_ndx];
961
962 buildHolesList( span, buildNPTHlist );
963
964 if( span.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) && !span.m_IsBackdrill )
965 {
966 fmt::print( outFp, "Drill file '{}' contains\n",
967 TO_UTF8( getDrillFileName( span, false, m_merge_PTH_NPTH ) ) );
968
969 fmt::print( outFp, " plated through holes:\n" );
970 fmt::print( outFp, "{}", separator );
971 totalHoleCount = printToolSummary( outFp, TOOL_SUMMARY::PLATED );
972 fmt::print( outFp, " Total plated holes count {}\n", totalHoleCount );
973 }
974 else if( span.m_IsBackdrill )
975 {
976 fmt::print( outFp, "Drill file '{}' contains\n",
977 TO_UTF8( getDrillFileName( span, false, m_merge_PTH_NPTH ) ) );
978
979 fmt::print( outFp, " backdrill span: '{}' to '{}':\n",
980 TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( span.DrillStartLayer() ) ) ),
981 TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( span.DrillEndLayer() ) ) ) );
982
983 fmt::print( outFp, "{}", separator );
984 totalHoleCount = printToolSummary( outFp, TOOL_SUMMARY::BACKDRILL );
985 fmt::print( outFp, " Total backdrilled holes count {}\n", totalHoleCount );
986 }
987 else
988 {
989 fmt::print( outFp, "Drill file '{}' contains\n",
990 TO_UTF8( getDrillFileName( span, false, m_merge_PTH_NPTH ) ) );
991
992 fmt::print( outFp, " holes connecting layer pair: '{} and {}' ({} vias):\n",
993 TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( span.Pair().first ) ) ),
994 TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( span.Pair().second ) ) ),
995 span.Pair().first == F_Cu || span.Pair().second == B_Cu ? "blind" : "buried" );
996
997 fmt::print( outFp, "{}", separator );
998 totalHoleCount = printToolSummary( outFp, TOOL_SUMMARY::PLATED );
999 fmt::print( outFp, " Total plated holes count {}\n", totalHoleCount );
1000 }
1001
1002 fmt::print( outFp, "\n\n" );
1003 }
1004
1005 // NPTHoles. Generate the full list (pads+vias) if PTH and NPTH are merged,
1006 // or only the NPTH list (which never has vias)
1007 if( !m_merge_PTH_NPTH )
1008 buildNPTHlist = true;
1009
1010 DRILL_SPAN npthSpan( F_Cu, B_Cu, false, buildNPTHlist );
1011
1012 buildHolesList( npthSpan, buildNPTHlist );
1013
1014 // nothing wrong with an empty NPTH file in report.
1015 if( m_merge_PTH_NPTH )
1016 fmt::print( outFp, "Not plated through holes are merged with plated holes\n" );
1017 else
1018 fmt::print( outFp, "Drill file '{}' contains\n",
1019 TO_UTF8( getDrillFileName( npthSpan, true, m_merge_PTH_NPTH ) ) );
1020
1021 fmt::print( outFp, " unplated through holes:\n" );
1022 fmt::print( outFp, "{}", separator );
1023 totalHoleCount = printToolSummary( outFp, TOOL_SUMMARY::UNPLATED );
1024 fmt::print( outFp, " Total unplated holes count {}\n", totalHoleCount );
1025 }
1026 catch( const std::system_error& )
1027 {
1028 writeError = true;
1029 }
1030 catch( const fmt::format_error& )
1031 {
1032 writeError = true;
1033 }
1034
1035 if( writeError && aReporter )
1036 {
1037 wxString msg = wxString::Format( _( "Error writing drill report file '%s'" ), aFullFileName );
1038 aReporter->Report( msg, RPT_SEVERITY_ERROR );
1039 }
1040
1041 return !writeError;
1042}
1043
1044
1046{
1047 // Plot the drill map:
1048 for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ )
1049 {
1050 const HOLE_INFO& hole = m_holeListBuffer[ii];
1051
1052 // Gives a good line thickness to have a good marker shape:
1054
1055 // Always plot the drill symbol (for slots identifies the needed cutter!
1056 aPlotter->Marker( hole.m_Hole_Pos, hole.m_Hole_Diameter, hole.m_Tool_Reference - 1 );
1057
1058 if( hole.m_Hole_Shape != 0 )
1059 {
1061 nullptr );
1062 }
1063 }
1064
1066
1067 return true;
1068}
1069
1070
1071unsigned GENDRILL_WRITER_BASE::printToolSummary( FILE* out, TOOL_SUMMARY aSummary ) const
1072{
1073 unsigned totalHoleCount = 0;
1074
1075 for( unsigned ii = 0; ii < m_toolListBuffer.size(); ii++ )
1076 {
1077 const DRILL_TOOL& tool = m_toolListBuffer[ii];
1078
1079 // Backdrills set the non-plated flag, so they must be classified before it is read
1083
1084 if( bucket != aSummary )
1085 continue;
1086
1087 // List the tool number assigned to each drill in mm then in inches.
1088 int tool_number = ii+1;
1089 fmt::print( out, " T{} {:2.3f}mm {:2.4f}\" ", tool_number,
1090 diameter_in_mm( tool.m_Diameter ),
1092
1093 // Now list how many holes and ovals are associated with each drill.
1094 if( ( tool.m_TotalCount == 1 ) && ( tool.m_OvalCount == 0 ) )
1095 fmt::print( out, "(1 hole" );
1096 else if( tool.m_TotalCount == 1 )
1097 fmt::print( out, "(1 hole) (with 1 slot" );
1098 else if( tool.m_OvalCount == 0 )
1099 fmt::print( out, "({} holes)", tool.m_TotalCount );
1100 else if( tool.m_OvalCount == 1 )
1101 fmt::print( out, "({} holes) (with 1 slot", tool.m_TotalCount );
1102 else // tool.m_OvalCount > 1
1103 fmt::print( out, "({} holes) (with {} slots", tool.m_TotalCount, tool.m_OvalCount );
1104
1106 fmt::print( out, ", castellated" );
1107
1109 fmt::print( out, ", press-fit" );
1110
1111 fmt::print( out, ")\n" );
1112
1113 totalHoleCount += tool.m_TotalCount;
1114 }
1115
1116 fmt::print( out, "\n" );
1117
1118 return totalHoleCount;
1119}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
BOX2< VECTOR2I > BOX2I
Definition box2.h:927
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:995
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr Vec Centre() const
Definition box2.h:94
constexpr coord_type GetX() const
Definition box2.h:204
constexpr size_type GetHeight() const
Definition box2.h:212
constexpr coord_type GetBottom() const
Definition box2.h:219
void SetLayerSet(const LSET &aLayerMask)
Definition pcbplot.h:84
void PlotShape(const PCB_SHAPE *aShape)
int GetCount() const
Return the number of objects in the list.
Definition collector.h:79
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:399
std::optional< int > m_MinStubLength
HOLE_ATTRIBUTE m_HoleAttribute
std::optional< int > m_MaxStubLength
void SetUnits(DXF_UNITS aUnit)
Set the units to use for plotting the DXF file.
virtual void SetParentGroup(EDA_GROUP *aGroup)
Definition eda_item.h:115
unsigned printToolSummary(FILE *out, TOOL_SUMMARY aSummary) const
Print m_toolListBuffer[] tools to aOut and returns total hole count.
virtual const wxString getProtectionFileName(const DRILL_SPAN &aSpan, IPC4761_FEATURES aFeature) const
const PAGE_INFO * m_pageInfo
void AddCreatedFile(const wxString &aPath)
std::vector< HOLE_INFO > m_holeListBuffer
TOOL_SUMMARY
Selects which subset of m_toolListBuffer a summary covers.
@ UNPLATED
Non-plated holes, excluding backdrills.
@ BACKDRILL
Backdrills, which are non-plated by construction.
void buildHolesList(const DRILL_SPAN &aSpan, bool aGenerateNPTH_list)
Create the list of holes and tools for a given board.
bool genDrillMapFile(const wxString &aFullFileName, PLOT_FORMAT aFormat)
Plot a map of drill marks for holes.
VECTOR2I GetOffset()
Return the plot offset (usually the position of the drill/place origin).
std::vector< DRILL_SPAN > getUniqueLayerPairs() const
Get unique layer pairs by examining the micro and blind_buried vias.
std::vector< DRILL_TOOL > m_toolListBuffer
const std::string layerPairName(DRILL_LAYER_PAIR aPair) const
bool CreateMapFilesSet(const wxString &aPlotDirectory, REPORTER *aReporter=nullptr)
Create the full set of map files for the board, in PS, PDF ... format (use SetMapFileFormat() to sele...
const std::string layerName(PCB_LAYER_ID aLayer) const
virtual const wxString getDrillFileName(const DRILL_SPAN &aSpan, bool aNPTH, bool aMerge_PTH_NPTH) const
bool plotDrillMarks(PLOTTER *aPlotter)
Write the drill marks in PDF, POSTSCRIPT or other supported formats/.
const wxString BuildFileFunctionAttributeString(const DRILL_SPAN &aSpan, TYPE_FILE aHoleType, bool aCompatNCdrill=false) const
bool GenDrillReportFile(const wxString &aFullFileName, REPORTER *aReporter=nullptr)
Create a plain text report file giving a list of drill values and drill count for through holes,...
void UseX2format(bool aEnable)
void UseX2NetAttributes(bool aEnable)
void DisableApertMacros(bool aDisable)
Disable Aperture Macro (AM) command, only for broken Gerber Readers.
Handle hole which must be drilled (diameter, position and layers).
HOLE_ATTRIBUTE m_HoleAttribute
EDA_ANGLE m_Hole_Orient
static const METRICS & Default()
Definition font.cpp:48
PCB specific render settings.
Definition pcb_painter.h:84
void SetDefaultPenWidth(int aWidth)
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:595
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:75
const VECTOR2D GetSizeIU(double aIUScale) const
Gets the page size in internal units.
Definition page_info.h:173
Parameters and options when plotting/printing a board.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Collect all BOARD_ITEM objects of a given set of KICAD_T type(s).
Definition collectors.h:517
void Collect(BOARD_ITEM *aBoard, const std::vector< KICAD_T > &aTypes)
Collect BOARD_ITEM objects using this class's Inspector method, which does the collection.
Base plotter engine class.
Definition plotter.h:136
virtual bool OpenFile(const wxString &aFullFilename)
Open or create the plot file aFullFilename.
Definition plotter.cpp:75
virtual void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition plotter.h:169
void SetRenderSettings(RENDER_SETTINGS *aSettings)
Definition plotter.h:166
static const int USE_DEFAULT_LINE_WIDTH
Definition plotter.h:140
virtual bool EndPlot()=0
virtual void ThickOval(const VECTOR2I &aPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, int aWidth, void *aData)
Definition plotter.cpp:388
virtual bool StartPlot(const wxString &aPageNumber)=0
virtual void SetGerberCoordinatesFormat(int aResolution, bool aUseInches=false)
Definition plotter.h:572
virtual PLOT_FORMAT GetPlotterType() const =0
Return the effective plot engine in use.
void Marker(const VECTOR2I &position, int diametre, unsigned aShapeId)
Draw a pattern shape number aShapeId, to coord position.
Definition plotter.cpp:363
virtual void SetCreator(const wxString &aCreator)
Definition plotter.h:188
void ClearHeaderLinesList()
Remove all lines from the list of free lines to print at the beginning of the file.
Definition plotter.h:206
virtual void SetViewport(const VECTOR2I &aOffset, double aIusPerDecimil, double aScale, bool aMirror)=0
Set the plot offset and scaling for the current plot.
void AddLineToHeader(const wxString &aExtraString)
Add a line to the list of free lines to print at the beginning of the file.
Definition plotter.h:198
virtual void SetColorMode(bool aColorMode)
Plot in B/W or color.
Definition plotter.h:163
virtual void SetCurrentLineWidth(int width, void *aData=nullptr)=0
Set the line width for the next drawing.
virtual void PlotText(const VECTOR2I &aPos, const COLOR4D &aColor, const wxString &aText, const TEXT_ATTRIBUTES &aAttributes, KIFONT::FONT *aFont=nullptr, const KIFONT::METRICS &aFontMetrics=KIFONT::METRICS::Default(), void *aData=nullptr)
Definition plotter.cpp:617
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:102
GR_TEXT_H_ALIGN_T m_Halign
GR_TEXT_V_ALIGN_T m_Valign
wxString GetDefaultPlotExtension(PLOT_FORMAT aFormat)
Return the default plot extension for a format.
std::vector< DRILL_OPERATION > EnumerateDrillOperations(const BOARD &aBoard, const DRILL_QUERY &aQuery)
The one place that decides what the board's holes are.
std::vector< HOLE_INFO > ToLegacyHoleList(const std::vector< DRILL_OPERATION > &aOperations)
Project canonical operations onto the drill writers' record.
HOLE_ATTRIBUTE
Definition drill_span.h:36
std::pair< PCB_LAYER_ID, PCB_LAYER_ID > DRILL_LAYER_PAIR
Definition drill_span.h:48
#define _(s)
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
Definition eda_angle.h:418
double diameter_in_mm(double ius)
double diameter_in_inches(double ius)
static bool cmpHoleSorting(const HOLE_INFO &a, const HOLE_INFO &b)
int getDefaultPenSize()
static int getMarkerBestPenSize(int aMarkerDiameter)
int getSketchOvalBestPenSize()
helper classes to handle hole info for drill files generators.
#define USE_ATTRIB_FOR_HOLES
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ Edge_Cuts
Definition layer_ids.h:108
@ Dwgs_User
Definition layer_ids.h:103
@ B_Cu
Definition layer_ids.h:61
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ F_Cu
Definition layer_ids.h:60
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition lset.cpp:750
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:79
void AddGerberX2Header(PLOTTER *aPlotter, const BOARD *aBoard, bool aUseX1CompatibilityMode)
Calculate some X2 attributes as defined in the Gerber file format specification J4 (chapter 5) and ad...
Definition pcbplot.cpp:290
PLOT_FORMAT
The set of supported output plot formats.
Definition plotter.h:63
Plotting engines similar to ps (PostScript, Gerber, svg)
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_ACTION
const int scale
std::vector< FAB_LAYER_COLOR > dummy
wxString From_UTF8(const char *cstring)
wxString GetISO8601CurrentDateTime()
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Selects which operations EnumerateDrillOperations() returns.
bool m_NonPlatedOnly
Emit only non-plated holes.
bool m_MergePTHNPTH
Emit plated and non-plated together, as the merged drill file does.
DRILL_SPAN m_Span
DRILL_LAYER_PAIR Pair() const
Definition drill_span.h:92
PCB_LAYER_ID DrillEndLayer() const
Definition drill_span.h:87
PCB_LAYER_ID DrillStartLayer() const
Definition drill_span.h:82
bool m_IsNonPlatedFile
Definition drill_span.h:130
bool m_IsBackdrill
Definition drill_span.h:129
The properties of a padstack drill.
Definition padstack.h:272
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_CENTER
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:80
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683