KiCad PCB EDA Suite
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 // We only care about drilled (ie: round) holes
149 if( pad->GetDrillSize().x && pad->GetDrillSize().x == pad->GetDrillSize().y )
151 }
152 else if( item->Type() == PCB_VIA_T )
153 {
154 PCB_VIA* via = static_cast<PCB_VIA*>( item );
155
156 // We only care about mechanically drilled (ie: non-laser) holes
157 if( via->GetViaType() == VIATYPE::THROUGH )
159 }
160
161 return true;
162 } );
163
164 std::unordered_map<PTR_PTR_CACHE_KEY, int> checkedPairs;
165
166 for( PCB_TRACK* track : m_board->Tracks() )
167 {
168 if( track->Type() != PCB_VIA_T )
169 continue;
170
171 PCB_VIA* via = static_cast<PCB_VIA*>( track );
172
173 if( !reportProgress( ii++, count, progressDelta ) )
174 return false; // DRC cancelled
175
176 // We only care about mechanically drilled (ie: non-laser) holes
177 if( via->GetViaType() == VIATYPE::THROUGH )
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 );
277 int epsilon = m_board->GetDesignSettings().GetDRCEpsilon();
278 SEG::ecoord epsilon_sq = SEG::Square( epsilon );
279
280 // Holes at same location generate a separate violation
281 if( ( aHole->GetCenter() - otherHole->GetCenter() ).SquaredEuclideanNorm() < epsilon_sq )
282 {
283 if( reportCoLocation )
284 {
285 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_DRILLED_HOLES_COLOCATED );
286 drce->SetItems( aItem, aOther );
287 reportViolation( drce, aHole->GetCenter(), UNDEFINED_LAYER );
288 }
289 }
290 else if( reportHole2Hole )
291 {
292 int actual = ( aHole->GetCenter() - otherHole->GetCenter() ).EuclideanNorm();
293 actual = std::max( 0, actual - aHole->GetRadius() - otherHole->GetRadius() );
294
295 auto constraint = m_drcEngine->EvalRules( HOLE_TO_HOLE_CONSTRAINT, aItem, aOther,
296 UNDEFINED_LAYER /* holes pierce all layers */ );
297 int minClearance = constraint.GetValue().Min() - epsilon;
298
299 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE
300 && minClearance >= 0
301 && actual < minClearance )
302 {
303 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_DRILLED_HOLES_TOO_CLOSE );
304 wxString msg;
305
306 msg.Printf( _( "(%s min %s; actual %s)" ),
307 constraint.GetName(),
308 MessageTextFromValue( minClearance ),
309 MessageTextFromValue( actual ) );
310
311 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
312 drce->SetItems( aItem, aOther );
313 drce->SetViolatingRule( constraint.GetParentRule() );
314
315 reportViolation( drce, aHole->GetCenter(), UNDEFINED_LAYER );
316 }
317 }
318
319 return !m_drcEngine->IsCancelled();
320}
321
322
323namespace detail
324{
326}
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:50
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:265
TRACKS & Tracks()
Definition: board.h:304
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:617
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:139
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:671
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:325
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:211
void clear()
Remove all items from the RTree.
Definition: drc_rtree.h:166
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).
virtual bool reportPhase(const wxString &aStageName)
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, LSET aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
virtual bool reportProgress(int aCount, int aSize, int aDelta)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer)
virtual void reportAux(wxString fmt,...)
DRC_ENGINE * m_drcEngine
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
static LSET AllLayersMask()
Definition: lset.cpp:808
T Min() const
Definition: minoptmax.h:33
Definition: pad.h:58
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
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
A lower-precision version of StringFromValue().
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:48
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:60
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
@ RPT_SEVERITY_IGNORE
double EuclideanNorm(const VECTOR2I &vector)
Definition: trigo.h:129
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:102
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
VECTOR2< int > VECTOR2I
Definition: vector2d.h:618