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 (C) 2004-2022 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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <common.h>
26#include <footprint.h>
27#include <pad.h>
28#include <pcb_track.h>
31#include <drc/drc_engine.h>
32#include <drc/drc_item.h>
33#include <drc/drc_rule.h>
35#include "drc_rtree.h"
36
37/*
38 Holes clearance test. Checks pad and via holes for their mechanical clearances.
39 Generated errors:
40 - DRCE_DRILLED_HOLES_TOO_CLOSE
41 - DRCE_DRILLED_HOLES_COLOCATED
42*/
43
45{
46public:
49 m_board( nullptr ),
51 {
52 }
53
55 {
56 }
57
58 virtual bool Run() override;
59
60 virtual const wxString GetName() const override
61 {
62 return wxT( "hole_to_hole_clearance" );
63 };
64
65 virtual const wxString GetDescription() const override
66 {
67 return wxT( "Tests hole to hole spacing" );
68 }
69
70private:
71 bool testHoleAgainstHole( BOARD_ITEM* aItem, SHAPE_CIRCLE* aHole, BOARD_ITEM* aOther );
72
76};
77
78
79static std::shared_ptr<SHAPE_CIRCLE> getDrilledHoleShape( BOARD_ITEM* aItem )
80{
81 if( aItem->Type() == PCB_VIA_T )
82 {
83 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
84 return std::make_shared<SHAPE_CIRCLE>( via->GetCenter(), via->GetDrillValue() / 2 );
85 }
86 else if( aItem->Type() == PCB_PAD_T )
87 {
88 PAD* pad = static_cast<PAD*>( aItem );
89 return std::make_shared<SHAPE_CIRCLE>( pad->GetPosition(), pad->GetDrillSize().x / 2 );
90 }
91
92 return std::make_shared<SHAPE_CIRCLE>( VECTOR2I( 0, 0 ), 0 );
93}
94
95
97{
100 {
101 reportAux( wxT( "Hole to hole violations ignored. Tests not run." ) );
102 return true; // continue with other tests
103 }
104
106
107 DRC_CONSTRAINT worstClearanceConstraint;
108
109 if( m_drcEngine->QueryWorstConstraint( HOLE_TO_HOLE_CONSTRAINT, worstClearanceConstraint ) )
110 {
111 m_largestHoleToHoleClearance = worstClearanceConstraint.GetValue().Min();
112 reportAux( wxT( "Worst hole to hole : %d nm" ), m_largestHoleToHoleClearance );
113 }
114 else
115 {
116 reportAux( wxT( "No hole to hole constraints found. Skipping check." ) );
117 return true; // continue with other tests
118 }
119
120 if( !reportPhase( _( "Checking hole to hole clearances..." ) ) )
121 return false; // DRC cancelled
122
123 const size_t progressDelta = 200;
124 size_t count = 0;
125 size_t ii = 0;
126
128
130 [&]( BOARD_ITEM* item ) -> bool
131 {
132 ++count;
133 return true;
134 } );
135
136 count *= 2; // One for adding to the rtree; one for checking
137
139 [&]( BOARD_ITEM* item ) -> bool
140 {
141 if( !reportProgress( ii++, count, progressDelta ) )
142 return false;
143
144 if( item->Type() == PCB_PAD_T )
145 {
146 PAD* pad = static_cast<PAD*>( item );
147
148 // Slots are generally milled _after_ drilling, so we ignore them.
149 if( pad->GetDrillSize().x && pad->GetDrillSize().x == pad->GetDrillSize().y )
151 }
152 else if( item->Type() == PCB_VIA_T )
153 {
154 // Blind/buried/microvias will be drilled/burned _prior_ to lamination, so
155 // subsequently drilled holes need to avoid them.
157 }
158
159 return true;
160 } );
161
162 std::unordered_map<PTR_PTR_CACHE_KEY, int> checkedPairs;
163
164 for( PCB_TRACK* track : m_board->Tracks() )
165 {
166 if( track->Type() != PCB_VIA_T )
167 continue;
168
169 PCB_VIA* via = static_cast<PCB_VIA*>( track );
170
171 if( !reportProgress( ii++, count, progressDelta ) )
172 return false; // DRC cancelled
173
174 // We only care about mechanically drilled (ie: non-laser) holes. These include both
175 // blind/buried via holes (drilled prior to lamination) and through-via and drilled pad
176 // holes (which are generally drilled post laminataion).
177 if( via->GetViaType() != VIATYPE::MICROVIA )
178 {
179 std::shared_ptr<SHAPE_CIRCLE> holeShape = getDrilledHoleShape( via );
180
182 // Filter:
183 [&]( BOARD_ITEM* other ) -> bool
184 {
185 BOARD_ITEM* a = via;
186 BOARD_ITEM* b = other;
187
188 // store canonical order so we don't collide in both directions
189 // (a:b and b:a)
190 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
191 std::swap( a, b );
192
193 if( checkedPairs.find( { a, b } ) != checkedPairs.end() )
194 {
195 return false;
196 }
197 else
198 {
199 checkedPairs[ { a, b } ] = 1;
200 return true;
201 }
202 },
203 // Visitor:
204 [&]( BOARD_ITEM* other ) -> bool
205 {
206 return testHoleAgainstHole( via, holeShape.get(), other );
207 },
209 }
210 }
211
212 checkedPairs.clear();
213
214 for( FOOTPRINT* footprint : m_board->Footprints() )
215 {
216 for( PAD* pad : footprint->Pads() )
217 {
218 if( !reportProgress( ii++, count, progressDelta ) )
219 return false; // DRC cancelled
220
221 // We only care about drilled (ie: round) holes
222 if( pad->GetDrillSize().x && pad->GetDrillSize().x == pad->GetDrillSize().y )
223 {
224 std::shared_ptr<SHAPE_CIRCLE> holeShape = getDrilledHoleShape( pad );
225
226 m_holeTree.QueryColliding( pad, Edge_Cuts, Edge_Cuts,
227 // Filter:
228 [&]( BOARD_ITEM* other ) -> bool
229 {
230 BOARD_ITEM* a = pad;
231 BOARD_ITEM* b = other;
232
233 // store canonical order so we don't collide in both directions
234 // (a:b and b:a)
235 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
236 std::swap( a, b );
237
238 if( checkedPairs.find( { a, b } ) != checkedPairs.end() )
239 {
240 return false;
241 }
242 else
243 {
244 checkedPairs[ { a, b } ] = 1;
245 return true;
246 }
247 },
248 // Visitor:
249 [&]( BOARD_ITEM* other ) -> bool
250 {
251 return testHoleAgainstHole( pad, holeShape.get(), other );
252 },
253 m_largestHoleToHoleClearance );
254 }
255 }
256
257 if( m_drcEngine->IsCancelled() )
258 return false;
259 }
260
261 reportRuleStatistics();
262
263 return !m_drcEngine->IsCancelled();
264}
265
266
268 BOARD_ITEM* aOther )
269{
272
273 if( !reportCoLocation && !reportHole2Hole )
274 return false;
275
276 std::shared_ptr<SHAPE_CIRCLE> otherHole = getDrilledHoleShape( aOther );
278 SEG::ecoord epsilon_sq = SEG::Square( epsilon );
279
280 // Blind-buried or microvias that don't overlap layers aren't an issue.
281 if( aItem->Type() == PCB_VIA_T && aOther->Type() == PCB_VIA_T )
282 {
283 LSET viaHoleLayers = static_cast<PCB_VIA*>( aItem )->GetLayerSet() & LSET::AllCuMask();
284
285 if( ( viaHoleLayers & static_cast<PCB_VIA*>( aOther )->GetLayerSet() ).none() )
286 return false;
287 }
288
289 // Holes at same location generate a separate violation
290 if( ( aHole->GetCenter() - otherHole->GetCenter() ).SquaredEuclideanNorm() < epsilon_sq )
291 {
292 if( reportCoLocation )
293 {
294 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_DRILLED_HOLES_COLOCATED );
295 drce->SetItems( aItem, aOther );
296 reportViolation( drce, aHole->GetCenter(), UNDEFINED_LAYER );
297 }
298 }
299 else if( reportHole2Hole )
300 {
301 int actual = ( aHole->GetCenter() - otherHole->GetCenter() ).EuclideanNorm();
302 actual = std::max( 0, actual - aHole->GetRadius() - otherHole->GetRadius() );
303
304 auto constraint = m_drcEngine->EvalRules( HOLE_TO_HOLE_CONSTRAINT, aItem, aOther,
305 UNDEFINED_LAYER /* holes pierce all layers */ );
306 int minClearance = constraint.GetValue().Min() - epsilon;
307
308 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE
309 && minClearance >= 0
310 && actual < minClearance )
311 {
312 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_DRILLED_HOLES_TOO_CLOSE );
313 wxString msg = formatMsg( _( "(%s min %s; actual %s)" ),
314 constraint.GetName(),
315 minClearance,
316 actual );
317
318 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
319 drce->SetItems( aItem, aOther );
320 drce->SetViolatingRule( constraint.GetParentRule() );
321
322 reportViolation( drce, aHole->GetCenter(), UNDEFINED_LAYER );
323 }
324 }
325
326 return !m_drcEngine->IsCancelled();
327}
328
329
330namespace detail
331{
333}
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:77
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:281
const TRACKS & Tracks() const
Definition: board.h:320
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:797
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:141
BOARD * GetBoard() const
Definition: drc_engine.h:89
bool IsErrorLimitExceeded(int error_code)
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
Definition: drc_engine.cpp:675
bool IsCancelled() const
bool QueryWorstConstraint(DRC_CONSTRAINT_T aRuleId, DRC_CONSTRAINT &aConstraint)
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition: drc_item.cpp:331
Implement an R-tree for fast spatial and layer indexing of connectable items.
Definition: drc_rtree.h:48
void Insert(BOARD_ITEM *aItem, PCB_LAYER_ID aLayer, int aWorstClearance=0)
Insert an item into the tree on a particular layer with an optional worst clearance.
Definition: drc_rtree.h:104
int QueryColliding(BOARD_ITEM *aRefItem, PCB_LAYER_ID aRefLayer, PCB_LAYER_ID aTargetLayer, std::function< bool(BOARD_ITEM *)> aFilter=nullptr, std::function< bool(BOARD_ITEM *)> aVisitor=nullptr, int aClearance=0) const
This is a fast test which essentially does bounding-box overlap given a worst-case clearance.
Definition: drc_rtree.h:214
void clear()
Remove all items from the RTree.
Definition: drc_rtree.h:169
virtual const wxString GetName() const override
bool testHoleAgainstHole(BOARD_ITEM *aItem, SHAPE_CIRCLE *aHole, BOARD_ITEM *aOther)
virtual const wxString GetDescription() const override
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, double aConstraint, double aActual)
virtual bool reportPhase(const wxString &aStageName)
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, LSET aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer)
DRC_ENGINE * m_drcEngine
void reportAux(const wxString &aMsg)
virtual bool reportProgress(size_t aCount, size_t aSize, size_t aDelta=1)
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:100
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:574
static LSET AllLayersMask()
Definition: lset.cpp:898
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:863
T Min() const
Definition: minoptmax.h:33
Definition: pad.h:59
VECTOR2I::extended_type ecoord
Definition: seg.h:44
static SEG::ecoord Square(int a)
Definition: seg.h:123
int GetRadius() const
Definition: shape_circle.h:108
const VECTOR2I GetCenter() const
Definition: shape_circle.h:113
The common library.
@ DRCE_DRILLED_HOLES_TOO_CLOSE
Definition: drc_item.h:51
@ DRCE_DRILLED_HOLES_COLOCATED
Definition: drc_item.h:52
@ HOLE_TO_HOLE_CONSTRAINT
Definition: drc_rule.h:49
static std::shared_ptr< SHAPE_CIRCLE > getDrilledHoleShape(BOARD_ITEM *aItem)
#define _(s)
@ Edge_Cuts
Definition: layer_ids.h:113
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
@ RPT_SEVERITY_IGNORE
const double epsilon
double EuclideanNorm(const VECTOR2I &vector)
Definition: trigo.h:128
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
VECTOR2< int > VECTOR2I
Definition: vector2d.h:588