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( IsCopperLayerLowerThan( aSpan.BottomLayer(), aSpan.TopLayer() ) ); // 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_SPAN drillSpan( aDrill.start, aDrill.end, true, false );
111
112 if( drillSpan.Pair() != aSpan.Pair() )
113 return false;
114
115 if( aDrill.start != aSpan.DrillStartLayer()
116 || aDrill.end != aSpan.DrillEndLayer() )
117 {
118 return false;
119 }
120
121 if( aDrill.size.x <= 0 && aDrill.size.y <= 0 )
122 return false;
123
124 HOLE_INFO hole;
125 hole.m_ItemParent = via;
127 hole.m_Tool_Reference = -1;
128 hole.m_Hole_Orient = ANGLE_0;
129 hole.m_Hole_NotPlated = true;
130 hole.m_Hole_Shape = 0;
131 hole.m_Hole_Pos = via->GetStart();
132 hole.m_Hole_Top_Layer = aSpan.TopLayer();
133 hole.m_Hole_Bottom_Layer = aSpan.BottomLayer();
134
135 int diameter = aDrill.size.x;
136
137 if( aDrill.size.y > 0 )
138 diameter = ( diameter > 0 ) ? std::min( diameter, aDrill.size.y )
139 : aDrill.size.y;
140
141 hole.m_Hole_Diameter = diameter;
142 hole.m_Hole_Size = aDrill.size;
143
144 if( aDrill.shape != PAD_DRILL_SHAPE::CIRCLE
145 && aDrill.size.x != aDrill.size.y )
146 {
147 hole.m_Hole_Shape = 1;
148 }
149
150 hole.m_Hole_Filled = aDrill.is_filled.value_or( false );
151 hole.m_Hole_Capped = aDrill.is_capped.value_or( false );
152 hole.m_Hole_Top_Covered = via->Padstack().IsCovered( hole.m_Hole_Top_Layer )
153 .value_or( false );
154 hole.m_Hole_Bot_Covered = via->Padstack().IsCovered( hole.m_Hole_Bottom_Layer )
155 .value_or( false );
156 hole.m_Hole_Top_Plugged = via->Padstack().IsPlugged( hole.m_Hole_Top_Layer )
157 .value_or( false );
158 hole.m_Hole_Bot_Plugged = via->Padstack().IsPlugged( hole.m_Hole_Bottom_Layer )
159 .value_or( false );
160 hole.m_Hole_Top_Tented = via->Padstack().IsTented( hole.m_Hole_Top_Layer )
161 .value_or( false );
162 hole.m_Hole_Bot_Tented = via->Padstack().IsTented( hole.m_Hole_Bottom_Layer )
163 .value_or( false );
164 hole.m_IsBackdrill = true;
173 hole.m_DrillStart = aDrill.start;
174 hole.m_DrillEnd = aDrill.end;
175 hole.m_StubLength = computeStubLength( aDrill.start, aDrill.end );
176
177 m_holeListBuffer.push_back( hole );
178 return true;
179 };
180
181 // A via may carry two independent backdrill operations (front-side and
182 // back-side), stored as secondary and tertiary drill props. Emit whichever
183 // one matches this span.
184 tryEmitBackdrill( via->Padstack().SecondaryDrill() );
185 tryEmitBackdrill( via->Padstack().TertiaryDrill() );
186 continue;
187 }
188
189 int hole_sz = via->GetDrillValue();
190
191 if( hole_sz == 0 )
192 continue;
193
194 PCB_LAYER_ID top_layer;
195 PCB_LAYER_ID bottom_layer;
196 via->LayerPair( &top_layer, &bottom_layer );
197
198 // Skip vias not starting and ending on current layer pair
199 // (layer order has not matter)
200 if( DRILL_LAYER_PAIR( top_layer, bottom_layer ) != aSpan.Pair()
201 && DRILL_LAYER_PAIR( bottom_layer, top_layer ) != aSpan.Pair() )
202 {
203 continue;
204 }
205
206 new_hole = HOLE_INFO();
207 new_hole.m_ItemParent = via;
208
209 if( aSpan.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
211 else
213
214 new_hole.m_Tool_Reference = -1;
215 new_hole.m_Hole_Orient = ANGLE_0;
216 new_hole.m_Hole_Diameter = hole_sz;
217 new_hole.m_Hole_NotPlated = false;
218 new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;
219 new_hole.m_Hole_Shape = 0;
220 new_hole.m_Hole_Pos = via->GetStart();
221 new_hole.m_Hole_Top_Layer = top_layer;
222 new_hole.m_Hole_Bottom_Layer = bottom_layer;
223 new_hole.m_Hole_Filled = via->Padstack().IsFilled().value_or( false );
224 new_hole.m_Hole_Capped = via->Padstack().IsCapped().value_or( false );
225 new_hole.m_Hole_Top_Covered = via->Padstack().IsCovered( top_layer ).value_or( false );
226 new_hole.m_Hole_Bot_Covered = via->Padstack().IsCovered( bottom_layer ).value_or( false );
227 new_hole.m_Hole_Top_Plugged = via->Padstack().IsPlugged( top_layer ).value_or( false );
228 new_hole.m_Hole_Bot_Plugged = via->Padstack().IsPlugged( bottom_layer ).value_or( false );
229 new_hole.m_Hole_Top_Tented = via->Padstack().IsTented( top_layer ).value_or( false );
230 new_hole.m_Hole_Bot_Tented = via->Padstack().IsTented( bottom_layer ).value_or( false );
231 new_hole.m_IsBackdrill = false;
232 new_hole.m_FrontPostMachining = via->Padstack().FrontPostMachining().mode.value_or( PAD_DRILL_POST_MACHINING_MODE::UNKNOWN );
233 new_hole.m_FrontPostMachiningSize = via->Padstack().FrontPostMachining().size;
234 new_hole.m_FrontPostMachiningDepth = via->Padstack().FrontPostMachining().depth;
235 new_hole.m_FrontPostMachiningAngle = via->Padstack().FrontPostMachining().angle;
236 new_hole.m_BackPostMachining = via->Padstack().BackPostMachining().mode.value_or( PAD_DRILL_POST_MACHINING_MODE::UNKNOWN );
237 new_hole.m_BackPostMachiningSize = via->Padstack().BackPostMachining().size;
238 new_hole.m_BackPostMachiningDepth = via->Padstack().BackPostMachining().depth;
239 new_hole.m_BackPostMachiningAngle = via->Padstack().BackPostMachining().angle;
240 new_hole.m_DrillStart = via->Padstack().Drill().start;
241 new_hole.m_DrillEnd = bottom_layer;
242
243 m_holeListBuffer.push_back( new_hole );
244 }
245 }
246
247 if( !aSpan.m_IsBackdrill && aSpan.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
248 {
249 for( FOOTPRINT* footprint : m_pcb->Footprints() )
250 {
251 for( PAD* pad : footprint->Pads() )
252 {
253 if( !m_merge_PTH_NPTH )
254 {
255 if( !aGenerateNPTH_list && pad->GetAttribute() == PAD_ATTRIB::NPTH )
256 continue;
257
258 if( aGenerateNPTH_list && pad->GetAttribute() != PAD_ATTRIB::NPTH )
259 continue;
260 }
261
262 if( pad->GetDrillSize().x == 0 )
263 continue;
264
265 new_hole = HOLE_INFO();
266 new_hole.m_ItemParent = pad;
267 new_hole.m_Hole_NotPlated = ( pad->GetAttribute() == PAD_ATTRIB::NPTH );
268
269 if( new_hole.m_Hole_NotPlated )
271 else
272 {
273 if( pad->GetProperty() == PAD_PROP::CASTELLATED )
275 else if( pad->GetProperty() == PAD_PROP::PRESSFIT )
277 else
279 }
280
281 new_hole.m_Tool_Reference = -1;
282 new_hole.m_Hole_Orient = pad->GetOrientation();
283 new_hole.m_Hole_Shape = 0;
284 new_hole.m_Hole_Diameter = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y );
285 new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;
286
287 if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE
288 && pad->GetDrillSizeX() != pad->GetDrillSizeY() )
289 {
290 new_hole.m_Hole_Shape = 1;
291 }
292
293 new_hole.m_Hole_Size = pad->GetDrillSize();
294 new_hole.m_Hole_Pos = pad->GetPosition();
295 new_hole.m_Hole_Bottom_Layer = B_Cu;
296 new_hole.m_Hole_Top_Layer = F_Cu;
297 m_holeListBuffer.push_back( new_hole );
298 }
299 }
300 }
301
302 // Sort holes per increasing diameter value (and for each dimater, by position)
303 sort( m_holeListBuffer.begin(), m_holeListBuffer.end(), cmpHoleSorting );
304
305 // build the tool list
306 int last_hole = -1; // Set to not initialized (this is a value not used
307 // for m_holeListBuffer[ii].m_Hole_Diameter)
308 bool last_notplated_opt = false;
310
311 DRILL_TOOL new_tool( 0, false );
312 unsigned jj;
313
314 for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ )
315 {
316 if( m_holeListBuffer[ii].m_Hole_Diameter != last_hole
317 || m_holeListBuffer[ii].m_Hole_NotPlated != last_notplated_opt
319 || m_holeListBuffer[ii].m_HoleAttribute != last_attribute
320#endif
321 )
322 {
323 new_tool.m_Diameter = m_holeListBuffer[ii].m_Hole_Diameter;
324 new_tool.m_Hole_NotPlated = m_holeListBuffer[ii].m_Hole_NotPlated;
325 new_tool.m_HoleAttribute = m_holeListBuffer[ii].m_HoleAttribute;
326 m_toolListBuffer.push_back( new_tool );
327 last_hole = new_tool.m_Diameter;
328 last_notplated_opt = new_tool.m_Hole_NotPlated;
329 last_attribute = new_tool.m_HoleAttribute;
330 }
331
332 jj = m_toolListBuffer.size();
333
334 if( jj == 0 )
335 continue; // Should not occurs
336
337 m_holeListBuffer[ii].m_Tool_Reference = jj; // Tool value Initialized (value >= 1)
338
339 m_toolListBuffer.back().m_TotalCount++;
340
341 if( m_holeListBuffer[ii].m_Hole_Shape )
342 m_toolListBuffer.back().m_OvalCount++;
343
344 if( m_holeListBuffer[ii].m_IsBackdrill )
345 {
346 m_toolListBuffer.back().m_IsBackdrill = true;
347
348 if( m_holeListBuffer[ii].m_StubLength.has_value() )
349 {
350 int stub = *m_holeListBuffer[ii].m_StubLength;
351
352 if( !m_toolListBuffer.back().m_MinStubLength.has_value()
353 || stub < *m_toolListBuffer.back().m_MinStubLength )
354 {
355 m_toolListBuffer.back().m_MinStubLength = stub;
356 }
357
358 if( !m_toolListBuffer.back().m_MaxStubLength.has_value()
359 || stub > *m_toolListBuffer.back().m_MaxStubLength )
360 {
361 m_toolListBuffer.back().m_MaxStubLength = stub;
362 }
363 }
364 }
365
370 || m_holeListBuffer[ii].m_IsBackdrill )
371 m_toolListBuffer.back().m_HasPostMachining = true;
372 }
373}
374
375
376std::vector<DRILL_SPAN> GENDRILL_WRITER_BASE::getUniqueLayerPairs() const
377{
378 wxASSERT( m_pcb );
379
381
382 vias.Collect( m_pcb, { PCB_VIA_T } );
383
384 std::set<DRILL_SPAN> unique;
385
386 for( int i = 0; i < vias.GetCount(); ++i )
387 {
388 PCB_VIA* via = static_cast<PCB_VIA*>( vias[i] );
389 PCB_LAYER_ID top_layer;
390 PCB_LAYER_ID bottom_layer;
391
392 via->LayerPair( &top_layer, &bottom_layer );
393
394 if( DRILL_LAYER_PAIR( top_layer, bottom_layer ) != DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
395 unique.emplace( top_layer, bottom_layer, false, false );
396
397 auto addBackdrillSpan = [&]( const PADSTACK::DRILL_PROPS& aDrill )
398 {
399 if( aDrill.start == UNDEFINED_LAYER || aDrill.end == UNDEFINED_LAYER )
400 return;
401
402 if( aDrill.size.x <= 0 && aDrill.size.y <= 0 )
403 return;
404
405 unique.emplace( aDrill.start, aDrill.end, true, false );
406 };
407
408 addBackdrillSpan( via->Padstack().SecondaryDrill() );
409 addBackdrillSpan( via->Padstack().TertiaryDrill() );
410 }
411
412 std::vector<DRILL_SPAN> ret;
413
414 ret.emplace_back( F_Cu, B_Cu, false, false );
415
416 for( const DRILL_SPAN& span : unique )
417 {
418 if( span.m_IsBackdrill || span.Pair() != DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
419 ret.push_back( span );
420 }
421
422 return ret;
423}
424
425
426const std::string GENDRILL_WRITER_BASE::layerName( PCB_LAYER_ID aLayer ) const
427{
428 // Generic names here.
429 switch( aLayer )
430 {
431 case F_Cu:
432 return "front";
433 case B_Cu:
434 return "back";
435 default:
436 {
437 // aLayer use even values, and the first internal layer (In1) is B_Cu + 2.
438 int ly_id = ( aLayer - B_Cu ) / 2;
439 return fmt::format( "in{}", ly_id );
440 }
441 }
442}
443
444
446{
447 std::string ret = layerName( aPair.first );
448 ret += '-';
449 ret += layerName( aPair.second );
450
451 return ret;
452}
453
454
455const wxString GENDRILL_WRITER_BASE::getDrillFileName( const DRILL_SPAN& aSpan, bool aNPTH,
456 bool aMerge_PTH_NPTH ) const
457{
458 wxASSERT( m_pcb );
459
460 wxString extend;
461
462 auto layerIndex = [&]( PCB_LAYER_ID aLayer )
463 {
464 int conventional_layer_num = 1;
465
466 for( PCB_LAYER_ID layer : LSET::AllCuMask( m_pcb->GetCopperLayerCount() ).UIOrder() )
467 {
468 if( layer == aLayer )
469 return conventional_layer_num;
470
471 conventional_layer_num++;
472 }
473
474 return conventional_layer_num;
475 };
476
477 if( aSpan.m_IsBackdrill )
478 {
479 extend.Printf( wxT( "_Backdrills_Drill_%d_%d" ),
480 layerIndex( aSpan.DrillStartLayer() ),
481 layerIndex( aSpan.DrillEndLayer() ) );
482 }
483 else if( aNPTH )
484 {
485 extend = wxT( "-NPTH" );
486 }
487 else if( aSpan.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
488 {
489 if( !aMerge_PTH_NPTH )
490 extend = wxT( "-PTH" );
491 // if merged, extend with nothing
492 }
493 else
494 {
495 extend += '-';
496 extend += layerPairName( aSpan.Pair() );
497 }
498
499 wxFileName fn = m_pcb->GetFileName();
500
501 fn.SetName( fn.GetName() + extend );
502 fn.SetExt( m_drillFileExtension );
503
504 wxString ret = fn.GetFullName();
505
506 return ret;
507}
508
509
511 IPC4761_FEATURES aFeature ) const
512{
513 wxASSERT( m_pcb );
514
515 wxString extend;
516
517 switch( aFeature )
518 {
520 extend << wxT( "-filling-" );
521 extend << layerPairName( aSpan.Pair() );
522 break;
524 extend << wxT( "-capping-" );
525 extend << layerPairName( aSpan.Pair() );
526 break;
528 extend << wxT( "-covering-" );
529 extend << layerName( aSpan.Pair().second );
530 break;
532 extend << wxT( "-covering-" );
533 extend << layerName( aSpan.Pair().first );
534 break;
536 extend << wxT( "-plugging-" );
537 extend << layerName( aSpan.Pair().second );
538 break;
540 extend << wxT( "-plugging-" );
541 extend << layerName( aSpan.Pair().first );
542 break;
544 extend << wxT( "-tenting-" );
545 extend << layerName( aSpan.Pair().second );
546 break;
548 extend << wxT( "-tenting-" );
549 extend << layerName( aSpan.Pair().first );
550 break;
551 }
552
553 wxFileName fn = m_pcb->GetFileName();
554
555 fn.SetName( fn.GetName() + extend );
556 fn.SetExt( m_drillFileExtension );
557
558 wxString ret = fn.GetFullName();
559
560 return ret;
561}
562
563
564bool GENDRILL_WRITER_BASE::CreateMapFilesSet( const wxString& aPlotDirectory, REPORTER * aReporter )
565{
566 wxFileName fn;
567 wxString msg;
568
569 std::vector<DRILL_SPAN> hole_sets = getUniqueLayerPairs();
570
571 if( !m_merge_PTH_NPTH )
572 hole_sets.emplace_back( F_Cu, B_Cu, false, true );
573
574 for( std::vector<DRILL_SPAN>::const_iterator it = hole_sets.begin(); it != hole_sets.end(); ++it )
575 {
576 const DRILL_SPAN& span = *it;
577 bool doing_npth = span.m_IsNonPlatedFile;
578
579 buildHolesList( span, doing_npth );
580
581 if( getHolesCount() > 0 || doing_npth || span.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
582 {
584 fn.SetPath( aPlotDirectory );
585
586 fn.SetExt( wxEmptyString ); // Will be added by GenDrillMap
587 wxString fullfilename = fn.GetFullPath() + wxT( "-drl_map" );
588 fullfilename << wxT(".") << GetDefaultPlotExtension( m_mapFileFmt );
589
590 bool success = genDrillMapFile( fullfilename, m_mapFileFmt );
591
592 if( ! success )
593 {
594 if( aReporter )
595 {
596 msg.Printf( _( "Failed to create file '%s'." ), fullfilename );
597 aReporter->Report( msg, RPT_SEVERITY_ERROR );
598 }
599
600 return false;
601 }
602 else
603 {
604 if( aReporter )
605 {
606 msg.Printf( _( "Created file '%s'." ), fullfilename );
607 aReporter->Report( msg, RPT_SEVERITY_ACTION );
608 }
609 }
610 }
611 }
612
613 return true;
614}
615
616
618 TYPE_FILE aHoleType,
619 bool aCompatNCdrill ) const
620{
621// Build a wxString containing the .FileFunction attribute for drill files.
622// %TF.FileFunction,Plated[NonPlated],layer1num,layer2num,PTH[NPTH][Blind][Buried],Drill[Route][Mixed]*%
623 wxString text;
624
625 if( aCompatNCdrill )
626 text = wxT( "; #@! " );
627 else
628 text = wxT( "%" );
629
630 text << wxT( "TF.FileFunction," );
631
632 if( aSpan.m_IsBackdrill || aHoleType == NPTH_FILE )
633 text << wxT( "NonPlated," );
634 else if( aHoleType == MIXED_FILE ) // only for Excellon format
635 text << wxT( "MixedPlating," );
636 else
637 text << wxT( "Plated," );
638
639 int layer1 = aSpan.Pair().first;
640 int layer2 = aSpan.Pair().second;
641
642 // In Gerber files, layers num are 1 to copper layer count instead of F_Cu to B_Cu
643 // (0 to copper layer count-1)
644 // Note also for a n copper layers board, gerber layers num are 1 ... n
645 //
646 // Copper layers use even values, so the layer id in file is
647 // (Copper layer id) /2 + 1 if layer is not B_Cu
648 if( layer1 == F_Cu )
649 layer1 = 1;
650 else if( layer1 == B_Cu )
651 layer1 = m_pcb->GetCopperLayerCount();
652 else
653 layer1 = ( ( layer1 - B_Cu ) / 2 ) + 1;
654
655 if( layer2 == F_Cu )
656 layer2 = 1;
657 else if( layer2 == B_Cu )
658 layer2 = m_pcb->GetCopperLayerCount();
659 else
660 layer2 = ( ( layer2 - B_Cu ) / 2) + 1;
661
662 // Ensure layer order is from top (smaller layer number) to bottom (bigger layer number)
663 if( layer1 > layer2 )
664 std::swap( layer1, layer2 );
665
666 text << layer1 << wxT( "," ) << layer2;
667
668 // Now add PTH or NPTH or Blind or Buried attribute
669 int toplayer = 1;
670 int bottomlayer = m_pcb->GetCopperLayerCount();
671
672 if( aSpan.m_IsBackdrill )
673 text << wxT( ",Blind" );
674 else if( aHoleType == NPTH_FILE )
675 text << wxT( ",NPTH" );
676 else if( aHoleType == MIXED_FILE ) // only for Excellon format
677 ; // write nothing
678 else if( layer1 == toplayer && layer2 == bottomlayer )
679 text << wxT( ",PTH" );
680 else if( layer1 == toplayer || layer2 == bottomlayer )
681 text << wxT( ",Blind" );
682 else
683 text << wxT( ",Buried" );
684
685 // In NC drill file, these previous parameters should be enough:
686 if( aCompatNCdrill )
687 return text;
688
689
690 // Now add Drill or Route or Mixed:
691 // file containing only round holes have Drill attribute
692 // file containing only oblong holes have Routed attribute
693 // file containing both holes have Mixed attribute
694 bool hasOblong = false;
695 bool hasDrill = false;
696
697 for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ )
698 {
699 const HOLE_INFO& hole_descr = m_holeListBuffer[ii];
700
701 if( hole_descr.m_Hole_Shape ) // m_Hole_Shape not 0 is an oblong hole)
702 hasOblong = true;
703 else
704 hasDrill = true;
705 }
706
707 if( hasOblong && hasDrill )
708 text << wxT( ",Mixed" );
709 else if( hasDrill )
710 text << wxT( ",Drill" );
711 else if( hasOblong )
712 text << wxT( ",Rout" );
713
714 // else: empty file.
715
716 // End of .FileFunction attribute:
717 text << wxT( "*%" );
718
719 return text;
720}
721
722
723/* Conversion utilities - these will be used often in there... */
724inline double diameter_in_inches( double ius )
725{
726 return ius * 0.001 / pcbIUScale.IU_PER_MILS;
727}
728
729
730inline double diameter_in_mm( double ius )
731{
732 return ius / pcbIUScale.IU_PER_MM;
733}
734
735
736// return a pen size to plot markers and having a readable shape
737// clamped to be >= MIN_SIZE_MM to avoid too small line width
738static int getMarkerBestPenSize( int aMarkerDiameter )
739{
740 int bestsize = aMarkerDiameter / 10;
741
742 const double MIN_SIZE_MM = 0.1;
743 bestsize = std::max( bestsize, pcbIUScale.mmToIU( MIN_SIZE_MM ) );
744
745 return bestsize;
746}
747
748
749// return a pen size to plot outlines for oval holes
751{
752 const double SKETCH_LINE_WIDTH_MM = 0.1;
753 return pcbIUScale.mmToIU( SKETCH_LINE_WIDTH_MM );
754}
755
756
757// return a default pen size to plot items with no specific line thickness
759{
760 const double DEFAULT_LINE_WIDTH_MM = 0.2;
761 return pcbIUScale.mmToIU( DEFAULT_LINE_WIDTH_MM );
762}
763
764
765bool GENDRILL_WRITER_BASE::genDrillMapFile( const wxString& aFullFileName, PLOT_FORMAT aFormat )
766{
767 // Remark:
768 // Hole list must be created before calling this function, by buildHolesList(),
769 // for the right holes set (PTH, NPTH, buried/blind vias ...)
770
771 double scale = 1.0;
772 VECTOR2I offset = GetOffset();
773 PLOTTER* plotter = nullptr;
775 int bottom_limit = 0; // Y coord limit of page. 0 mean do not use
776
777 PCB_PLOT_PARAMS plot_opts; // starts plotting with default options
778
779 const PAGE_INFO& page_info = m_pageInfo ? *m_pageInfo : dummy;
780
781 // Calculate dimensions and center of PCB. The Edge_Cuts layer must be visible
782 // to calculate the board edges bounding box
783 LSET visibleLayers = m_pcb->GetVisibleLayers();
784 m_pcb->SetVisibleLayers( visibleLayers | LSET( { Edge_Cuts } ) );
785 BOX2I bbbox = m_pcb->GetBoardEdgesBoundingBox();
786 m_pcb->SetVisibleLayers( visibleLayers );
787
788 // Some formats cannot be used to generate a document like the map files
789 // Currently HPGL (old format not very used)
790
791 if( aFormat == PLOT_FORMAT::HPGL )
792 aFormat = PLOT_FORMAT::PDF;
793
794 // Calculate the scale for the format type, scale 1 in HPGL, drawing on
795 // an A4 sheet in PS, + text description of symbols
796 switch( aFormat )
797 {
799 plotter = new GERBER_PLOTTER();
800 plotter->SetViewport( offset, pcbIUScale.IU_PER_MILS / 10, scale, false );
801 plotter->SetGerberCoordinatesFormat( 5 ); // format x.5 unit = mm
802 break;
803
804 default: wxASSERT( false ); KI_FALLTHROUGH;
805
806 case PLOT_FORMAT::PDF:
808 case PLOT_FORMAT::SVG:
809 {
810 VECTOR2I pageSizeIU = page_info.GetSizeIU( pcbIUScale.IU_PER_MILS );
811
812 // Reserve a 10 mm margin around the page.
813 int margin = pcbIUScale.mmToIU( 10 );
814
815 // Calculate a scaling factor to print the board on the sheet
816 double Xscale = double( pageSizeIU.x - ( 2 * margin ) ) / bbbox.GetWidth();
817
818 // We should print the list of drill sizes, so reserve room for it
819 // 60% height for board 40% height for list
820 int ypagesize_for_board = KiROUND( pageSizeIU.y * 0.6 );
821 double Yscale = double( ypagesize_for_board - margin ) / bbbox.GetHeight();
822
823 scale = std::min( Xscale, Yscale );
824
825 // Experience shows the scale should not to large, because texts
826 // create problem (can be to big or too small).
827 // So the scale is clipped at 3.0;
828 scale = std::min( scale, 3.0 );
829
830 offset.x = KiROUND( double( bbbox.Centre().x ) - ( pageSizeIU.x / 2.0 ) / scale );
831 offset.y = KiROUND( double( bbbox.Centre().y ) - ( ypagesize_for_board / 2.0 ) / scale );
832
833 // bottom_limit is used to plot the legend (drill diameters)
834 // texts are scaled differently for scale > 1.0 and <= 1.0
835 // so the limit is scaled differently.
836 bottom_limit = ( pageSizeIU.y - margin ) / std::min( scale, 1.0 );
837
838 if( aFormat == PLOT_FORMAT::SVG )
839 plotter = new SVG_PLOTTER;
840 else if( aFormat == PLOT_FORMAT::PDF )
841 plotter = new PDF_PLOTTER;
842 else
843 plotter = new PS_PLOTTER;
844
845 plotter->SetPageSettings( page_info );
846 plotter->SetViewport( offset, pcbIUScale.IU_PER_MILS / 10, scale, false );
847 break;
848 }
849
850 case PLOT_FORMAT::DXF:
851 {
852 DXF_PLOTTER* dxf_plotter = new DXF_PLOTTER;
853
855
856 plotter = dxf_plotter;
857 plotter->SetPageSettings( page_info );
858 plotter->SetViewport( offset, pcbIUScale.IU_PER_MILS / 10, scale, false );
859 break;
860 }
861 }
862
863 plotter->SetCreator( wxT( "PCBNEW" ) );
864 plotter->SetColorMode( false );
865
866 KIGFX::PCB_RENDER_SETTINGS renderSettings;
867 renderSettings.SetDefaultPenWidth( getDefaultPenSize() );
868
869 plotter->SetRenderSettings( &renderSettings );
870
871 if( !plotter->OpenFile( aFullFileName ) )
872 {
873 delete plotter;
874 return false;
875 }
876
877 plotter->ClearHeaderLinesList();
878
879 // For the Gerber X2 format we need to set the "FileFunction" to Drillmap
880 // and set a few other options.
881 if( plotter->GetPlotterType() == PLOT_FORMAT::GERBER )
882 {
883 GERBER_PLOTTER* gbrplotter = static_cast<GERBER_PLOTTER*>( plotter );
884 gbrplotter->DisableApertMacros( false );
885 gbrplotter->UseX2format( true ); // Mandatory
886 gbrplotter->UseX2NetAttributes( false ); // net attributes have no meaning here
887
888 // Attributes are added using X2 format
889 AddGerberX2Header( gbrplotter, m_pcb, false );
890
891 wxString text;
892
893 // Add the TF.FileFunction
894 text = "%TF.FileFunction,Drillmap*%";
895 gbrplotter->AddLineToHeader( text );
896
897 // Add the TF.FilePolarity
898 text = wxT( "%TF.FilePolarity,Positive*%" );
899 gbrplotter->AddLineToHeader( text );
900 }
901
902 plotter->StartPlot( wxT( "1" ) );
903
904 // Draw items on edge layer.
905 // Not all, only items useful for drill map, i.e. board outlines.
906 BRDITEMS_PLOTTER itemplotter( plotter, m_pcb, plot_opts );
907
908 // Use attributes of a drawing layer (we are not really draw the Edge.Cuts layer)
909 itemplotter.SetLayerSet( { Dwgs_User } );
910
911 for( BOARD_ITEM* item : m_pcb->Drawings() )
912 {
913 if( item->GetLayer() != Edge_Cuts )
914 continue;
915
916 switch( item->Type() )
917 {
918 case PCB_SHAPE_T:
919 {
920 PCB_SHAPE dummy_shape( *static_cast<PCB_SHAPE*>( item ) );
921 dummy_shape.SetLayer( Dwgs_User );
922 dummy_shape.SetParentGroup( nullptr ); // Remove group association, not needed for plotting
923 itemplotter.PlotShape( &dummy_shape );
924 }
925 break;
926
927 default: break;
928 }
929 }
930
931 // Plot edge cuts in footprints
932 for( const FOOTPRINT* footprint : m_pcb->Footprints() )
933 {
934 for( BOARD_ITEM* item : footprint->GraphicalItems() )
935 {
936 if( item->GetLayer() != Edge_Cuts )
937 continue;
938
939 switch( item->Type() )
940 {
941 case PCB_SHAPE_T:
942 {
943 PCB_SHAPE dummy_shape( *static_cast<PCB_SHAPE*>( item ) );
944 dummy_shape.SetLayer( Dwgs_User );
945 dummy_shape.SetParentGroup( nullptr ); // Remove group association, not needed for plotting
946 itemplotter.PlotShape( &dummy_shape );
947 }
948 break;
949
950 default: break;
951 }
952 }
953 }
954
955 int plotX, plotY, TextWidth;
956 int intervalle = 0;
957 char line[1024];
958 wxString msg;
959 int textmarginaftersymbol = pcbIUScale.mmToIU( 2 );
960
961 // Set Drill Symbols width
962 plotter->SetCurrentLineWidth( -1 );
963
964 // Plot board outlines and drill map
965 plotDrillMarks( plotter );
966
967 // Print a list of symbols used.
968 int charSize = pcbIUScale.mmToIU( 2 ); // text size in IUs
969
970 // real char scale will be 1/scale, because the global plot scale is scale
971 // for scale < 1.0 ( plot bigger actual size)
972 // Therefore charScale = 1.0 / scale keep the initial charSize
973 // (for scale < 1 we use the global scaling factor: the board must be plotted
974 // smaller than the actual size)
975 double charScale = std::min( 1.0, 1.0 / scale );
976
977 TextWidth = KiROUND( ( charSize * charScale ) / 10.0 ); // Set text width (thickness)
978 intervalle = KiROUND( charSize * charScale ) + TextWidth;
979
980 // Trace information.
981 plotX = KiROUND( bbbox.GetX() + textmarginaftersymbol * charScale );
982 plotY = bbbox.GetBottom() + intervalle;
983
984 // Plot title "Info"
985 wxString Text = wxT( "Drill Map:" );
986
987 TEXT_ATTRIBUTES attrs;
988 attrs.m_StrokeWidth = TextWidth;
990 attrs.m_Size = KiROUND( charSize * charScale, charSize * charScale );
993 attrs.m_Multiline = false;
994
995 plotter->PlotText( VECTOR2I( plotX, plotY ), COLOR4D::UNSPECIFIED, Text, attrs, nullptr /* stroke font */,
997
998 // For some formats (PS, PDF SVG) we plot the drill size list on more than one column
999 // because the list must be contained inside the printed page
1000 // (others formats do not have a defined page size)
1001 int max_line_len = 0; // The max line len in iu of the currently plotted column
1002
1003 for( unsigned ii = 0; ii < m_toolListBuffer.size(); ii++ )
1004 {
1005 DRILL_TOOL& tool = m_toolListBuffer[ii];
1006
1007 if( tool.m_TotalCount == 0 )
1008 continue;
1009
1010 plotY += intervalle;
1011
1012 // Ensure there are room to plot the line
1013 if( bottom_limit && ( plotY + intervalle > bottom_limit ) )
1014 {
1015 plotY = bbbox.GetBottom() + intervalle;
1016 plotX += max_line_len + pcbIUScale.mmToIU( 10 ); //column_width;
1017 max_line_len = 0;
1018 }
1019
1020 int plot_diam = KiROUND( tool.m_Diameter );
1021
1022 // For markers plotted with the comment, keep marker size <= text height
1023 plot_diam = std::min( plot_diam, KiROUND( charSize * charScale ) );
1024 int x = KiROUND( plotX - textmarginaftersymbol * charScale - plot_diam / 2.0 );
1025 int y = KiROUND( plotY + charSize * charScale );
1026
1027 plotter->SetCurrentLineWidth( getMarkerBestPenSize( plot_diam ) );
1028 plotter->Marker( VECTOR2I( x, y ), plot_diam, ii );
1029 plotter->SetCurrentLineWidth( -1 );
1030
1031 // List the diameter of each drill in mm and inches.
1032 snprintf( line, sizeof( line ), "%3.3fmm / %2.4f\" ", diameter_in_mm( tool.m_Diameter ),
1034
1035 msg = From_UTF8( line );
1036 wxString extraInfo;
1037
1039 extraInfo += wxT( ", castellated" );
1041 extraInfo += wxT( ", press-fit" );
1042
1043 if( tool.m_IsBackdrill )
1044 {
1045 if( tool.m_MinStubLength.has_value() )
1046 {
1047 double minStub = pcbIUScale.IUTomm( *tool.m_MinStubLength );
1048
1049 if( tool.m_MaxStubLength.has_value() && tool.m_MaxStubLength != tool.m_MinStubLength )
1050 {
1051 double maxStub = pcbIUScale.IUTomm( *tool.m_MaxStubLength );
1052 extraInfo += wxString::Format( wxT( ", backdrill stub %.3f-%.3fmm" ), minStub, maxStub );
1053 }
1054 else
1055 {
1056 extraInfo += wxString::Format( wxT( ", backdrill stub %.3fmm" ), minStub );
1057 }
1058 }
1059 else
1060 {
1061 extraInfo += wxT( ", backdrill" );
1062 }
1063 }
1064
1065 if( tool.m_HasPostMachining )
1066 extraInfo += wxT( ", post-machined" );
1067
1068 wxString counts;
1069
1070 if( ( tool.m_TotalCount == 1 ) && ( tool.m_OvalCount == 0 ) )
1071 counts.Printf( wxT( "(1 hole%s)" ), extraInfo );
1072 else if( tool.m_TotalCount == 1 )
1073 counts.Printf( wxT( "(1 slot%s)" ), extraInfo );
1074 else if( tool.m_OvalCount == 0 )
1075 counts.Printf( wxT( "(%d holes%s)" ), tool.m_TotalCount, extraInfo );
1076 else if( tool.m_OvalCount == 1 )
1077 counts.Printf( wxT( "(%d holes + 1 slot%s)" ), tool.m_TotalCount - 1, extraInfo );
1078 else
1079 counts.Printf( wxT( "(%d holes + %d slots%s)" ), tool.m_TotalCount - tool.m_OvalCount, tool.m_OvalCount,
1080 extraInfo );
1081
1082 msg += counts;
1083
1084 // Backdrills carry the non-plated flag but are already called out as backdrills above
1085 if( tool.m_Hole_NotPlated && !tool.m_IsBackdrill )
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, TOOL_SUMMARY::PLATED );
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, TOOL_SUMMARY::BACKDRILL );
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, TOOL_SUMMARY::PLATED );
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, TOOL_SUMMARY::UNPLATED );
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, TOOL_SUMMARY aSummary ) 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 // Backdrills set the non-plated flag, so they must be classified before it is read
1293
1294 if( bucket != aSummary )
1295 continue;
1296
1297 // List the tool number assigned to each drill in mm then in inches.
1298 int tool_number = ii+1;
1299 fmt::print( out, " T{} {:2.3f}mm {:2.4f}\" ", tool_number,
1300 diameter_in_mm( tool.m_Diameter ),
1302
1303 // Now list how many holes and ovals are associated with each drill.
1304 if( ( tool.m_TotalCount == 1 ) && ( tool.m_OvalCount == 0 ) )
1305 fmt::print( out, "(1 hole" );
1306 else if( tool.m_TotalCount == 1 )
1307 fmt::print( out, "(1 hole) (with 1 slot" );
1308 else if( tool.m_OvalCount == 0 )
1309 fmt::print( out, "({} holes)", tool.m_TotalCount );
1310 else if( tool.m_OvalCount == 1 )
1311 fmt::print( out, "({} holes) (with 1 slot", tool.m_TotalCount );
1312 else // tool.m_OvalCount > 1
1313 fmt::print( out, "({} holes) (with {} slots", tool.m_TotalCount, tool.m_OvalCount );
1314
1316 fmt::print( out, ", castellated" );
1317
1319 fmt::print( out, ", press-fit" );
1320
1321 fmt::print( out, ")\n" );
1322
1323 totalHoleCount += tool.m_TotalCount;
1324 }
1325
1326 fmt::print( out, "\n" );
1327
1328 return totalHoleCount;
1329}
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:83
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
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
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).
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:80
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:72
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:101
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
bool IsCopperLayerLowerThan(PCB_LAYER_ID aLayerA, PCB_LAYER_ID aLayerB)
Return true if copper aLayerA is placed lower than aLayerB, false otherwise.
Definition layer_ids.h:830
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