KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drill_enumerator.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <board.h>
24#include <footprint.h>
25#include <pad.h>
26#include <pcb_track.h>
27
28#include <set>
29#include <utility>
30
31
32namespace
33{
34
35DRILL_POST_MACHINING fromPadstack( const PADSTACK::POST_MACHINING_PROPS& aProps )
36{
38
40 pm.m_Size = aProps.size;
41 pm.m_Depth = aProps.depth;
42 pm.m_Angle = aProps.angle;
43
44 return pm;
45}
46
47
48bool isBackdrillSet( const PADSTACK::DRILL_PROPS& aDrill )
49{
50 return aDrill.start != UNDEFINED_LAYER && aDrill.end != UNDEFINED_LAYER
51 && ( aDrill.size.x > 0 || aDrill.size.y > 0 );
52}
53
54
55std::optional<DRILL_OPERATION> backdrillOperation( const PADSTACK::DRILL_PROPS& aDrill, DRILL_OP_KIND aKind,
56 const DRILL_SPAN& aSpan, BOARD_ITEM* aItem )
57{
58 if( !isBackdrillSet( aDrill ) || aDrill.start != aSpan.DrillStartLayer()
59 || aDrill.end != aSpan.DrillEndLayer() )
60 {
61 return std::nullopt;
62 }
63
65 op.m_Kind = aKind;
66 op.m_SourceItem = aItem;
67 op.m_SourceId = aItem->m_Uuid;
68 op.m_NotPlated = true;
69 op.m_TopLayer = aSpan.TopLayer();
70 op.m_BottomLayer = aSpan.BottomLayer();
71 op.m_DrillStart = aDrill.start;
72 op.m_DrillEnd = aDrill.end;
73 op.m_SizeXY = aDrill.size;
74 op.m_Diameter = aDrill.size.x;
75
76 if( aDrill.size.y > 0 )
77 op.m_Diameter = op.m_Diameter > 0 ? std::min( op.m_Diameter, aDrill.size.y ) : aDrill.size.y;
78
79 op.m_IsSlot = aDrill.shape != PAD_DRILL_SHAPE::CIRCLE && aDrill.size.x != aDrill.size.y;
80 return op;
81}
82
83
84void enumerateVia( PCB_VIA* aVia, const BOARD& aBoard, const DRILL_QUERY& aQuery,
85 std::vector<DRILL_OPERATION>& aOut )
86{
87 const DRILL_SPAN& span = aQuery.m_Span;
88 const BOARD_DESIGN_SETTINGS& bds = aBoard.GetDesignSettings();
89
90 auto boardSide =
91 [&]( PCB_LAYER_ID aLayer, bool aFront, bool aBack )
92 {
93 return aLayer == F_Cu ? aFront : aLayer == B_Cu ? aBack : false;
94 };
95
96 auto covered =
97 [&]( PCB_LAYER_ID aLayer )
98 {
99 bool fallback = boardSide( aLayer, bds.m_CoverViasFront, bds.m_CoverViasBack );
100 return aVia->Padstack().IsCovered( aLayer ).value_or( fallback );
101 };
102
103 auto plugged =
104 [&]( PCB_LAYER_ID aLayer )
105 {
106 bool fallback = boardSide( aLayer, bds.m_PlugViasFront, bds.m_PlugViasBack );
107 return aVia->Padstack().IsPlugged( aLayer ).value_or( fallback );
108 };
109
110 auto tented =
111 [&]( PCB_LAYER_ID aLayer )
112 {
113 bool fallback = boardSide( aLayer, bds.m_TentViasFront, bds.m_TentViasBack );
114 return aVia->Padstack().IsTented( aLayer ).value_or( fallback );
115 };
116
117 auto setProtection =
118 [&]( DRILL_OPERATION& aOp, std::optional<bool> aFilled, std::optional<bool> aCapped )
119 {
120 aOp.m_Filled = aFilled.value_or( bds.m_FillVias );
121 aOp.m_Capped = aCapped.value_or( bds.m_CapVias );
122 aOp.m_TopCovered = covered( aOp.m_TopLayer );
123 aOp.m_BottomCovered = covered( aOp.m_BottomLayer );
124 aOp.m_TopPlugged = plugged( aOp.m_TopLayer );
125 aOp.m_BottomPlugged = plugged( aOp.m_BottomLayer );
126 aOp.m_TopTented = tented( aOp.m_TopLayer );
127 aOp.m_BottomTented = tented( aOp.m_BottomLayer );
128 };
129
130 auto stubLength =
131 [&]( PCB_LAYER_ID aStart, PCB_LAYER_ID aEnd ) -> std::optional<int>
132 {
133 if( aStart == UNDEFINED_LAYER || aEnd == UNDEFINED_LAYER )
134 return std::optional<int>();
135
136 const BOARD_STACKUP& stackup = aBoard.GetDesignSettings().GetStackupDescriptor();
137
138 return std::optional<int>( stackup.GetDrillDepth( aStart, aEnd ) );
139 };
140
141 if( span.m_IsBackdrill )
142 {
143 auto tryBackdrill =
144 [&]( const PADSTACK::DRILL_PROPS& aDrill, DRILL_OP_KIND aKind )
145 {
146 std::optional<DRILL_OPERATION> op = backdrillOperation( aDrill, aKind, span, aVia );
147
148 if( !op )
149 return;
150
151 op->m_Attribute = HOLE_ATTRIBUTE::HOLE_VIA_BACKDRILL;
152 op->m_Orientation = ANGLE_0;
153 op->m_Position = aVia->GetStart();
154 op->m_StubLength = stubLength( aDrill.start, aDrill.end );
155 // Backdrills remove the via barrel, so via surface protection is not inherited
156 op->m_Filled = aDrill.is_filled.value_or( false );
157 op->m_Capped = aDrill.is_capped.value_or( false );
158 aOut.push_back( std::move( *op ) );
159 };
160
161 tryBackdrill( aVia->Padstack().SecondaryDrill(), DRILL_OP_KIND::SECONDARY_DRILL );
162 tryBackdrill( aVia->Padstack().TertiaryDrill(), DRILL_OP_KIND::TERTIARY_DRILL );
163 return;
164 }
165
166 int holeSize = aVia->GetDrillValue();
167
168 if( holeSize == 0 )
169 return;
170
171 PCB_LAYER_ID topLayer;
172 PCB_LAYER_ID bottomLayer;
173 aVia->LayerPair( &topLayer, &bottomLayer );
174
175 if( DRILL_LAYER_PAIR( topLayer, bottomLayer ) != span.Pair()
176 && DRILL_LAYER_PAIR( bottomLayer, topLayer ) != span.Pair() )
177 {
178 return;
179 }
180
182 op.m_SourceItem = aVia;
183 op.m_SourceId = aVia->m_Uuid;
184
185 if( span.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
187 else
189
191 op.m_Diameter = holeSize;
192 op.m_NotPlated = false;
193 op.m_SizeXY = VECTOR2I( holeSize, holeSize );
194 op.m_Position = aVia->GetStart();
195 op.m_TopLayer = topLayer;
196 op.m_BottomLayer = bottomLayer;
197 setProtection( op, aVia->Padstack().IsFilled(), aVia->Padstack().IsCapped() );
198 op.m_FrontPostMachining = fromPadstack( aVia->Padstack().FrontPostMachining() );
199 op.m_BackPostMachining = fromPadstack( aVia->Padstack().BackPostMachining() );
200 op.m_DrillStart = aVia->Padstack().Drill().start;
201 op.m_DrillEnd = bottomLayer;
202
203 aOut.push_back( op );
204}
205
206
207void enumeratePad( PAD* aPad, const DRILL_QUERY& aQuery, std::vector<DRILL_OPERATION>& aOut )
208{
209 if( aQuery.m_Span.m_IsBackdrill )
210 {
211 if( !aQuery.m_PadMachining )
212 return;
213
214 auto addBackdrill = [&]( const PADSTACK::DRILL_PROPS& aDrill, DRILL_OP_KIND aKind )
215 {
216 std::optional<DRILL_OPERATION> op = backdrillOperation( aDrill, aKind, aQuery.m_Span, aPad );
217
218 if( !op )
219 return;
220
221 op->m_Attribute = HOLE_ATTRIBUTE::HOLE_PAD_BACKDRILL;
222 op->m_Position = aPad->GetPosition();
223 op->m_Orientation = aPad->GetOrientation();
224 aOut.push_back( std::move( *op ) );
225 };
226
227 addBackdrill( aPad->Padstack().SecondaryDrill(), DRILL_OP_KIND::SECONDARY_DRILL );
228 addBackdrill( aPad->Padstack().TertiaryDrill(), DRILL_OP_KIND::TERTIARY_DRILL );
229 return;
230 }
231
232 if( !aQuery.m_MergePTHNPTH )
233 {
234 if( !aQuery.m_NonPlatedOnly && aPad->GetAttribute() == PAD_ATTRIB::NPTH )
235 return;
236
237 if( aQuery.m_NonPlatedOnly && aPad->GetAttribute() != PAD_ATTRIB::NPTH )
238 return;
239 }
240
241 if( aPad->GetDrillSize().x == 0 )
242 return;
243
245 op.m_SourceItem = aPad;
246 op.m_SourceId = aPad->m_Uuid;
247 op.m_NotPlated = ( aPad->GetAttribute() == PAD_ATTRIB::NPTH );
248
249 if( op.m_NotPlated )
250 {
252 }
253 else if( aPad->GetProperty() == PAD_PROP::CASTELLATED )
254 {
256 }
257 else if( aPad->GetProperty() == PAD_PROP::PRESSFIT )
258 {
260 }
261 else
262 {
264 }
265
266 op.m_Orientation = aPad->GetOrientation();
267 op.m_Diameter = std::min( aPad->GetDrillSize().x, aPad->GetDrillSize().y );
268
269 op.m_IsSlot = IsDrillSlot( *aPad );
270
271 op.m_SizeXY = aPad->GetDrillSize();
272 op.m_Position = aPad->GetPosition();
273 op.m_BottomLayer = B_Cu;
274 op.m_TopLayer = F_Cu;
275
276 if( aQuery.m_PadMachining )
277 {
278 op.m_FrontPostMachining = fromPadstack( aPad->Padstack().FrontPostMachining() );
279 op.m_BackPostMachining = fromPadstack( aPad->Padstack().BackPostMachining() );
280 }
281
282 aOut.push_back( op );
283}
284
285} // namespace
286
287
288bool IsDrillSlot( const PAD& aPad )
289{
290 return aPad.HasHole()
292 && aPad.GetDrillSize().x != aPad.GetDrillSize().y;
293}
294
295
296std::vector<DRILL_OPERATION> EnumerateDrillOperations( const BOARD& aBoard, const DRILL_QUERY& aQuery )
297{
298 std::vector<DRILL_OPERATION> operations;
299
300 wxASSERT( IsCopperLayerLowerThan( aQuery.m_Span.BottomLayer(), aQuery.m_Span.TopLayer() )
301 || aQuery.m_Span.BottomLayer() == aQuery.m_Span.TopLayer() );
302
303 if( !aQuery.m_NonPlatedOnly )
304 {
305 for( PCB_TRACK* track : aBoard.Tracks() )
306 {
307 if( track->Type() != PCB_VIA_T )
308 continue;
309
310 enumerateVia( static_cast<PCB_VIA*>( track ), aBoard, aQuery, operations );
311 }
312 }
313
314 if( ( aQuery.m_Span.m_IsBackdrill && aQuery.m_PadMachining )
315 || ( !aQuery.m_Span.m_IsBackdrill && aQuery.m_Span.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) ) )
316 {
317 for( FOOTPRINT* footprint : aBoard.Footprints() )
318 {
319 for( PAD* pad : footprint->Pads() )
320 enumeratePad( pad, aQuery, operations );
321 }
322 }
323
324 return operations;
325}
326
327
328std::vector<DRILL_SPAN> EnumerateDrillSpans( const BOARD& aBoard, bool aPadBackdrills )
329{
330 std::set<DRILL_SPAN> unique;
331
332 auto addBackdrill =
333 [&]( const PADSTACK::DRILL_PROPS& aDrill )
334 {
335 if( isBackdrillSet( aDrill ) )
336 unique.emplace( aDrill.start, aDrill.end, true, false );
337 };
338
339 for( PCB_TRACK* track : aBoard.Tracks() )
340 {
341 if( track->Type() != PCB_VIA_T )
342 continue;
343
344 PCB_VIA* via = static_cast<PCB_VIA*>( track );
346 PCB_LAYER_ID bottom;
347 via->LayerPair( &top, &bottom );
348
349 if( DRILL_LAYER_PAIR( top, bottom ) != DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
350 unique.emplace( top, bottom, false, false );
351
352 addBackdrill( via->Padstack().SecondaryDrill() );
353 addBackdrill( via->Padstack().TertiaryDrill() );
354 }
355
356 if( aPadBackdrills )
357 {
358 for( FOOTPRINT* footprint : aBoard.Footprints() )
359 {
360 for( PAD* pad : footprint->Pads() )
361 {
362 addBackdrill( pad->Padstack().SecondaryDrill() );
363 addBackdrill( pad->Padstack().TertiaryDrill() );
364 }
365 }
366 }
367
368 std::vector<DRILL_SPAN> spans;
369 spans.emplace_back( F_Cu, B_Cu, false, false );
370
371 for( const DRILL_SPAN& span : unique )
372 {
373 if( span.m_IsBackdrill || span.Pair() != DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
374 spans.push_back( span );
375 }
376
377 return spans;
378}
Container for design settings for a BOARD object.
BOARD_STACKUP & GetStackupDescriptor()
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:85
Manage layers needed to make a physical board.
int GetDrillDepth(PCB_LAYER_ID aFirstLayer, PCB_LAYER_ID aSecondLayer) const
Calculate the drill depth needed to cut from aFirstLayer /through/ aSecondLayer.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:411
const FOOTPRINTS & Footprints() const
Definition board.h:465
const TRACKS & Tracks() const
Definition board.h:463
BOARD_DESIGN_SETTINGS & GetDesignSettings()
Definition board.cpp:1290
const KIID m_Uuid
Definition eda_item.h:599
std::optional< bool > IsFilled() const
std::optional< bool > IsTented(PCB_LAYER_ID aSide) const
Checks if this padstack is tented (covered in soldermask) on the given side.
POST_MACHINING_PROPS & FrontPostMachining()
Definition padstack.h:367
std::optional< bool > IsPlugged(PCB_LAYER_ID aSide) const
DRILL_PROPS & TertiaryDrill()
Definition padstack.h:364
DRILL_PROPS & Drill()
Definition padstack.h:358
std::optional< bool > IsCapped() const
std::optional< bool > IsCovered(PCB_LAYER_ID aSide) const
DRILL_PROPS & SecondaryDrill()
Definition padstack.h:361
POST_MACHINING_PROPS & BackPostMachining()
Definition padstack.h:370
Definition pad.h:61
PAD_PROP GetProperty() const
Definition pad.h:563
PAD_ATTRIB GetAttribute() const
Definition pad.h:560
VECTOR2I GetPosition() const override
Definition pad.cpp:255
VECTOR2I GetDrillSize() const
Definition pad.h:320
const PADSTACK & Padstack() const
Definition pad.h:331
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition pad.cpp:1795
PAD_DRILL_SHAPE GetDrillShape() const
Definition pad.h:434
bool HasHole() const override
Definition pad.h:113
const VECTOR2I & GetStart() const
Definition pcb_track.h:98
const PADSTACK & Padstack() const
Definition pcb_track.h:431
int GetDrillValue() const
Calculate the drill value for vias (m_drill if > 0, or default drill value for the board).
void LayerPair(PCB_LAYER_ID *top_layer, PCB_LAYER_ID *bottom_layer) const
Return the 2 layers used by the via (the via actually uses all layers between these 2 layers)
bool IsDrillSlot(const PAD &aPad)
Whether a pad's hole is a slot rather than a round drill.
std::vector< DRILL_SPAN > EnumerateDrillSpans(const BOARD &aBoard, bool aPadBackdrills)
Every drill span present on the board, through-holes first.
std::vector< DRILL_OPERATION > EnumerateDrillOperations(const BOARD &aBoard, const DRILL_QUERY &aQuery)
The one place that decides what the board's holes are.
DRILL_OP_KIND
Which of the padstack's drill props produced the operation.
std::pair< PCB_LAYER_ID, PCB_LAYER_ID > DRILL_LAYER_PAIR
Definition drill_span.h:49
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:448
bool IsCopperLayerLowerThan(PCB_LAYER_ID aLayerA, PCB_LAYER_ID aLayerB)
Return true if copper aLayerA is placed lower than aLayerB, false otherwise.
Definition layer_ids.h:888
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ B_Cu
Definition layer_ids.h:61
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ F_Cu
Definition layer_ids.h:60
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:102
@ PRESSFIT
a PTH with a hole diameter with tight tolerances for press fit pin
Definition padstack.h:122
@ CASTELLATED
a pad with a castellated through hole
Definition padstack.h:120
One machining action, which is also one NC hit and one tool assignment.
DRILL_OP_KIND m_Kind
DRILL_POST_MACHINING m_BackPostMachining
PCB_LAYER_ID m_DrillEnd
PCB_LAYER_ID m_TopLayer
PCB_LAYER_ID m_BottomLayer
VECTOR2I m_SizeXY
Source axes, never normalized to (width, length).
BOARD_ITEM * m_SourceItem
non-owning, valid only for this enumeration
EDA_ANGLE m_Orientation
DRILL_POST_MACHINING m_FrontPostMachining
HOLE_ATTRIBUTE m_Attribute
PCB_LAYER_ID m_DrillStart
int m_Size
int m_Depth
int m_Angle
PAD_DRILL_POST_MACHINING_MODE m_Mode
Selects which operations EnumerateDrillOperations() returns.
bool m_NonPlatedOnly
Emit only non-plated holes.
bool m_MergePTHNPTH
Emit plated and non-plated together, as the merged drill file does.
DRILL_SPAN m_Span
bool m_PadMachining
Include pad backdrills and post-machining, kept off for NC drill output.
DRILL_LAYER_PAIR Pair() const
Definition drill_span.h:93
PCB_LAYER_ID DrillEndLayer() const
Definition drill_span.h:88
PCB_LAYER_ID TopLayer() const
Definition drill_span.h:71
PCB_LAYER_ID DrillStartLayer() const
Definition drill_span.h:83
PCB_LAYER_ID BottomLayer() const
Definition drill_span.h:78
bool m_IsBackdrill
Definition drill_span.h:130
The properties of a padstack drill.
Definition padstack.h:272
PCB_LAYER_ID start
Definition padstack.h:275
PCB_LAYER_ID end
Definition padstack.h:276
VECTOR2I size
Drill diameter (x == y) or slot dimensions (x != y)
Definition padstack.h:273
std::optional< bool > is_capped
True if the drill hole should be capped.
Definition padstack.h:279
std::optional< bool > is_filled
True if the drill hole should be filled completely.
Definition padstack.h:278
PAD_DRILL_SHAPE shape
Definition padstack.h:274
std::optional< PAD_DRILL_POST_MACHINING_MODE > mode
Definition padstack.h:287
KIBIS top(path, &reporter)
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708