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
44
45
46/* Helper function for sorting hole list.
47 * Compare function used for sorting holes type type:
48 * plated then not plated
49 * then by increasing diameter value
50 * then by attribute type (vias, pad, mechanical)
51 * then by X then Y position
52 */
53static bool cmpHoleSorting( const HOLE_INFO& a, const HOLE_INFO& b )
54{
56 return b.m_Hole_NotPlated;
57
60
61 // At this point (same diameter, same plated type), group by attribute
62 // type (via, pad, mechanical, although currently only not plated pads are mechanical)
65
66 // At this point (same diameter, same type), sort by X then Y position.
67 // This is optimal for drilling and make the file reproducible as long as holes
68 // have not changed, even if the data order has changed.
69 if( a.m_Hole_Pos.x != b.m_Hole_Pos.x )
70 return a.m_Hole_Pos.x < b.m_Hole_Pos.x;
71
72 return a.m_Hole_Pos.y < b.m_Hole_Pos.y;
73}
74
75
76void GENDRILL_WRITER_BASE::buildHolesList( const DRILL_SPAN& aSpan, bool aGenerateNPTH_list )
77{
78 HOLE_INFO new_hole;
79
80 m_holeListBuffer.clear();
81 m_toolListBuffer.clear();
82
83 wxASSERT( aSpan.TopLayer() < aSpan.BottomLayer() ); // fix the caller
84
85 auto computeStubLength = [&]( PCB_LAYER_ID aStartLayer, PCB_LAYER_ID aEndLayer )
86 {
87 if( aStartLayer == UNDEFINED_LAYER || aEndLayer == UNDEFINED_LAYER )
88 return std::optional<int>();
89
90 BOARD_STACKUP& stackup = m_pcb->GetDesignSettings().GetStackupDescriptor();
91 return std::optional<int>( stackup.GetLayerDistance( aStartLayer, aEndLayer ) );
92 };
93
94 if( !aGenerateNPTH_list )
95 {
96 for( PCB_TRACK* track : m_pcb->Tracks() )
97 {
98 if( track->Type() != PCB_VIA_T )
99 continue;
100
101 PCB_VIA* via = static_cast<PCB_VIA*>( track );
102
103 if( aSpan.m_IsBackdrill )
104 {
105 auto tryEmitBackdrill = [&]( const PADSTACK::DRILL_PROPS& aDrill ) -> bool
106 {
107 if( aDrill.start == UNDEFINED_LAYER || aDrill.end == UNDEFINED_LAYER )
108 return false;
109
110 DRILL_LAYER_PAIR drillPair( std::min( aDrill.start, aDrill.end ),
111 std::max( aDrill.start, aDrill.end ) );
112
113 if( drillPair != aSpan.Pair() )
114 return false;
115
116 if( aDrill.start != aSpan.DrillStartLayer()
117 || aDrill.end != aSpan.DrillEndLayer() )
118 {
119 return false;
120 }
121
122 if( aDrill.size.x <= 0 && aDrill.size.y <= 0 )
123 return false;
124
125 HOLE_INFO hole;
126 hole.m_ItemParent = via;
128 hole.m_Tool_Reference = -1;
129 hole.m_Hole_Orient = ANGLE_0;
130 hole.m_Hole_NotPlated = true;
131 hole.m_Hole_Shape = 0;
132 hole.m_Hole_Pos = via->GetStart();
133 hole.m_Hole_Top_Layer = aSpan.TopLayer();
134 hole.m_Hole_Bottom_Layer = aSpan.BottomLayer();
135
136 int diameter = aDrill.size.x;
137
138 if( aDrill.size.y > 0 )
139 diameter = ( diameter > 0 ) ? std::min( diameter, aDrill.size.y )
140 : aDrill.size.y;
141
142 hole.m_Hole_Diameter = diameter;
143 hole.m_Hole_Size = aDrill.size;
144
145 if( aDrill.shape != PAD_DRILL_SHAPE::CIRCLE
146 && aDrill.size.x != aDrill.size.y )
147 {
148 hole.m_Hole_Shape = 1;
149 }
150
151 hole.m_Hole_Filled = aDrill.is_filled.value_or( false );
152 hole.m_Hole_Capped = aDrill.is_capped.value_or( false );
153 hole.m_Hole_Top_Covered = via->Padstack().IsCovered( hole.m_Hole_Top_Layer )
154 .value_or( false );
155 hole.m_Hole_Bot_Covered = via->Padstack().IsCovered( hole.m_Hole_Bottom_Layer )
156 .value_or( false );
157 hole.m_Hole_Top_Plugged = via->Padstack().IsPlugged( hole.m_Hole_Top_Layer )
158 .value_or( false );
159 hole.m_Hole_Bot_Plugged = via->Padstack().IsPlugged( hole.m_Hole_Bottom_Layer )
160 .value_or( false );
161 hole.m_Hole_Top_Tented = via->Padstack().IsTented( hole.m_Hole_Top_Layer )
162 .value_or( false );
163 hole.m_Hole_Bot_Tented = via->Padstack().IsTented( hole.m_Hole_Bottom_Layer )
164 .value_or( false );
165 hole.m_IsBackdrill = true;
174 hole.m_DrillStart = aDrill.start;
175 hole.m_DrillEnd = aDrill.end;
176 hole.m_StubLength = computeStubLength( aDrill.start, aDrill.end );
177
178 m_holeListBuffer.push_back( hole );
179 return true;
180 };
181
182 // A via may carry two independent backdrill operations (front-side and
183 // back-side), stored as secondary and tertiary drill props. Emit whichever
184 // one matches this span.
185 tryEmitBackdrill( via->Padstack().SecondaryDrill() );
186 tryEmitBackdrill( via->Padstack().TertiaryDrill() );
187 continue;
188 }
189
190 int hole_sz = via->GetDrillValue();
191
192 if( hole_sz == 0 )
193 continue;
194
195 PCB_LAYER_ID top_layer;
196 PCB_LAYER_ID bottom_layer;
197 via->LayerPair( &top_layer, &bottom_layer );
198
199 // Skip vias not starting and ending on current layer pair
200 // (layer order has not matter)
201 if( DRILL_LAYER_PAIR( top_layer, bottom_layer ) != aSpan.Pair()
202 && DRILL_LAYER_PAIR( bottom_layer, top_layer ) != aSpan.Pair() )
203 {
204 continue;
205 }
206
207 new_hole = HOLE_INFO();
208 new_hole.m_ItemParent = via;
209
210 if( aSpan.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
212 else
214
215 new_hole.m_Tool_Reference = -1;
216 new_hole.m_Hole_Orient = ANGLE_0;
217 new_hole.m_Hole_Diameter = hole_sz;
218 new_hole.m_Hole_NotPlated = false;
219 new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;
220 new_hole.m_Hole_Shape = 0;
221 new_hole.m_Hole_Pos = via->GetStart();
222 new_hole.m_Hole_Top_Layer = top_layer;
223 new_hole.m_Hole_Bottom_Layer = bottom_layer;
224 new_hole.m_Hole_Filled = via->Padstack().IsFilled().value_or( false );
225 new_hole.m_Hole_Capped = via->Padstack().IsCapped().value_or( false );
226 new_hole.m_Hole_Top_Covered = via->Padstack().IsCovered( top_layer ).value_or( false );
227 new_hole.m_Hole_Bot_Covered = via->Padstack().IsCovered( bottom_layer ).value_or( false );
228 new_hole.m_Hole_Top_Plugged = via->Padstack().IsPlugged( top_layer ).value_or( false );
229 new_hole.m_Hole_Bot_Plugged = via->Padstack().IsPlugged( bottom_layer ).value_or( false );
230 new_hole.m_Hole_Top_Tented = via->Padstack().IsTented( top_layer ).value_or( false );
231 new_hole.m_Hole_Bot_Tented = via->Padstack().IsTented( bottom_layer ).value_or( false );
232 new_hole.m_IsBackdrill = false;
233 new_hole.m_FrontPostMachining = via->Padstack().FrontPostMachining().mode.value_or( PAD_DRILL_POST_MACHINING_MODE::UNKNOWN );
234 new_hole.m_FrontPostMachiningSize = via->Padstack().FrontPostMachining().size;
235 new_hole.m_FrontPostMachiningDepth = via->Padstack().FrontPostMachining().depth;
236 new_hole.m_FrontPostMachiningAngle = via->Padstack().FrontPostMachining().angle;
237 new_hole.m_BackPostMachining = via->Padstack().BackPostMachining().mode.value_or( PAD_DRILL_POST_MACHINING_MODE::UNKNOWN );
238 new_hole.m_BackPostMachiningSize = via->Padstack().BackPostMachining().size;
239 new_hole.m_BackPostMachiningDepth = via->Padstack().BackPostMachining().depth;
240 new_hole.m_BackPostMachiningAngle = via->Padstack().BackPostMachining().angle;
241 new_hole.m_DrillStart = via->Padstack().Drill().start;
242 new_hole.m_DrillEnd = bottom_layer;
243
244 m_holeListBuffer.push_back( new_hole );
245 }
246 }
247
248 if( !aSpan.m_IsBackdrill && aSpan.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
249 {
250 for( FOOTPRINT* footprint : m_pcb->Footprints() )
251 {
252 for( PAD* pad : footprint->Pads() )
253 {
254 if( !m_merge_PTH_NPTH )
255 {
256 if( !aGenerateNPTH_list && pad->GetAttribute() == PAD_ATTRIB::NPTH )
257 continue;
258
259 if( aGenerateNPTH_list && pad->GetAttribute() != PAD_ATTRIB::NPTH )
260 continue;
261 }
262
263 if( pad->GetDrillSize().x == 0 )
264 continue;
265
266 new_hole = HOLE_INFO();
267 new_hole.m_ItemParent = pad;
268 new_hole.m_Hole_NotPlated = ( pad->GetAttribute() == PAD_ATTRIB::NPTH );
269
270 if( new_hole.m_Hole_NotPlated )
272 else
273 {
274 if( pad->GetProperty() == PAD_PROP::CASTELLATED )
276 else if( pad->GetProperty() == PAD_PROP::PRESSFIT )
278 else
280 }
281
282 new_hole.m_Tool_Reference = -1;
283 new_hole.m_Hole_Orient = pad->GetOrientation();
284 new_hole.m_Hole_Shape = 0;
285 new_hole.m_Hole_Diameter = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y );
286 new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;
287
288 if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE
289 && pad->GetDrillSizeX() != pad->GetDrillSizeY() )
290 {
291 new_hole.m_Hole_Shape = 1;
292 }
293
294 new_hole.m_Hole_Size = pad->GetDrillSize();
295 new_hole.m_Hole_Pos = pad->GetPosition();
296 new_hole.m_Hole_Bottom_Layer = B_Cu;
297 new_hole.m_Hole_Top_Layer = F_Cu;
298 m_holeListBuffer.push_back( new_hole );
299 }
300 }
301 }
302
303 // Sort holes per increasing diameter value (and for each dimater, by position)
304 sort( m_holeListBuffer.begin(), m_holeListBuffer.end(), cmpHoleSorting );
305
306 // build the tool list
307 int last_hole = -1; // Set to not initialized (this is a value not used
308 // for m_holeListBuffer[ii].m_Hole_Diameter)
309 bool last_notplated_opt = false;
311
312 DRILL_TOOL new_tool( 0, false );
313 unsigned jj;
314
315 for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ )
316 {
317 if( m_holeListBuffer[ii].m_Hole_Diameter != last_hole
318 || m_holeListBuffer[ii].m_Hole_NotPlated != last_notplated_opt
320 || m_holeListBuffer[ii].m_HoleAttribute != last_attribute
321#endif
322 )
323 {
324 new_tool.m_Diameter = m_holeListBuffer[ii].m_Hole_Diameter;
325 new_tool.m_Hole_NotPlated = m_holeListBuffer[ii].m_Hole_NotPlated;
326 new_tool.m_HoleAttribute = m_holeListBuffer[ii].m_HoleAttribute;
327 m_toolListBuffer.push_back( new_tool );
328 last_hole = new_tool.m_Diameter;
329 last_notplated_opt = new_tool.m_Hole_NotPlated;
330 last_attribute = new_tool.m_HoleAttribute;
331 }
332
333 jj = m_toolListBuffer.size();
334
335 if( jj == 0 )
336 continue; // Should not occurs
337
338 m_holeListBuffer[ii].m_Tool_Reference = jj; // Tool value Initialized (value >= 1)
339
340 m_toolListBuffer.back().m_TotalCount++;
341
342 if( m_holeListBuffer[ii].m_Hole_Shape )
343 m_toolListBuffer.back().m_OvalCount++;
344
345 if( m_holeListBuffer[ii].m_IsBackdrill )
346 {
347 m_toolListBuffer.back().m_IsBackdrill = true;
348
349 if( m_holeListBuffer[ii].m_StubLength.has_value() )
350 {
351 int stub = *m_holeListBuffer[ii].m_StubLength;
352
353 if( !m_toolListBuffer.back().m_MinStubLength.has_value()
354 || stub < *m_toolListBuffer.back().m_MinStubLength )
355 {
356 m_toolListBuffer.back().m_MinStubLength = stub;
357 }
358
359 if( !m_toolListBuffer.back().m_MaxStubLength.has_value()
360 || stub > *m_toolListBuffer.back().m_MaxStubLength )
361 {
362 m_toolListBuffer.back().m_MaxStubLength = stub;
363 }
364 }
365 }
366
371 || m_holeListBuffer[ii].m_IsBackdrill )
372 m_toolListBuffer.back().m_HasPostMachining = true;
373 }
374}
375
376
377std::vector<DRILL_SPAN> GENDRILL_WRITER_BASE::getUniqueLayerPairs() const
378{
379 wxASSERT( m_pcb );
380
382
383 vias.Collect( m_pcb, { PCB_VIA_T } );
384
385 std::set<DRILL_SPAN> unique;
386
387 for( int i = 0; i < vias.GetCount(); ++i )
388 {
389 PCB_VIA* via = static_cast<PCB_VIA*>( vias[i] );
390 PCB_LAYER_ID top_layer;
391 PCB_LAYER_ID bottom_layer;
392
393 via->LayerPair( &top_layer, &bottom_layer );
394
395 if( DRILL_LAYER_PAIR( top_layer, bottom_layer ) != DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
396 unique.emplace( top_layer, bottom_layer, false, false );
397
398 auto addBackdrillSpan = [&]( const PADSTACK::DRILL_PROPS& aDrill )
399 {
400 if( aDrill.start == UNDEFINED_LAYER || aDrill.end == UNDEFINED_LAYER )
401 return;
402
403 if( aDrill.size.x <= 0 && aDrill.size.y <= 0 )
404 return;
405
406 unique.emplace( aDrill.start, aDrill.end, true, false );
407 };
408
409 addBackdrillSpan( via->Padstack().SecondaryDrill() );
410 addBackdrillSpan( via->Padstack().TertiaryDrill() );
411 }
412
413 std::vector<DRILL_SPAN> ret;
414
415 ret.emplace_back( F_Cu, B_Cu, false, false );
416
417 for( const DRILL_SPAN& span : unique )
418 {
419 if( span.m_IsBackdrill || span.Pair() != DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
420 ret.push_back( span );
421 }
422
423 return ret;
424}
425
426
427const std::string GENDRILL_WRITER_BASE::layerName( PCB_LAYER_ID aLayer ) const
428{
429 // Generic names here.
430 switch( aLayer )
431 {
432 case F_Cu:
433 return "front";
434 case B_Cu:
435 return "back";
436 default:
437 {
438 // aLayer use even values, and the first internal layer (In1) is B_Cu + 2.
439 int ly_id = ( aLayer - B_Cu ) / 2;
440 return fmt::format( "in{}", ly_id );
441 }
442 }
443}
444
445
447{
448 std::string ret = layerName( aPair.first );
449 ret += '-';
450 ret += layerName( aPair.second );
451
452 return ret;
453}
454
455
456const wxString GENDRILL_WRITER_BASE::getDrillFileName( const DRILL_SPAN& aSpan, bool aNPTH,
457 bool aMerge_PTH_NPTH ) const
458{
459 wxASSERT( m_pcb );
460
461 wxString extend;
462
463 auto layerIndex = [&]( PCB_LAYER_ID aLayer )
464 {
465 int conventional_layer_num = 1;
466
467 for( PCB_LAYER_ID layer : LSET::AllCuMask( m_pcb->GetCopperLayerCount() ).UIOrder() )
468 {
469 if( layer == aLayer )
470 return conventional_layer_num;
471
472 conventional_layer_num++;
473 }
474
475 return conventional_layer_num;
476 };
477
478 if( aSpan.m_IsBackdrill )
479 {
480 extend.Printf( wxT( "_Backdrills_Drill_%d_%d" ),
481 layerIndex( aSpan.DrillStartLayer() ),
482 layerIndex( aSpan.DrillEndLayer() ) );
483 }
484 else if( aNPTH )
485 {
486 extend = wxT( "-NPTH" );
487 }
488 else if( aSpan.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
489 {
490 if( !aMerge_PTH_NPTH )
491 extend = wxT( "-PTH" );
492 // if merged, extend with nothing
493 }
494 else
495 {
496 extend += '-';
497 extend += layerPairName( aSpan.Pair() );
498 }
499
500 wxFileName fn = m_pcb->GetFileName();
501
502 fn.SetName( fn.GetName() + extend );
503 fn.SetExt( m_drillFileExtension );
504
505 wxString ret = fn.GetFullName();
506
507 return ret;
508}
509
510
512 IPC4761_FEATURES aFeature ) const
513{
514 wxASSERT( m_pcb );
515
516 wxString extend;
517
518 switch( aFeature )
519 {
521 extend << wxT( "-filling-" );
522 extend << layerPairName( aSpan.Pair() );
523 break;
525 extend << wxT( "-capping-" );
526 extend << layerPairName( aSpan.Pair() );
527 break;
529 extend << wxT( "-covering-" );
530 extend << layerName( aSpan.Pair().second );
531 break;
533 extend << wxT( "-covering-" );
534 extend << layerName( aSpan.Pair().first );
535 break;
537 extend << wxT( "-plugging-" );
538 extend << layerName( aSpan.Pair().second );
539 break;
541 extend << wxT( "-plugging-" );
542 extend << layerName( aSpan.Pair().first );
543 break;
545 extend << wxT( "-tenting-" );
546 extend << layerName( aSpan.Pair().second );
547 break;
549 extend << wxT( "-tenting-" );
550 extend << layerName( aSpan.Pair().first );
551 break;
552 }
553
554 wxFileName fn = m_pcb->GetFileName();
555
556 fn.SetName( fn.GetName() + extend );
557 fn.SetExt( m_drillFileExtension );
558
559 wxString ret = fn.GetFullName();
560
561 return ret;
562}
563
564
565bool GENDRILL_WRITER_BASE::CreateMapFilesSet( const wxString& aPlotDirectory, REPORTER * aReporter )
566{
567 wxFileName fn;
568 wxString msg;
569
570 std::vector<DRILL_SPAN> hole_sets = getUniqueLayerPairs();
571
572 if( !m_merge_PTH_NPTH )
573 hole_sets.emplace_back( F_Cu, B_Cu, false, true );
574
575 for( std::vector<DRILL_SPAN>::const_iterator it = hole_sets.begin(); it != hole_sets.end(); ++it )
576 {
577 const DRILL_SPAN& span = *it;
578 bool doing_npth = span.m_IsNonPlatedFile;
579
580 buildHolesList( span, doing_npth );
581
582 if( getHolesCount() > 0 || doing_npth || span.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
583 {
585 fn.SetPath( aPlotDirectory );
586
587 fn.SetExt( wxEmptyString ); // Will be added by GenDrillMap
588 wxString fullfilename = fn.GetFullPath() + wxT( "-drl_map" );
589 fullfilename << wxT(".") << GetDefaultPlotExtension( m_mapFileFmt );
590
591 bool success = genDrillMapFile( fullfilename, m_mapFileFmt );
592
593 if( ! success )
594 {
595 if( aReporter )
596 {
597 msg.Printf( _( "Failed to create file '%s'." ), fullfilename );
598 aReporter->Report( msg, RPT_SEVERITY_ERROR );
599 }
600
601 return false;
602 }
603 else
604 {
605 if( aReporter )
606 {
607 msg.Printf( _( "Created file '%s'." ), fullfilename );
608 aReporter->Report( msg, RPT_SEVERITY_ACTION );
609 }
610 }
611 }
612 }
613
614 return true;
615}
616
617
619 TYPE_FILE aHoleType,
620 bool aCompatNCdrill ) const
621{
622// Build a wxString containing the .FileFunction attribute for drill files.
623// %TF.FileFunction,Plated[NonPlated],layer1num,layer2num,PTH[NPTH][Blind][Buried],Drill[Route][Mixed]*%
624 wxString text;
625
626 if( aCompatNCdrill )
627 text = wxT( "; #@! " );
628 else
629 text = wxT( "%" );
630
631 text << wxT( "TF.FileFunction," );
632
633 if( aSpan.m_IsBackdrill || aHoleType == NPTH_FILE )
634 text << wxT( "NonPlated," );
635 else if( aHoleType == MIXED_FILE ) // only for Excellon format
636 text << wxT( "MixedPlating," );
637 else
638 text << wxT( "Plated," );
639
640 int layer1 = aSpan.Pair().first;
641 int layer2 = aSpan.Pair().second;
642
643 // In Gerber files, layers num are 1 to copper layer count instead of F_Cu to B_Cu
644 // (0 to copper layer count-1)
645 // Note also for a n copper layers board, gerber layers num are 1 ... n
646 //
647 // Copper layers use even values, so the layer id in file is
648 // (Copper layer id) /2 + 1 if layer is not B_Cu
649 if( layer1 == F_Cu )
650 layer1 = 1;
651 else if( layer1 == B_Cu )
652 layer1 = m_pcb->GetCopperLayerCount();
653 else
654 layer1 = ( ( layer1 - B_Cu ) / 2 ) + 1;
655
656 if( layer2 == F_Cu )
657 layer2 = 1;
658 else if( layer2 == B_Cu )
659 layer2 = m_pcb->GetCopperLayerCount();
660 else
661 layer2 = ( ( layer2 - B_Cu ) / 2) + 1;
662
663 // Ensure layer order is from top (smaller layer number) to bottom (bigger layer number)
664 if( layer1 > layer2 )
665 std::swap( layer1, layer2 );
666
667 text << layer1 << wxT( "," ) << layer2;
668
669 // Now add PTH or NPTH or Blind or Buried attribute
670 int toplayer = 1;
671 int bottomlayer = m_pcb->GetCopperLayerCount();
672
673 if( aSpan.m_IsBackdrill )
674 text << wxT( ",Blind" );
675 else if( aHoleType == NPTH_FILE )
676 text << wxT( ",NPTH" );
677 else if( aHoleType == MIXED_FILE ) // only for Excellon format
678 ; // write nothing
679 else if( layer1 == toplayer && layer2 == bottomlayer )
680 text << wxT( ",PTH" );
681 else if( layer1 == toplayer || layer2 == bottomlayer )
682 text << wxT( ",Blind" );
683 else
684 text << wxT( ",Buried" );
685
686 // In NC drill file, these previous parameters should be enough:
687 if( aCompatNCdrill )
688 return text;
689
690
691 // Now add Drill or Route or Mixed:
692 // file containing only round holes have Drill attribute
693 // file containing only oblong holes have Routed attribute
694 // file containing both holes have Mixed attribute
695 bool hasOblong = false;
696 bool hasDrill = false;
697
698 for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ )
699 {
700 const HOLE_INFO& hole_descr = m_holeListBuffer[ii];
701
702 if( hole_descr.m_Hole_Shape ) // m_Hole_Shape not 0 is an oblong hole)
703 hasOblong = true;
704 else
705 hasDrill = true;
706 }
707
708 if( hasOblong && hasDrill )
709 text << wxT( ",Mixed" );
710 else if( hasDrill )
711 text << wxT( ",Drill" );
712 else if( hasOblong )
713 text << wxT( ",Rout" );
714
715 // else: empty file.
716
717 // End of .FileFunction attribute:
718 text << wxT( "*%" );
719
720 return text;
721}
722
723
724/* Conversion utilities - these will be used often in there... */
725inline double diameter_in_inches( double ius )
726{
727 return ius * 0.001 / pcbIUScale.IU_PER_MILS;
728}
729
730
731inline double diameter_in_mm( double ius )
732{
733 return ius / pcbIUScale.IU_PER_MM;
734}
735
736
737// return a pen size to plot markers and having a readable shape
738// clamped to be >= MIN_SIZE_MM to avoid too small line width
739static int getMarkerBestPenSize( int aMarkerDiameter )
740{
741 int bestsize = aMarkerDiameter / 10;
742
743 const double MIN_SIZE_MM = 0.1;
744 bestsize = std::max( bestsize, pcbIUScale.mmToIU( MIN_SIZE_MM ) );
745
746 return bestsize;
747}
748
749
750// return a pen size to plot outlines for oval holes
752{
753 const double SKETCH_LINE_WIDTH_MM = 0.1;
754 return pcbIUScale.mmToIU( SKETCH_LINE_WIDTH_MM );
755}
756
757
758// return a default pen size to plot items with no specific line thickness
760{
761 const double DEFAULT_LINE_WIDTH_MM = 0.2;
762 return pcbIUScale.mmToIU( DEFAULT_LINE_WIDTH_MM );
763}
764
765
766bool GENDRILL_WRITER_BASE::genDrillMapFile( const wxString& aFullFileName, PLOT_FORMAT aFormat )
767{
768 // Remark:
769 // Hole list must be created before calling this function, by buildHolesList(),
770 // for the right holes set (PTH, NPTH, buried/blind vias ...)
771
772 double scale = 1.0;
773 VECTOR2I offset = GetOffset();
774 PLOTTER* plotter = nullptr;
776 int bottom_limit = 0; // Y coord limit of page. 0 mean do not use
777
778 PCB_PLOT_PARAMS plot_opts; // starts plotting with default options
779
780 const PAGE_INFO& page_info = m_pageInfo ? *m_pageInfo : dummy;
781
782 // Calculate dimensions and center of PCB. The Edge_Cuts layer must be visible
783 // to calculate the board edges bounding box
784 LSET visibleLayers = m_pcb->GetVisibleLayers();
785 m_pcb->SetVisibleLayers( visibleLayers | LSET( { Edge_Cuts } ) );
786 BOX2I bbbox = m_pcb->GetBoardEdgesBoundingBox();
787 m_pcb->SetVisibleLayers( visibleLayers );
788
789 // Some formats cannot be used to generate a document like the map files
790 // Currently HPGL (old format not very used)
791
792 if( aFormat == PLOT_FORMAT::HPGL )
793 aFormat = PLOT_FORMAT::PDF;
794
795 // Calculate the scale for the format type, scale 1 in HPGL, drawing on
796 // an A4 sheet in PS, + text description of symbols
797 switch( aFormat )
798 {
800 plotter = new GERBER_PLOTTER();
801 plotter->SetViewport( offset, pcbIUScale.IU_PER_MILS / 10, scale, false );
802 plotter->SetGerberCoordinatesFormat( 5 ); // format x.5 unit = mm
803 break;
804
805 default: wxASSERT( false ); KI_FALLTHROUGH;
806
807 case PLOT_FORMAT::PDF:
809 case PLOT_FORMAT::SVG:
810 {
811 VECTOR2I pageSizeIU = page_info.GetSizeIU( pcbIUScale.IU_PER_MILS );
812
813 // Reserve a 10 mm margin around the page.
814 int margin = pcbIUScale.mmToIU( 10 );
815
816 // Calculate a scaling factor to print the board on the sheet
817 double Xscale = double( pageSizeIU.x - ( 2 * margin ) ) / bbbox.GetWidth();
818
819 // We should print the list of drill sizes, so reserve room for it
820 // 60% height for board 40% height for list
821 int ypagesize_for_board = KiROUND( pageSizeIU.y * 0.6 );
822 double Yscale = double( ypagesize_for_board - margin ) / bbbox.GetHeight();
823
824 scale = std::min( Xscale, Yscale );
825
826 // Experience shows the scale should not to large, because texts
827 // create problem (can be to big or too small).
828 // So the scale is clipped at 3.0;
829 scale = std::min( scale, 3.0 );
830
831 offset.x = KiROUND( double( bbbox.Centre().x ) - ( pageSizeIU.x / 2.0 ) / scale );
832 offset.y = KiROUND( double( bbbox.Centre().y ) - ( ypagesize_for_board / 2.0 ) / scale );
833
834 // bottom_limit is used to plot the legend (drill diameters)
835 // texts are scaled differently for scale > 1.0 and <= 1.0
836 // so the limit is scaled differently.
837 bottom_limit = ( pageSizeIU.y - margin ) / std::min( scale, 1.0 );
838
839 if( aFormat == PLOT_FORMAT::SVG )
840 plotter = new SVG_PLOTTER;
841 else if( aFormat == PLOT_FORMAT::PDF )
842 plotter = new PDF_PLOTTER;
843 else
844 plotter = new PS_PLOTTER;
845
846 plotter->SetPageSettings( page_info );
847 plotter->SetViewport( offset, pcbIUScale.IU_PER_MILS / 10, scale, false );
848 break;
849 }
850
851 case PLOT_FORMAT::DXF:
852 {
853 DXF_PLOTTER* dxf_plotter = new DXF_PLOTTER;
854
856
857 plotter = dxf_plotter;
858 plotter->SetPageSettings( page_info );
859 plotter->SetViewport( offset, pcbIUScale.IU_PER_MILS / 10, scale, false );
860 break;
861 }
862 }
863
864 plotter->SetCreator( wxT( "PCBNEW" ) );
865 plotter->SetColorMode( false );
866
867 KIGFX::PCB_RENDER_SETTINGS renderSettings;
868 renderSettings.SetDefaultPenWidth( getDefaultPenSize() );
869
870 plotter->SetRenderSettings( &renderSettings );
871
872 if( !plotter->OpenFile( aFullFileName ) )
873 {
874 delete plotter;
875 return false;
876 }
877
878 plotter->ClearHeaderLinesList();
879
880 // For the Gerber X2 format we need to set the "FileFunction" to Drillmap
881 // and set a few other options.
882 if( plotter->GetPlotterType() == PLOT_FORMAT::GERBER )
883 {
884 GERBER_PLOTTER* gbrplotter = static_cast<GERBER_PLOTTER*>( plotter );
885 gbrplotter->DisableApertMacros( false );
886 gbrplotter->UseX2format( true ); // Mandatory
887 gbrplotter->UseX2NetAttributes( false ); // net attributes have no meaning here
888
889 // Attributes are added using X2 format
890 AddGerberX2Header( gbrplotter, m_pcb, false );
891
892 wxString text;
893
894 // Add the TF.FileFunction
895 text = "%TF.FileFunction,Drillmap*%";
896 gbrplotter->AddLineToHeader( text );
897
898 // Add the TF.FilePolarity
899 text = wxT( "%TF.FilePolarity,Positive*%" );
900 gbrplotter->AddLineToHeader( text );
901 }
902
903 plotter->StartPlot( wxT( "1" ) );
904
905 // Draw items on edge layer.
906 // Not all, only items useful for drill map, i.e. board outlines.
907 BRDITEMS_PLOTTER itemplotter( plotter, m_pcb, plot_opts );
908
909 // Use attributes of a drawing layer (we are not really draw the Edge.Cuts layer)
910 itemplotter.SetLayerSet( { Dwgs_User } );
911
912 for( BOARD_ITEM* item : m_pcb->Drawings() )
913 {
914 if( item->GetLayer() != Edge_Cuts )
915 continue;
916
917 switch( item->Type() )
918 {
919 case PCB_SHAPE_T:
920 {
921 PCB_SHAPE dummy_shape( *static_cast<PCB_SHAPE*>( item ) );
922 dummy_shape.SetLayer( Dwgs_User );
923 dummy_shape.SetParentGroup( nullptr ); // Remove group association, not needed for plotting
924 itemplotter.PlotShape( &dummy_shape );
925 }
926 break;
927
928 default: break;
929 }
930 }
931
932 // Plot edge cuts in footprints
933 for( const FOOTPRINT* footprint : m_pcb->Footprints() )
934 {
935 for( BOARD_ITEM* item : footprint->GraphicalItems() )
936 {
937 if( item->GetLayer() != Edge_Cuts )
938 continue;
939
940 switch( item->Type() )
941 {
942 case PCB_SHAPE_T:
943 {
944 PCB_SHAPE dummy_shape( *static_cast<PCB_SHAPE*>( item ) );
945 dummy_shape.SetLayer( Dwgs_User );
946 dummy_shape.SetParentGroup( nullptr ); // Remove group association, not needed for plotting
947 itemplotter.PlotShape( &dummy_shape );
948 }
949 break;
950
951 default: break;
952 }
953 }
954 }
955
956 int plotX, plotY, TextWidth;
957 int intervalle = 0;
958 char line[1024];
959 wxString msg;
960 int textmarginaftersymbol = pcbIUScale.mmToIU( 2 );
961
962 // Set Drill Symbols width
963 plotter->SetCurrentLineWidth( -1 );
964
965 // Plot board outlines and drill map
966 plotDrillMarks( plotter );
967
968 // Print a list of symbols used.
969 int charSize = pcbIUScale.mmToIU( 2 ); // text size in IUs
970
971 // real char scale will be 1/scale, because the global plot scale is scale
972 // for scale < 1.0 ( plot bigger actual size)
973 // Therefore charScale = 1.0 / scale keep the initial charSize
974 // (for scale < 1 we use the global scaling factor: the board must be plotted
975 // smaller than the actual size)
976 double charScale = std::min( 1.0, 1.0 / scale );
977
978 TextWidth = KiROUND( ( charSize * charScale ) / 10.0 ); // Set text width (thickness)
979 intervalle = KiROUND( charSize * charScale ) + TextWidth;
980
981 // Trace information.
982 plotX = KiROUND( bbbox.GetX() + textmarginaftersymbol * charScale );
983 plotY = bbbox.GetBottom() + intervalle;
984
985 // Plot title "Info"
986 wxString Text = wxT( "Drill Map:" );
987
988 TEXT_ATTRIBUTES attrs;
989 attrs.m_StrokeWidth = TextWidth;
991 attrs.m_Size = KiROUND( charSize * charScale, charSize * charScale );
994 attrs.m_Multiline = false;
995
996 plotter->PlotText( VECTOR2I( plotX, plotY ), COLOR4D::UNSPECIFIED, Text, attrs, nullptr /* stroke font */,
998
999 // For some formats (PS, PDF SVG) we plot the drill size list on more than one column
1000 // because the list must be contained inside the printed page
1001 // (others formats do not have a defined page size)
1002 int max_line_len = 0; // The max line len in iu of the currently plotted column
1003
1004 for( unsigned ii = 0; ii < m_toolListBuffer.size(); ii++ )
1005 {
1006 DRILL_TOOL& tool = m_toolListBuffer[ii];
1007
1008 if( tool.m_TotalCount == 0 )
1009 continue;
1010
1011 plotY += intervalle;
1012
1013 // Ensure there are room to plot the line
1014 if( bottom_limit && ( plotY + intervalle > bottom_limit ) )
1015 {
1016 plotY = bbbox.GetBottom() + intervalle;
1017 plotX += max_line_len + pcbIUScale.mmToIU( 10 ); //column_width;
1018 max_line_len = 0;
1019 }
1020
1021 int plot_diam = KiROUND( tool.m_Diameter );
1022
1023 // For markers plotted with the comment, keep marker size <= text height
1024 plot_diam = std::min( plot_diam, KiROUND( charSize * charScale ) );
1025 int x = KiROUND( plotX - textmarginaftersymbol * charScale - plot_diam / 2.0 );
1026 int y = KiROUND( plotY + charSize * charScale );
1027
1028 plotter->SetCurrentLineWidth( getMarkerBestPenSize( plot_diam ) );
1029 plotter->Marker( VECTOR2I( x, y ), plot_diam, ii );
1030 plotter->SetCurrentLineWidth( -1 );
1031
1032 // List the diameter of each drill in mm and inches.
1033 snprintf( line, sizeof( line ), "%3.3fmm / %2.4f\" ", diameter_in_mm( tool.m_Diameter ),
1035
1036 msg = From_UTF8( line );
1037 wxString extraInfo;
1038
1040 extraInfo += wxT( ", castellated" );
1042 extraInfo += wxT( ", press-fit" );
1043
1044 if( tool.m_IsBackdrill )
1045 {
1046 if( tool.m_MinStubLength.has_value() )
1047 {
1048 double minStub = pcbIUScale.IUTomm( *tool.m_MinStubLength );
1049
1050 if( tool.m_MaxStubLength.has_value() && tool.m_MaxStubLength != tool.m_MinStubLength )
1051 {
1052 double maxStub = pcbIUScale.IUTomm( *tool.m_MaxStubLength );
1053 extraInfo += wxString::Format( wxT( ", backdrill stub %.3f-%.3fmm" ), minStub, maxStub );
1054 }
1055 else
1056 {
1057 extraInfo += wxString::Format( wxT( ", backdrill stub %.3fmm" ), minStub );
1058 }
1059 }
1060 else
1061 {
1062 extraInfo += wxT( ", backdrill" );
1063 }
1064 }
1065
1066 if( tool.m_HasPostMachining )
1067 extraInfo += wxT( ", post-machined" );
1068
1069 wxString counts;
1070
1071 if( ( tool.m_TotalCount == 1 ) && ( tool.m_OvalCount == 0 ) )
1072 counts.Printf( wxT( "(1 hole%s)" ), extraInfo );
1073 else if( tool.m_TotalCount == 1 )
1074 counts.Printf( wxT( "(1 slot%s)" ), extraInfo );
1075 else if( tool.m_OvalCount == 0 )
1076 counts.Printf( wxT( "(%d holes%s)" ), tool.m_TotalCount, extraInfo );
1077 else if( tool.m_OvalCount == 1 )
1078 counts.Printf( wxT( "(%d holes + 1 slot%s)" ), tool.m_TotalCount - 1, extraInfo );
1079 else
1080 counts.Printf( wxT( "(%d holes + %d slots%s)" ), tool.m_TotalCount - tool.m_OvalCount, tool.m_OvalCount,
1081 extraInfo );
1082
1083 msg += counts;
1084
1085 if( tool.m_Hole_NotPlated )
1086 msg += wxT( " (not plated)" );
1087
1088 plotter->PlotText( VECTOR2I( plotX, y ), COLOR4D::UNSPECIFIED, msg, attrs, nullptr /* stroke font */,
1090
1091 intervalle = KiROUND( ( ( charSize * charScale ) + TextWidth ) * 1.2 );
1092
1093 if( intervalle < ( plot_diam + ( 1 * pcbIUScale.IU_PER_MM / scale ) + TextWidth ) )
1094 intervalle = plot_diam + ( 1 * pcbIUScale.IU_PER_MM / scale ) + TextWidth;
1095
1096 // Evaluate the text horizontal size, to know the maximal column size
1097 // This is a rough value, but ok to create a new column to plot next texts
1098 int text_len = msg.Len() * ( ( charSize * charScale ) + TextWidth );
1099 max_line_len = std::max( max_line_len, text_len + plot_diam );
1100 }
1101
1102 plotter->EndPlot();
1103 delete plotter;
1104
1105 return true;
1106}
1107
1108
1109bool GENDRILL_WRITER_BASE::GenDrillReportFile( const wxString& aFullFileName, REPORTER* aReporter )
1110{
1111 wxFFile out( aFullFileName, "wb" );
1112
1113 if( !out.IsOpened() )
1114 {
1115 if( aReporter )
1116 {
1117 wxString msg = wxString::Format( _( "Error creating drill report file '%s'" ),
1118 aFullFileName );
1119 aReporter->Report( msg, RPT_SEVERITY_ERROR );
1120 }
1121
1122 return false;
1123 }
1124
1125 FILE* outFp = out.fp();
1126
1127 static const char separator[] =
1128 " =============================================================\n";
1129
1130 wxASSERT( m_pcb );
1131
1132 unsigned totalHoleCount;
1133 wxFileName brdFilename( m_pcb->GetFileName() );
1134
1135 std::vector<DRILL_SPAN> hole_sets = getUniqueLayerPairs();
1136
1137 bool writeError = false;
1138
1139 try
1140 {
1141 fmt::print( outFp, "Drill report for {}\n", TO_UTF8( brdFilename.GetFullName() ) );
1142 fmt::print( outFp, "Created on {}\n\n", TO_UTF8( GetISO8601CurrentDateTime() ) );
1143
1144 // Output the cu layer stackup, so layer name references make sense.
1145 fmt::print( outFp, "Copper Layer Stackup:\n" );
1146 fmt::print( outFp, "{}", separator );
1147
1148 int conventional_layer_num = 1;
1149
1150 for( PCB_LAYER_ID layer : LSET::AllCuMask( m_pcb->GetCopperLayerCount() ).UIOrder() )
1151 {
1152 fmt::print( outFp, " L{:<2}: {:<25} {}\n", conventional_layer_num++,
1153 TO_UTF8( m_pcb->GetLayerName( layer ) ),
1154 layerName( layer ).c_str() ); // generic layer name
1155 }
1156
1157 fmt::print( outFp, "\n\n" );
1158
1159 /* output hole lists:
1160 * 1 - through holes
1161 * 2 - for partial holes only: by layer starting and ending pair
1162 * 3 - Non Plated through holes
1163 */
1164
1165 bool buildNPTHlist = false; // First pass: build PTH list only
1166
1167 // in this loop are plated only:
1168 for( unsigned pair_ndx = 0; pair_ndx < hole_sets.size(); ++pair_ndx )
1169 {
1170 const DRILL_SPAN& span = hole_sets[pair_ndx];
1171
1172 buildHolesList( span, buildNPTHlist );
1173
1174 if( span.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) && !span.m_IsBackdrill )
1175 {
1176 fmt::print( outFp, "Drill file '{}' contains\n",
1177 TO_UTF8( getDrillFileName( span, false, m_merge_PTH_NPTH ) ) );
1178
1179 fmt::print( outFp, " plated through holes:\n" );
1180 fmt::print( outFp, "{}", separator );
1181 totalHoleCount = printToolSummary( outFp, false );
1182 fmt::print( outFp, " Total plated holes count {}\n", totalHoleCount );
1183 }
1184 else if( span.m_IsBackdrill )
1185 {
1186 fmt::print( outFp, "Drill file '{}' contains\n",
1187 TO_UTF8( getDrillFileName( span, false, m_merge_PTH_NPTH ) ) );
1188
1189 fmt::print( outFp, " backdrill span: '{}' to '{}':\n",
1190 TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( span.DrillStartLayer() ) ) ),
1191 TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( span.DrillEndLayer() ) ) ) );
1192
1193 fmt::print( outFp, "{}", separator );
1194 totalHoleCount = printToolSummary( outFp, false );
1195 fmt::print( outFp, " Total backdrilled holes count {}\n", totalHoleCount );
1196 }
1197 else
1198 {
1199 fmt::print( outFp, "Drill file '{}' contains\n",
1200 TO_UTF8( getDrillFileName( span, false, m_merge_PTH_NPTH ) ) );
1201
1202 fmt::print( outFp, " holes connecting layer pair: '{} and {}' ({} vias):\n",
1203 TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( span.Pair().first ) ) ),
1204 TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( span.Pair().second ) ) ),
1205 span.Pair().first == F_Cu || span.Pair().second == B_Cu ? "blind" : "buried" );
1206
1207 fmt::print( outFp, "{}", separator );
1208 totalHoleCount = printToolSummary( outFp, false );
1209 fmt::print( outFp, " Total plated holes count {}\n", totalHoleCount );
1210 }
1211
1212 fmt::print( outFp, "\n\n" );
1213 }
1214
1215 // NPTHoles. Generate the full list (pads+vias) if PTH and NPTH are merged,
1216 // or only the NPTH list (which never has vias)
1217 if( !m_merge_PTH_NPTH )
1218 buildNPTHlist = true;
1219
1220 DRILL_SPAN npthSpan( F_Cu, B_Cu, false, buildNPTHlist );
1221
1222 buildHolesList( npthSpan, buildNPTHlist );
1223
1224 // nothing wrong with an empty NPTH file in report.
1225 if( m_merge_PTH_NPTH )
1226 fmt::print( outFp, "Not plated through holes are merged with plated holes\n" );
1227 else
1228 fmt::print( outFp, "Drill file '{}' contains\n",
1229 TO_UTF8( getDrillFileName( npthSpan, true, m_merge_PTH_NPTH ) ) );
1230
1231 fmt::print( outFp, " unplated through holes:\n" );
1232 fmt::print( outFp, "{}", separator );
1233 totalHoleCount = printToolSummary( outFp, true );
1234 fmt::print( outFp, " Total unplated holes count {}\n", totalHoleCount );
1235 }
1236 catch( const std::system_error& )
1237 {
1238 writeError = true;
1239 }
1240 catch( const fmt::format_error& )
1241 {
1242 writeError = true;
1243 }
1244
1245 if( writeError && aReporter )
1246 {
1247 wxString msg = wxString::Format( _( "Error writing drill report file '%s'" ), aFullFileName );
1248 aReporter->Report( msg, RPT_SEVERITY_ERROR );
1249 }
1250
1251 return !writeError;
1252}
1253
1254
1256{
1257 // Plot the drill map:
1258 for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ )
1259 {
1260 const HOLE_INFO& hole = m_holeListBuffer[ii];
1261
1262 // Gives a good line thickness to have a good marker shape:
1264
1265 // Always plot the drill symbol (for slots identifies the needed cutter!
1266 aPlotter->Marker( hole.m_Hole_Pos, hole.m_Hole_Diameter, hole.m_Tool_Reference - 1 );
1267
1268 if( hole.m_Hole_Shape != 0 )
1269 {
1271 nullptr );
1272 }
1273 }
1274
1276
1277 return true;
1278}
1279
1280
1281unsigned GENDRILL_WRITER_BASE::printToolSummary( FILE* out, bool aSummaryNPTH ) const
1282{
1283 unsigned totalHoleCount = 0;
1284
1285 for( unsigned ii = 0; ii < m_toolListBuffer.size(); ii++ )
1286 {
1287 const DRILL_TOOL& tool = m_toolListBuffer[ii];
1288
1289 if( aSummaryNPTH && !tool.m_Hole_NotPlated )
1290 continue;
1291
1292 if( !aSummaryNPTH && tool.m_Hole_NotPlated )
1293 continue;
1294
1295 // List the tool number assigned to each drill in mm then in inches.
1296 int tool_number = ii+1;
1297 fmt::print( out, " T{} {:2.3f}mm {:2.4f}\" ", tool_number,
1298 diameter_in_mm( tool.m_Diameter ),
1300
1301 // Now list how many holes and ovals are associated with each drill.
1302 if( ( tool.m_TotalCount == 1 ) && ( tool.m_OvalCount == 0 ) )
1303 fmt::print( out, "(1 hole" );
1304 else if( tool.m_TotalCount == 1 )
1305 fmt::print( out, "(1 hole) (with 1 slot" );
1306 else if( tool.m_OvalCount == 0 )
1307 fmt::print( out, "({} holes)", tool.m_TotalCount );
1308 else if( tool.m_OvalCount == 1 )
1309 fmt::print( out, "({} holes) (with 1 slot", tool.m_TotalCount );
1310 else // tool.m_OvalCount > 1
1311 fmt::print( out, "({} holes) (with {} slots", tool.m_TotalCount, tool.m_OvalCount );
1312
1314 fmt::print( out, ", castellated" );
1315
1317 fmt::print( out, ", press-fit" );
1318
1319 fmt::print( out, ")\n" );
1320
1321 totalHoleCount += tool.m_TotalCount;
1322 }
1323
1324 fmt::print( out, "\n" );
1325
1326 return totalHoleCount;
1327}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:81
Manage layers needed to make a physical board.
int GetLayerDistance(PCB_LAYER_ID aFirstLayer, PCB_LAYER_ID aSecondLayer) const
Calculate the distance (height) between the two given copper layers.
constexpr size_type GetWidth() const
Definition box2.h:210
constexpr Vec Centre() const
Definition box2.h:93
constexpr coord_type GetX() const
Definition box2.h:203
constexpr size_type GetHeight() const
Definition box2.h:211
constexpr coord_type GetBottom() const
Definition box2.h:218
void SetLayerSet(const LSET &aLayerMask)
Definition pcbplot.h:83
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:398
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:113
virtual const wxString getProtectionFileName(const DRILL_SPAN &aSpan, IPC4761_FEATURES aFeature) const
const PAGE_INFO * m_pageInfo
std::vector< HOLE_INFO > m_holeListBuffer
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
unsigned printToolSummary(FILE *out, bool aSummaryNPTH) const
Print m_toolListBuffer[] tools to aOut and returns total hole count.
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).
PAD_DRILL_POST_MACHINING_MODE m_FrontPostMachining
PCB_LAYER_ID m_Hole_Bottom_Layer
std::optional< int > m_StubLength
PCB_LAYER_ID m_DrillEnd
PCB_LAYER_ID m_Hole_Top_Layer
HOLE_ATTRIBUTE m_HoleAttribute
PCB_LAYER_ID m_DrillStart
BOARD_ITEM * m_ItemParent
PAD_DRILL_POST_MACHINING_MODE m_BackPostMachining
EDA_ANGLE m_Hole_Orient
static const METRICS & Default()
Definition font.cpp:48
PCB specific render settings.
Definition pcb_painter.h:78
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
Definition pad.h:61
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:133
virtual bool OpenFile(const wxString &aFullFilename)
Open or create the plot file aFullFilename.
Definition plotter.cpp:73
virtual void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition plotter.h:166
void SetRenderSettings(RENDER_SETTINGS *aSettings)
Definition plotter.h:163
static const int USE_DEFAULT_LINE_WIDTH
Definition plotter.h:137
virtual bool EndPlot()=0
virtual void ThickOval(const VECTOR2I &aPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, int aWidth, void *aData)
Definition plotter.cpp:483
virtual bool StartPlot(const wxString &aPageNumber)=0
virtual void SetGerberCoordinatesFormat(int aResolution, bool aUseInches=false)
Definition plotter.h:569
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:361
virtual void SetCreator(const wxString &aCreator)
Definition plotter.h:185
void ClearHeaderLinesList()
Remove all lines from the list of free lines to print at the beginning of the file.
Definition plotter.h:203
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:195
virtual void SetColorMode(bool aColorMode)
Plot in B/W or color.
Definition plotter.h:160
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:712
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:71
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:100
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.
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
Definition eda_angle.h:407
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
std::pair< PCB_LAYER_ID, PCB_LAYER_ID > DRILL_LAYER_PAIR
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
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:103
@ PRESSFIT
a PTH with a hole diameter with tight tolerances for press fit pin
Definition padstack.h:123
@ CASTELLATED
a pad with a castellated through hole
Definition padstack.h:121
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:60
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.
DRILL_LAYER_PAIR Pair() const
PCB_LAYER_ID DrillEndLayer() const
PCB_LAYER_ID TopLayer() const
PCB_LAYER_ID DrillStartLayer() const
PCB_LAYER_ID BottomLayer() const
! The properties of a padstack drill. Drill position is always the pad position (origin).
Definition padstack.h:266
@ 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:81
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:90
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683