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