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
89 const BOARD_DESIGN_SETTINGS& settings = aBoard.GetDesignSettings();
90
91 auto boardSide = [&]( 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 = [&]( PCB_LAYER_ID aLayer )
97 {
98 bool fallback = boardSide( aLayer, settings.m_CoverViasFront, settings.m_CoverViasBack );
99 return aVia->Padstack().IsCovered( aLayer ).value_or( fallback );
100 };
101
102 auto plugged = [&]( PCB_LAYER_ID aLayer )
103 {
104 bool fallback = boardSide( aLayer, settings.m_PlugViasFront, settings.m_PlugViasBack );
105 return aVia->Padstack().IsPlugged( aLayer ).value_or( fallback );
106 };
107
108 auto tented = [&]( PCB_LAYER_ID aLayer )
109 {
110 bool fallback = boardSide( aLayer, settings.m_TentViasFront, settings.m_TentViasBack );
111 return aVia->Padstack().IsTented( aLayer ).value_or( fallback );
112 };
113
114 auto setProtection = [&]( DRILL_OPERATION& aOp, std::optional<bool> aFilled, std::optional<bool> aCapped )
115 {
116 aOp.m_Filled = aFilled.value_or( settings.m_FillVias );
117 aOp.m_Capped = aCapped.value_or( settings.m_CapVias );
118 aOp.m_TopCovered = covered( aOp.m_TopLayer );
119 aOp.m_BottomCovered = covered( aOp.m_BottomLayer );
120 aOp.m_TopPlugged = plugged( aOp.m_TopLayer );
121 aOp.m_BottomPlugged = plugged( aOp.m_BottomLayer );
122 aOp.m_TopTented = tented( aOp.m_TopLayer );
123 aOp.m_BottomTented = tented( aOp.m_BottomLayer );
124 };
125
126 auto stubLength =
127 [&]( PCB_LAYER_ID aStart, PCB_LAYER_ID aEnd ) -> std::optional<int>
128 {
129 if( aStart == UNDEFINED_LAYER || aEnd == UNDEFINED_LAYER )
130 return std::optional<int>();
131
132 const BOARD_STACKUP& stackup = aBoard.GetDesignSettings().GetStackupDescriptor();
133
134 return std::optional<int>( stackup.GetLayerDistance( aStart, aEnd ) );
135 };
136
137 if( span.m_IsBackdrill )
138 {
139 auto tryBackdrill =
140 [&]( const PADSTACK::DRILL_PROPS& aDrill, DRILL_OP_KIND aKind )
141 {
142 std::optional<DRILL_OPERATION> op = backdrillOperation( aDrill, aKind, span, aVia );
143
144 if( !op )
145 return;
146
147 op->m_Attribute = HOLE_ATTRIBUTE::HOLE_VIA_BACKDRILL;
148 op->m_Orientation = ANGLE_0;
149 op->m_Position = aVia->GetStart();
150 op->m_StubLength = stubLength( aDrill.start, aDrill.end );
151 // Backdrills remove the via barrel, so via surface protection is not inherited
152 op->m_Filled = aDrill.is_filled.value_or( false );
153 op->m_Capped = aDrill.is_capped.value_or( false );
154 aOut.push_back( std::move( *op ) );
155 };
156
157 tryBackdrill( aVia->Padstack().SecondaryDrill(), DRILL_OP_KIND::SECONDARY_DRILL );
158 tryBackdrill( aVia->Padstack().TertiaryDrill(), DRILL_OP_KIND::TERTIARY_DRILL );
159 return;
160 }
161
162 int holeSize = aVia->GetDrillValue();
163
164 if( holeSize == 0 )
165 return;
166
167 PCB_LAYER_ID topLayer;
168 PCB_LAYER_ID bottomLayer;
169 aVia->LayerPair( &topLayer, &bottomLayer );
170
171 if( DRILL_LAYER_PAIR( topLayer, bottomLayer ) != span.Pair()
172 && DRILL_LAYER_PAIR( bottomLayer, topLayer ) != span.Pair() )
173 {
174 return;
175 }
176
178 op.m_SourceItem = aVia;
179 op.m_SourceId = aVia->m_Uuid;
180
181 if( span.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
183 else
185
187 op.m_Diameter = holeSize;
188 op.m_NotPlated = false;
189 op.m_SizeXY = VECTOR2I( holeSize, holeSize );
190 op.m_Position = aVia->GetStart();
191 op.m_TopLayer = topLayer;
192 op.m_BottomLayer = bottomLayer;
193 setProtection( op, aVia->Padstack().IsFilled(), aVia->Padstack().IsCapped() );
194 op.m_FrontPostMachining = fromPadstack( aVia->Padstack().FrontPostMachining() );
195 op.m_BackPostMachining = fromPadstack( aVia->Padstack().BackPostMachining() );
196 op.m_DrillStart = aVia->Padstack().Drill().start;
197 op.m_DrillEnd = bottomLayer;
198
199 aOut.push_back( op );
200}
201
202
203void enumeratePad( PAD* aPad, const DRILL_QUERY& aQuery, std::vector<DRILL_OPERATION>& aOut )
204{
205 if( aQuery.m_Span.m_IsBackdrill )
206 {
207 if( !aQuery.m_PadMachining )
208 return;
209
210 auto addBackdrill = [&]( const PADSTACK::DRILL_PROPS& aDrill, DRILL_OP_KIND aKind )
211 {
212 std::optional<DRILL_OPERATION> op = backdrillOperation( aDrill, aKind, aQuery.m_Span, aPad );
213
214 if( !op )
215 return;
216
217 op->m_Attribute = HOLE_ATTRIBUTE::HOLE_PAD_BACKDRILL;
218 op->m_Position = aPad->GetPosition();
219 op->m_Orientation = aPad->GetOrientation();
220 aOut.push_back( std::move( *op ) );
221 };
222
223 addBackdrill( aPad->Padstack().SecondaryDrill(), DRILL_OP_KIND::SECONDARY_DRILL );
224 addBackdrill( aPad->Padstack().TertiaryDrill(), DRILL_OP_KIND::TERTIARY_DRILL );
225 return;
226 }
227
228 if( !aQuery.m_MergePTHNPTH )
229 {
230 if( !aQuery.m_NonPlatedOnly && aPad->GetAttribute() == PAD_ATTRIB::NPTH )
231 return;
232
233 if( aQuery.m_NonPlatedOnly && aPad->GetAttribute() != PAD_ATTRIB::NPTH )
234 return;
235 }
236
237 if( aPad->GetDrillSize().x == 0 )
238 return;
239
241 op.m_SourceItem = aPad;
242 op.m_SourceId = aPad->m_Uuid;
243 op.m_NotPlated = ( aPad->GetAttribute() == PAD_ATTRIB::NPTH );
244
245 if( op.m_NotPlated )
246 {
248 }
249 else if( aPad->GetProperty() == PAD_PROP::CASTELLATED )
250 {
252 }
253 else if( aPad->GetProperty() == PAD_PROP::PRESSFIT )
254 {
256 }
257 else
258 {
260 }
261
262 op.m_Orientation = aPad->GetOrientation();
263 op.m_Diameter = std::min( aPad->GetDrillSize().x, aPad->GetDrillSize().y );
264
265 op.m_IsSlot = IsDrillSlot( *aPad );
266
267 op.m_SizeXY = aPad->GetDrillSize();
268 op.m_Position = aPad->GetPosition();
269 op.m_BottomLayer = B_Cu;
270 op.m_TopLayer = F_Cu;
271
272 if( aQuery.m_PadMachining )
273 {
274 op.m_FrontPostMachining = fromPadstack( aPad->Padstack().FrontPostMachining() );
275 op.m_BackPostMachining = fromPadstack( aPad->Padstack().BackPostMachining() );
276 }
277
278 aOut.push_back( op );
279}
280
281} // namespace
282
283
284bool IsDrillSlot( const PAD& aPad )
285{
286 return aPad.HasHole() && aPad.GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE
287 && aPad.GetDrillSizeX() != aPad.GetDrillSizeY();
288}
289
290
291std::vector<DRILL_OPERATION> EnumerateDrillOperations( const BOARD& aBoard, const DRILL_QUERY& aQuery )
292{
293 std::vector<DRILL_OPERATION> operations;
294
295 wxASSERT( IsCopperLayerLowerThan( aQuery.m_Span.BottomLayer(), aQuery.m_Span.TopLayer() ) );
296
297 if( !aQuery.m_NonPlatedOnly )
298 {
299 for( PCB_TRACK* track : aBoard.Tracks() )
300 {
301 if( track->Type() != PCB_VIA_T )
302 continue;
303
304 enumerateVia( static_cast<PCB_VIA*>( track ), aBoard, aQuery, operations );
305 }
306 }
307
308 if( ( aQuery.m_Span.m_IsBackdrill && aQuery.m_PadMachining )
309 || ( !aQuery.m_Span.m_IsBackdrill && aQuery.m_Span.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) ) )
310 {
311 for( FOOTPRINT* footprint : aBoard.Footprints() )
312 {
313 for( PAD* pad : footprint->Pads() )
314 enumeratePad( pad, aQuery, operations );
315 }
316 }
317
318 return operations;
319}
320
321
322std::vector<DRILL_SPAN> EnumerateDrillSpans( const BOARD& aBoard, bool aPadBackdrills )
323{
324 std::set<DRILL_SPAN> unique;
325
326 auto addBackdrill = [&]( const PADSTACK::DRILL_PROPS& aDrill )
327 {
328 if( isBackdrillSet( aDrill ) )
329 {
330 unique.emplace( aDrill.start, aDrill.end, true, false );
331 }
332 };
333
334 for( PCB_TRACK* track : aBoard.Tracks() )
335 {
336 if( track->Type() != PCB_VIA_T )
337 continue;
338
339 PCB_VIA* via = static_cast<PCB_VIA*>( track );
341 PCB_LAYER_ID bottom;
342 via->LayerPair( &top, &bottom );
343
344 if( DRILL_LAYER_PAIR( top, bottom ) != DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
345 unique.emplace( top, bottom, false, false );
346
347 addBackdrill( via->Padstack().SecondaryDrill() );
348 addBackdrill( via->Padstack().TertiaryDrill() );
349 }
350
351 if( aPadBackdrills )
352 {
353 for( FOOTPRINT* footprint : aBoard.Footprints() )
354 {
355 for( PAD* pad : footprint->Pads() )
356 {
357 addBackdrill( pad->Padstack().SecondaryDrill() );
358 addBackdrill( pad->Padstack().TertiaryDrill() );
359 }
360 }
361 }
362
363 std::vector<DRILL_SPAN> spans;
364 spans.emplace_back( F_Cu, B_Cu, false, false );
365
366 for( const DRILL_SPAN& span : unique )
367 {
368 if( span.m_IsBackdrill || span.Pair() != DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
369 spans.push_back( span );
370 }
371
372 return spans;
373}
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: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.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
const FOOTPRINTS & Footprints() const
Definition board.h:464
const TRACKS & Tracks() const
Definition board.h:462
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1301
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:365
std::optional< bool > IsPlugged(PCB_LAYER_ID aSide) const
DRILL_PROPS & TertiaryDrill()
Definition padstack.h:362
DRILL_PROPS & Drill()
Definition padstack.h:356
std::optional< bool > IsCapped() const
std::optional< bool > IsCovered(PCB_LAYER_ID aSide) const
DRILL_PROPS & SecondaryDrill()
Definition padstack.h:359
POST_MACHINING_PROPS & BackPostMachining()
Definition padstack.h:368
Definition pad.h:61
PAD_PROP GetProperty() const
Definition pad.h:563
int GetDrillSizeY() const
Definition pad.h:324
PAD_ATTRIB GetAttribute() const
Definition pad.h:560
VECTOR2I GetPosition() const override
Definition pad.cpp:256
VECTOR2I GetDrillSize() const
Definition pad.h:320
int GetDrillSizeX() const
Definition pad.h:322
const PADSTACK & Padstack() const
Definition pad.h:331
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition pad.cpp:1757
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:427
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:850
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