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