KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drc_test_provider_hole_to_hole.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.
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
20#include <common.h>
22#include <footprint.h>
23#include <pad.h>
24#include <pcb_track.h>
26#include <drc/drc_engine.h>
27#include <drc/drc_item.h>
28#include <drc/drc_rule.h>
30#include "drc_rtree.h"
31
32/*
33 Holes clearance test. Checks pad and via holes for their mechanical clearances.
34 Generated errors:
35 - DRCE_DRILLED_HOLES_TOO_CLOSE
36 - DRCE_DRILLED_HOLES_COLOCATED
37*/
38
40{
41public:
47
49
50 virtual bool Run() override;
51
52 virtual const wxString GetName() const override { return wxT( "hole_to_hole_clearance" ); };
53
54private:
55 bool testHoleAgainstHole( BOARD_ITEM* aItem, SHAPE_SEGMENT* aHole, BOARD_ITEM* aOther );
56
60};
61
62
63static std::shared_ptr<SHAPE_SEGMENT> getHoleShape( BOARD_ITEM* aItem )
64{
65 if( aItem->Type() == PCB_VIA_T )
66 {
67 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
68 return std::make_shared<SHAPE_SEGMENT>( via->GetCenter(), via->GetCenter(),
69 via->GetDrillValue() );
70 }
71 else if( aItem->Type() == PCB_PAD_T )
72 {
73 // Round drills come back as a zero-length segment; oval (slotted) holes carry the
74 // slot axis, so the same shape models both round holes and milled slots exactly.
75 return static_cast<PAD*>( aItem )->GetEffectiveHoleShape();
76 }
77
78 return std::make_shared<SHAPE_SEGMENT>( VECTOR2I( 0, 0 ), VECTOR2I( 0, 0 ), 0 );
79}
80
81
83{
84 if( m_drcEngine->IsErrorLimitExceeded( DRCE_DRILLED_HOLES_TOO_CLOSE )
85 && m_drcEngine->IsErrorLimitExceeded( DRCE_DRILLED_HOLES_COLOCATED ) )
86 {
87 REPORT_AUX( wxT( "Hole to hole violations ignored. Tests not run." ) );
88 return true; // continue with other tests
89 }
90
91 m_board = m_drcEngine->GetBoard();
92
93 DRC_CONSTRAINT worstClearanceConstraint;
94
95 if( m_drcEngine->QueryWorstConstraint( HOLE_TO_HOLE_CONSTRAINT, worstClearanceConstraint ) )
96 {
97 m_largestHoleToHoleClearance = worstClearanceConstraint.GetValue().Min();
98 }
99 else
100 {
101 REPORT_AUX( wxT( "No hole to hole constraints found. Skipping check." ) );
102 return true; // continue with other tests
103 }
104
105 if( !reportPhase( _( "Checking hole to hole clearances..." ) ) )
106 return false; // DRC cancelled
107
108 const size_t progressDelta = 200;
109 size_t count = 0;
110 size_t ii = 0;
111
112 m_holeTree.clear();
113
115 [&]( BOARD_ITEM* item ) -> bool
116 {
117 ++count;
118 return true;
119 } );
120
121 count *= 2; // One for adding to the rtree; one for checking
122
124 [&]( BOARD_ITEM* item ) -> bool
125 {
126 if( !reportProgress( ii++, count, progressDelta ) )
127 return false;
128
129 if( item->Type() == PCB_PAD_T )
130 {
131 PAD* pad = static_cast<PAD*>( item );
132
133 // Index every drilled or milled hole, including oval (slotted) holes. A
134 // slot too close to another hole or slot is still a manufacturing defect.
135 if( pad->HasHole() )
137 }
138 else if( item->Type() == PCB_VIA_T )
139 {
140 // Blind/buried/microvias will be drilled/burned _prior_ to lamination, so
141 // subsequently drilled holes need to avoid them.
143 }
144
145 return true;
146 } );
147
148 m_holeTree.Build();
149
150 std::unordered_map<PTR_PTR_CACHE_KEY, int> checkedPairs;
151
152 for( PCB_TRACK* track : m_board->Tracks() )
153 {
154 if( track->Type() != PCB_VIA_T )
155 continue;
156
157 PCB_VIA* via = static_cast<PCB_VIA*>( track );
158
159 if( !reportProgress( ii++, count, progressDelta ) )
160 return false; // DRC cancelled
161
162 // We only care about mechanically drilled (ie: non-laser) holes. These include both
163 // blind/buried via holes (drilled prior to lamination) and through-via and drilled pad
164 // holes (which are generally drilled post laminataion).
165 if( via->GetViaType() != VIATYPE::MICROVIA )
166 {
167 std::shared_ptr<SHAPE_SEGMENT> holeShape = getHoleShape( via );
168
169 m_holeTree.QueryColliding( via, Edge_Cuts, Edge_Cuts,
170 // Filter:
171 [&]( BOARD_ITEM* other ) -> bool
172 {
173 BOARD_ITEM* a = via;
174 BOARD_ITEM* b = other;
175
176 // store canonical order so we don't collide in both directions
177 // (a:b and b:a)
178 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
179 std::swap( a, b );
180
181 if( checkedPairs.find( { a, b } ) != checkedPairs.end() )
182 {
183 return false;
184 }
185 else
186 {
187 checkedPairs[ { a, b } ] = 1;
188 return true;
189 }
190 },
191 // Visitor:
192 [&]( BOARD_ITEM* other ) -> bool
193 {
194 return testHoleAgainstHole( via, holeShape.get(), other );
195 },
197 }
198 }
199
200 // Keep the same checkedPairs across both passes so a via/pad pair tested in the via pass
201 // above is not reported a second time when the pad queries the via below.
202
203 for( FOOTPRINT* footprint : m_board->Footprints() )
204 {
205 for( PAD* pad : footprint->Pads() )
206 {
207 if( !reportProgress( ii++, count, progressDelta ) )
208 return false; // DRC cancelled
209
210 // Test every drilled or milled hole, including oval (slotted) holes
211 if( pad->HasHole() )
212 {
213 std::shared_ptr<SHAPE_SEGMENT> holeShape = getHoleShape( pad );
214
215 m_holeTree.QueryColliding( pad, Edge_Cuts, Edge_Cuts,
216 // Filter:
217 [&]( BOARD_ITEM* other ) -> bool
218 {
219 BOARD_ITEM* a = pad;
220 BOARD_ITEM* b = other;
221
222 // store canonical order so we don't collide in both directions
223 // (a:b and b:a)
224 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
225 std::swap( a, b );
226
227 if( checkedPairs.find( { a, b } ) != checkedPairs.end() )
228 {
229 return false;
230 }
231 else
232 {
233 checkedPairs[ { a, b } ] = 1;
234 return true;
235 }
236 },
237 // Visitor:
238 [&]( BOARD_ITEM* other ) -> bool
239 {
240 return testHoleAgainstHole( pad, holeShape.get(), other );
241 },
243 }
244 }
245
246 if( m_drcEngine->IsCancelled() )
247 return false;
248 }
249
250 return !m_drcEngine->IsCancelled();
251}
252
253
255 BOARD_ITEM* aOther )
256{
257 bool reportCoLocation = !m_drcEngine->IsErrorLimitExceeded( DRCE_DRILLED_HOLES_COLOCATED );
258 bool reportHole2Hole = !m_drcEngine->IsErrorLimitExceeded( DRCE_DRILLED_HOLES_TOO_CLOSE );
259
260 if( !reportCoLocation && !reportHole2Hole )
261 return false;
262
263 std::shared_ptr<SHAPE_SEGMENT> otherHole = getHoleShape( aOther );
264 int epsilon = m_board->GetDesignSettings().GetDRCEpsilon();
265 SEG::ecoord epsilon_sq = SEG::Square( epsilon );
266
267 // Blind-buried vias are drilled prior to stackup; they're only an issue if they share layers
268 if( aItem->Type() == PCB_VIA_T && aOther->Type() == PCB_VIA_T )
269 {
270 LSET viaHoleLayers = static_cast<PCB_VIA*>( aItem )->GetLayerSet() & LSET::AllCuMask();
271
272 if( ( viaHoleLayers & static_cast<PCB_VIA*>( aOther )->GetLayerSet() ).none() )
273 return false;
274 }
275
276 // Holes at same location generate a separate violation
277 if( ( aHole->GetCenter() - otherHole->GetCenter() ).SquaredEuclideanNorm() < epsilon_sq )
278 {
279 if( reportCoLocation )
280 {
281 // Generate violations based on a well-defined order so that exclusion checking
282 // against previously-generated violations will work.
283 if( aItem->m_Uuid > aOther->m_Uuid )
284 std::swap( aItem, aOther );
285
286 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_DRILLED_HOLES_COLOCATED );
287 drcItem->SetItems( aItem, aOther );
288 reportTwoPointGeometry( drcItem, aHole->GetCenter(), aHole->GetCenter(), aHole->GetCenter(),
290 }
291 }
292 else if( reportHole2Hole )
293 {
294 // Measure between the hole axes, then back off the two half-widths. For a round hole
295 // the segment is zero-length and its width is the drill diameter, so this reduces to
296 // the centre-to-centre distance less the two radii; for a slot it follows the milled
297 // oval correctly.
298 int actual = aHole->GetSeg().Distance( otherHole->GetSeg() );
299 actual = std::max( 0, actual - aHole->GetWidth() / 2 - otherHole->GetWidth() / 2 );
300
301 auto constraint = m_drcEngine->EvalRules( HOLE_TO_HOLE_CONSTRAINT, aItem, aOther,
302 UNDEFINED_LAYER /* holes pierce all layers */ );
303 int minClearance = std::max( 0, constraint.GetValue().Min() - epsilon );
304
305 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE
306 && minClearance >= 0
307 && actual < minClearance )
308 {
309 // Generate violations based on a well-defined order so that exclusion checking
310 // against previously-generated violations will work.
311 if( aItem->m_Uuid > aOther->m_Uuid )
312 std::swap( aItem, aOther );
313
314 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_DRILLED_HOLES_TOO_CLOSE );
315 drcItem->SetErrorDetail( formatMsg( _( "(%s min %s; actual %s)" ),
316 constraint.GetName(),
317 minClearance,
318 actual ) );
319 drcItem->SetItems( aItem, aOther );
320 drcItem->SetViolatingRule( constraint.GetParentRule() );
321 reportTwoShapeGeometry( drcItem, aHole->GetCenter(), aHole, otherHole.get(), UNDEFINED_LAYER, actual );
322 }
323 }
324
325 return !m_drcEngine->IsCancelled();
326}
327
328
329namespace detail
330{
332}
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:81
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:372
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:196
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:417
Implement an R-tree for fast spatial and layer indexing of connectable items.
Definition drc_rtree.h:45
virtual const wxString GetName() const override
virtual ~DRC_TEST_PROVIDER_HOLE_TO_HOLE()=default
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
bool testHoleAgainstHole(BOARD_ITEM *aItem, SHAPE_SEGMENT *aHole, BOARD_ITEM *aOther)
virtual bool reportPhase(const wxString &aStageName)
void reportTwoShapeGeometry(std::shared_ptr< DRC_ITEM > &aDrcItem, const VECTOR2I &aMarkerPos, const SHAPE *aShape1, const SHAPE *aShape2, PCB_LAYER_ID aLayer, int aDistance)
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, const LSET &aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
void reportTwoPointGeometry(std::shared_ptr< DRC_ITEM > &aDrcItem, const VECTOR2I &aMarkerPos, const VECTOR2I &ptA, const VECTOR2I &ptB, PCB_LAYER_ID aLayer)
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, double aConstraint, double aActual, EDA_DATA_TYPE aDataType=EDA_DATA_TYPE::DISTANCE)
virtual bool reportProgress(size_t aCount, size_t aSize, size_t aDelta=1)
const KIID m_Uuid
Definition eda_item.h:531
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:604
static const LSET & AllLayersMask()
Definition lset.cpp:637
T Min() const
Definition minoptmax.h:29
Definition pad.h:61
VECTOR2I::extended_type ecoord
Definition seg.h:40
static SEG::ecoord Square(int a)
Definition seg.h:119
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition seg.cpp:698
const SEG & GetSeg() const
int GetWidth() const override
VECTOR2I GetCenter() const
The common library.
@ DRCE_DRILLED_HOLES_TOO_CLOSE
Definition drc_item.h:49
@ DRCE_DRILLED_HOLES_COLOCATED
Definition drc_item.h:50
@ HOLE_TO_HOLE_CONSTRAINT
Definition drc_rule.h:54
#define REPORT_AUX(s)
static std::shared_ptr< SHAPE_SEGMENT > getHoleShape(BOARD_ITEM *aItem)
#define _(s)
@ Edge_Cuts
Definition layer_ids.h:108
@ UNDEFINED_LAYER
Definition layer_ids.h:57
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
@ RPT_SEVERITY_IGNORE
const double epsilon
int actual
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:90
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:80
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683