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