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