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
64{
65 if( m_drcEngine->IsErrorLimitExceeded( DRCE_DRILLED_HOLES_TOO_CLOSE )
66 && m_drcEngine->IsErrorLimitExceeded( DRCE_DRILLED_HOLES_COLOCATED ) )
67 {
68 REPORT_AUX( wxT( "Hole to hole violations ignored. Tests not run." ) );
69 return true; // continue with other tests
70 }
71
72 m_board = m_drcEngine->GetBoard();
73
74 DRC_CONSTRAINT worstClearanceConstraint;
75
76 if( m_drcEngine->QueryWorstConstraint( HOLE_TO_HOLE_CONSTRAINT, worstClearanceConstraint ) )
77 {
78 m_largestHoleToHoleClearance = worstClearanceConstraint.GetValue().Min();
79 }
80 else
81 {
82 REPORT_AUX( wxT( "No hole to hole constraints found. Skipping check." ) );
83 return true; // continue with other tests
84 }
85
86 if( !reportPhase( _( "Checking hole to hole clearances..." ) ) )
87 return false; // DRC cancelled
88
89 const size_t progressDelta = 200;
90 size_t count = 0;
91 size_t ii = 0;
92
93 m_holeTree.clear();
94
96 [&]( BOARD_ITEM* item ) -> bool
97 {
98 ++count;
99 return true;
100 } );
101
102 count *= 2; // One for adding to the rtree; one for checking
103
105 [&]( BOARD_ITEM* item ) -> bool
106 {
107 if( !reportProgress( ii++, count, progressDelta ) )
108 return false;
109
110 if( item->Type() == PCB_PAD_T )
111 {
112 PAD* pad = static_cast<PAD*>( item );
113
114 // Index every drilled or milled hole, including oval (slotted) holes. A
115 // slot too close to another hole or slot is still a manufacturing defect.
116 if( pad->HasHole() )
118 }
119 else if( item->Type() == PCB_VIA_T )
120 {
121 // Blind/buried/microvias will be drilled/burned _prior_ to lamination, so
122 // subsequently drilled holes need to avoid them.
124 }
125
126 return true;
127 } );
128
129 m_holeTree.Build();
130
131 std::unordered_map<PTR_PTR_CACHE_KEY, int> checkedPairs;
132
133 for( PCB_TRACK* track : m_board->Tracks() )
134 {
135 if( track->Type() != PCB_VIA_T )
136 continue;
137
138 PCB_VIA* via = static_cast<PCB_VIA*>( track );
139
140 if( !reportProgress( ii++, count, progressDelta ) )
141 return false; // DRC cancelled
142
143 // We only care about mechanically drilled (ie: non-laser) holes. These include both
144 // blind/buried via holes (drilled prior to lamination) and through-via and drilled pad
145 // holes (which are generally drilled post laminataion).
146 if( via->GetViaType() != VIATYPE::MICROVIA )
147 {
148 std::shared_ptr<SHAPE_SEGMENT> holeShape = via->GetEffectiveHoleShape( UNDEFINED_LAYER,
150
151 m_holeTree.QueryColliding( via, Edge_Cuts, Edge_Cuts,
152 // Filter:
153 [&]( BOARD_ITEM* other ) -> bool
154 {
155 BOARD_ITEM* a = via;
156 BOARD_ITEM* b = other;
157
158 // store canonical order so we don't collide in both directions
159 // (a:b and b:a)
160 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
161 std::swap( a, b );
162
163 if( checkedPairs.find( { a, b } ) != checkedPairs.end() )
164 {
165 return false;
166 }
167 else
168 {
169 checkedPairs[ { a, b } ] = 1;
170 return true;
171 }
172 },
173 // Visitor:
174 [&]( BOARD_ITEM* other ) -> bool
175 {
176 return testHoleAgainstHole( via, holeShape.get(), other );
177 },
179 }
180 }
181
182 // Keep the same checkedPairs across both passes so a via/pad pair tested in the via pass
183 // above is not reported a second time when the pad queries the via below.
184
185 for( FOOTPRINT* footprint : m_board->Footprints() )
186 {
187 for( PAD* pad : footprint->Pads() )
188 {
189 if( !reportProgress( ii++, count, progressDelta ) )
190 return false; // DRC cancelled
191
192 // Test every drilled or milled hole, including oval (slotted) holes
193 if( pad->HasHole() )
194 {
195 std::shared_ptr<SHAPE_SEGMENT> holeShape = pad->GetEffectiveHoleShape( UNDEFINED_LAYER,
197
198 m_holeTree.QueryColliding( pad, Edge_Cuts, Edge_Cuts,
199 // Filter:
200 [&]( BOARD_ITEM* other ) -> bool
201 {
202 BOARD_ITEM* a = pad;
203 BOARD_ITEM* b = other;
204
205 // store canonical order so we don't collide in both directions
206 // (a:b and b:a)
207 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
208 std::swap( a, b );
209
210 if( checkedPairs.find( { a, b } ) != checkedPairs.end() )
211 {
212 return false;
213 }
214 else
215 {
216 checkedPairs[ { a, b } ] = 1;
217 return true;
218 }
219 },
220 // Visitor:
221 [&]( BOARD_ITEM* other ) -> bool
222 {
223 return testHoleAgainstHole( pad, holeShape.get(), other );
224 },
226 }
227 }
228
229 if( m_drcEngine->IsCancelled() )
230 return false;
231 }
232
233 return !m_drcEngine->IsCancelled();
234}
235
236
238 BOARD_ITEM* aOther )
239{
240 bool reportCoLocation = !m_drcEngine->IsErrorLimitExceeded( DRCE_DRILLED_HOLES_COLOCATED );
241 bool reportHole2Hole = !m_drcEngine->IsErrorLimitExceeded( DRCE_DRILLED_HOLES_TOO_CLOSE );
242
243 if( !reportCoLocation && !reportHole2Hole )
244 return false;
245
246 std::shared_ptr<SHAPE_SEGMENT> otherHole = aOther->GetEffectiveHoleShape( UNDEFINED_LAYER,
248 int epsilon = m_board->GetDesignSettings().GetDRCEpsilon();
249 SEG::ecoord epsilon_sq = SEG::Square( epsilon );
250
251 // Blind-buried vias are drilled prior to stackup; they're only an issue if they share layers
252 if( aItem->Type() == PCB_VIA_T && aOther->Type() == PCB_VIA_T )
253 {
254 LSET viaHoleLayers = static_cast<PCB_VIA*>( aItem )->GetLayerSet() & LSET::AllCuMask();
255
256 if( ( viaHoleLayers & static_cast<PCB_VIA*>( aOther )->GetLayerSet() ).none() )
257 return false;
258 }
259
260 // Holes at same location generate a separate violation
261 if( ( aHole->GetCenter() - otherHole->GetCenter() ).SquaredEuclideanNorm() < epsilon_sq )
262 {
263 if( reportCoLocation )
264 {
265 // Generate violations based on a well-defined order so that exclusion checking
266 // against previously-generated violations will work.
267 if( aItem->m_Uuid > aOther->m_Uuid )
268 std::swap( aItem, aOther );
269
270 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_DRILLED_HOLES_COLOCATED );
271 drcItem->SetItems( aItem, aOther );
272 reportTwoPointGeometry( drcItem, aHole->GetCenter(), aHole->GetCenter(), aHole->GetCenter(),
274 }
275 }
276 else if( reportHole2Hole )
277 {
278 // Measure between the hole axes, then back off the two half-widths. For a round hole
279 // the segment is zero-length and its width is the drill diameter, so this reduces to
280 // the centre-to-centre distance less the two radii; for a slot it follows the milled
281 // oval correctly.
282 int actual = aHole->GetSeg().Distance( otherHole->GetSeg() );
283 actual = std::max( 0, actual - aHole->GetWidth() / 2 - otherHole->GetWidth() / 2 );
284
285 auto constraint = m_drcEngine->EvalRules( HOLE_TO_HOLE_CONSTRAINT, aItem, aOther,
286 UNDEFINED_LAYER /* holes pierce all layers */ );
287 int minClearance = constraint.GetValue().Min();
288
289 // Relax the comparison by the epsilon, but quote the rule as the user entered it
290 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE
291 && minClearance >= 0
292 && actual < std::max( 0, minClearance - epsilon ) )
293 {
294 // Generate violations based on a well-defined order so that exclusion checking
295 // against previously-generated violations will work.
296 if( aItem->m_Uuid > aOther->m_Uuid )
297 std::swap( aItem, aOther );
298
299 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_DRILLED_HOLES_TOO_CLOSE );
300 drcItem->SetErrorDetail( formatMsg( _( "(%s min %s; actual %s)" ),
301 constraint.GetName(),
302 minClearance,
303 actual ) );
304 drcItem->SetItems( aItem, aOther );
305 drcItem->SetViolatingRule( constraint.GetParentRule() );
306 reportTwoShapeGeometry( drcItem, aHole->GetCenter(), aHole, otherHole.get(), UNDEFINED_LAYER, actual );
307 }
308 }
309
310 return !m_drcEngine->IsCancelled();
311}
312
313
314namespace detail
315{
317}
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, DRC_CONSTRAINT_T aUsage=NULL_CONSTRAINT) const
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:409
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:200
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:444
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:597
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
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:709
const SEG & GetSeg() const
int GetWidth() const override
VECTOR2I GetCenter() const
@ DRCE_DRILLED_HOLES_TOO_CLOSE
Definition drc_item.h:50
@ DRCE_DRILLED_HOLES_COLOCATED
Definition drc_item.h:51
@ HOLE_CLEARANCE_CONSTRAINT
Definition drc_rule.h:53
@ HOLE_TO_HOLE_CONSTRAINT
Definition drc_rule.h:54
#define REPORT_AUX(s)
#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:89
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79