KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drc_test_provider_courtyard_clearance.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
25#include <drc/drc_engine.h>
26#include <drc/drc_item.h>
27#include <drc/drc_rule.h>
28#include <pad.h>
31#include <footprint.h>
32
33/*
34 Couartyard clearance. Tests for malformed component courtyards and overlapping footprints.
35 Generated errors:
36 - DRCE_OVERLAPPING_FOOTPRINTS
37 - DRCE_MISSING_COURTYARD
38 - DRCE_MALFORMED_COURTYARD
39 - DRCE_PTH_IN_COURTYARD,
40 - DRCE_NPTH_IN_COURTYARD,
41*/
42
44{
45public:
49 {
50 m_isRuleDriven = false;
51 }
52
54 {
55 }
56
57 virtual bool Run() override;
58
59 virtual const wxString GetName() const override
60 {
61 return wxT( "courtyard_clearance" );
62 }
63
64 virtual const wxString GetDescription() const override
65 {
66 return wxT( "Tests footprints' courtyard clearance" );
67 }
68
69private:
71
73
74private:
76};
77
78
80{
81 // Detects missing (or malformed) footprint courtyards
84 {
85 if( !reportPhase( _( "Checking footprint courtyard definitions..." ) ) )
86 return false; // DRC cancelled
87 }
89 {
90 if( !reportPhase( _( "Gathering footprint courtyards..." ) ) )
91 return false; // DRC cancelled
92 }
93 else
94 {
95 reportAux( wxT( "All courtyard violations ignored. Tests not run." ) );
96 return true; // continue with other tests
97 }
98
99 const int progressDelta = 500;
100 int ii = 0;
101
102 for( FOOTPRINT* footprint : m_board->Footprints() )
103 {
104 if( !reportProgress( ii++, m_board->Footprints().size(), progressDelta ) )
105 return false; // DRC cancelled
106
107 if( ( footprint->GetFlags() & MALFORMED_COURTYARDS ) != 0 )
108 {
110 continue;
111
112 OUTLINE_ERROR_HANDLER errorHandler =
113 [&]( const wxString& msg, BOARD_ITEM*, BOARD_ITEM*, const VECTOR2I& pt )
114 {
115 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MALFORMED_COURTYARD );
116 drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
117 drcItem->SetItems( footprint );
118 reportViolation( drcItem, pt, UNDEFINED_LAYER );
119 };
120
121 // Re-run courtyard tests to generate DRC_ITEMs
122 footprint->BuildCourtyardCaches( &errorHandler );
123 }
124 else if( footprint->GetCourtyard( F_CrtYd ).OutlineCount() == 0
125 && footprint->GetCourtyard( B_CrtYd ).OutlineCount() == 0 )
126 {
128 continue;
129
130 if( footprint->GetAttributes() & FP_ALLOW_MISSING_COURTYARD )
131 continue;
132
133 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MISSING_COURTYARD );
134 drcItem->SetItems( footprint );
135 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
136 }
137 else
138 {
139 footprint->GetCourtyard( F_CrtYd ).BuildBBoxCaches();
140 footprint->GetCourtyard( B_CrtYd ).BuildBBoxCaches();
141 }
142 }
143
144 return !m_drcEngine->IsCancelled();
145}
146
147
149{
150 if( !reportPhase( _( "Checking footprints for overlapping courtyards..." ) ) )
151 return false; // DRC cancelled
152
153 const int progressDelta = 100;
154 int ii = 0;
155
156 // Stable sorting gives stable violation generation (and stable comparisons to previously-
157 // generated violations for exclusion checking).
158 std::vector<FOOTPRINT*> footprints;
159
160 footprints.insert( footprints.begin(), m_board->Footprints().begin(),
161 m_board->Footprints().end() );
162
163 std::sort( footprints.begin(), footprints.end(),
164 []( const FOOTPRINT* a, const FOOTPRINT* b )
165 {
166 return a->m_Uuid < b->m_Uuid;
167 } );
168
169 for( auto itA = footprints.begin(); itA != footprints.end(); itA++ )
170 {
171 if( !reportProgress( ii++, footprints.size(), progressDelta ) )
172 return false; // DRC cancelled
173
174 // Ensure tests realted to courtyard constraints are not fully disabled:
178 {
179 return true; // continue with other tests
180 }
181
182 FOOTPRINT* fpA = *itA;
183 const SHAPE_POLY_SET& frontA = fpA->GetCourtyard( F_CrtYd );
184 const SHAPE_POLY_SET& backA = fpA->GetCourtyard( B_CrtYd );
185
186 if( frontA.OutlineCount() == 0 && backA.OutlineCount() == 0
189 {
190 // No courtyards defined and no hole testing against other footprint's courtyards
191 continue;
192 }
193
194 BOX2I frontA_worstCaseBBox = frontA.BBoxFromCaches();
195 BOX2I backA_worstCaseBBox = backA.BBoxFromCaches();
196
197 frontA_worstCaseBBox.Inflate( m_largestCourtyardClearance );
198 backA_worstCaseBBox.Inflate( m_largestCourtyardClearance );
199
200 BOX2I fpA_bbox = fpA->GetBoundingBox();
201
202 for( auto itB = itA + 1; itB != footprints.end(); itB++ )
203 {
204 FOOTPRINT* fpB = *itB;
205 const SHAPE_POLY_SET& frontB = fpB->GetCourtyard( F_CrtYd );
206 const SHAPE_POLY_SET& backB = fpB->GetCourtyard( B_CrtYd );
207
208 if( frontB.OutlineCount() == 0 && backB.OutlineCount() == 0
211 {
212 // No courtyards defined and no hole testing against other footprint's courtyards
213 continue;
214 }
215
216 BOX2I frontB_worstCaseBBox = frontB.BBoxFromCaches();
217 BOX2I backB_worstCaseBBox = backB.BBoxFromCaches();
218
219 frontB_worstCaseBBox.Inflate( m_largestCourtyardClearance );
220 backB_worstCaseBBox.Inflate( m_largestCourtyardClearance );
221
222 BOX2I fpB_bbox = fpB->GetBoundingBox();
223 DRC_CONSTRAINT constraint;
224 int clearance;
225 int actual;
226 VECTOR2I pos;
227
228 // Check courtyard-to-courtyard collisions on front of board,
229 // if DRCE_OVERLAPPING_FOOTPRINTS is not diasbled
230 if( frontA.OutlineCount() > 0 && frontB.OutlineCount() > 0
231 && frontA_worstCaseBBox.Intersects( frontB.BBoxFromCaches() )
233 {
234 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, F_Cu );
235 clearance = constraint.GetValue().Min();
236
237 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
238 {
239 if( frontA.Collide( &frontB, clearance, &actual, &pos ) )
240 {
242
243 if( clearance > 0 )
244 {
245 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
246 constraint.GetName(),
247 clearance,
248 actual );
249
250 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
251 }
252
253 drce->SetViolatingRule( constraint.GetParentRule() );
254 drce->SetItems( fpA, fpB );
255 reportViolation( drce, pos, F_CrtYd );
256 }
257 }
258 }
259
260 // Check courtyard-to-courtyard collisions on back of board,
261 // if DRCE_OVERLAPPING_FOOTPRINTS is not disabled
262 if( backA.OutlineCount() > 0 && backB.OutlineCount() > 0
263 && backA_worstCaseBBox.Intersects( backB.BBoxFromCaches() )
265 {
266 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, B_Cu );
267 clearance = constraint.GetValue().Min();
268
269 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
270 {
271 if( backA.Collide( &backB, clearance, &actual, &pos ) )
272 {
274
275 if( clearance > 0 )
276 {
277 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
278 constraint.GetName(),
279 clearance,
280 actual );
281
282 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
283 }
284
285 drce->SetViolatingRule( constraint.GetParentRule() );
286 drce->SetItems( fpA, fpB );
287 reportViolation( drce, pos, B_CrtYd );
288 }
289 }
290 }
291
292 //
293 // Check pad-hole-to-courtyard collisions on front and back of board.
294 //
295 // NB: via holes are not checked. There is a presumption that a physical object goes
296 // through a pad hole, which is not the case for via holes.
297 //
298 bool checkFront = false;
299 bool checkBack = false;
300
301 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, F_Cu );
302 clearance = constraint.GetValue().Min();
303
304 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
305 checkFront = true;
306
307 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpB, fpA, F_Cu );
308 clearance = constraint.GetValue().Min();
309
310 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
311 checkFront = true;
312
313 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, B_Cu );
314 clearance = constraint.GetValue().Min();
315
316 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
317 checkBack = true;
318
319 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpB, fpA, B_Cu );
320 clearance = constraint.GetValue().Min();
321
322 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
323 checkBack = true;
324
325 auto testPadAgainstCourtyards =
326 [&]( const PAD* pad, const FOOTPRINT* fp )
327 {
328 int errorCode = 0;
329
330 if( pad->GetAttribute() == PAD_ATTRIB::PTH )
331 errorCode = DRCE_PTH_IN_COURTYARD;
332 else if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
333 errorCode = DRCE_NPTH_IN_COURTYARD;
334 else
335 return;
336
337 if( m_drcEngine->IsErrorLimitExceeded( errorCode ) )
338 return;
339
340 if( pad->HasHole() )
341 {
342 std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
343 const SHAPE_POLY_SET& front = fp->GetCourtyard( F_CrtYd );
344 const SHAPE_POLY_SET& back = fp->GetCourtyard( B_CrtYd );
345
346 if( checkFront && front.OutlineCount() > 0 && front.Collide( hole.get(), 0 ) )
347 {
348 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( errorCode );
349 drce->SetItems( pad, fp );
350 reportViolation( drce, pad->GetPosition(), F_CrtYd );
351 }
352 else if( checkBack && back.OutlineCount() > 0 && back.Collide( hole.get(), 0 ) )
353 {
354 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( errorCode );
355 drce->SetItems( pad, fp );
356 reportViolation( drce, pad->GetPosition(), B_CrtYd );
357 }
358 }
359 };
360
361 if( ( frontA.OutlineCount() > 0 && frontA_worstCaseBBox.Intersects( fpB_bbox ) )
362 || ( backA.OutlineCount() > 0 && backA_worstCaseBBox.Intersects( fpB_bbox ) ) )
363 {
364 for( const PAD* padB : fpB->Pads() )
365 testPadAgainstCourtyards( padB, fpA );
366 }
367
368 if( ( frontB.OutlineCount() > 0 && frontB.BBoxFromCaches().Intersects( fpA_bbox ) )
369 || ( backB.OutlineCount() > 0 && backB.BBoxFromCaches().Intersects( fpA_bbox ) ) )
370 {
371 for( const PAD* padA : fpA->Pads() )
372 testPadAgainstCourtyards( padA, fpB );
373 }
374
375 if( m_drcEngine->IsCancelled() )
376 return false;
377 }
378 }
379
380 return !m_drcEngine->IsCancelled();
381}
382
383
385{
387 DRC_CONSTRAINT constraint;
388
391
392 reportAux( wxT( "Worst courtyard clearance : %d nm" ), m_largestCourtyardClearance );
393
395 return false;
396
398 return false;
399
400 return true;
401}
402
403
404namespace detail
405{
407}
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:79
const FOOTPRINTS & Footprints() const
Definition: board.h:336
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:558
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:311
wxString GetName() const
Definition: drc_rule.h:160
SEVERITY GetSeverity() const
Definition: drc_rule.h:173
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:152
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:156
BOARD * GetBoard() const
Definition: drc_engine.h:96
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:690
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:395
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
virtual const wxString GetDescription() const override
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, double aConstraint, double aActual)
virtual bool reportPhase(const wxString &aStageName)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer, DRC_CUSTOM_MARKER_HANDLER *aCustomHandler=nullptr)
DRC_ENGINE * m_drcEngine
void reportAux(const wxString &aMsg)
virtual bool reportProgress(size_t aCount, size_t aSize, size_t aDelta=1)
std::deque< PAD * > & Pads()
Definition: footprint.h:204
const SHAPE_POLY_SET & GetCourtyard(PCB_LAYER_ID aLayer) const
Used in DRC to test the courtyard area (a complex polygon).
Definition: footprint.cpp:2966
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: footprint.cpp:1337
T Min() const
Definition: minoptmax.h:33
Definition: pad.h:54
Represent a set of closed polygons.
bool Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
int OutlineCount() const
Return the number of outlines in the set.
const BOX2I BBoxFromCaches() const
const std::function< void(const wxString &msg, BOARD_ITEM *itemA, BOARD_ITEM *itemB, const VECTOR2I &pt)> OUTLINE_ERROR_HANDLER
@ DRCE_OVERLAPPING_FOOTPRINTS
Definition: drc_item.h:65
@ DRCE_MISSING_COURTYARD
Definition: drc_item.h:66
@ DRCE_PTH_IN_COURTYARD
Definition: drc_item.h:69
@ DRCE_MALFORMED_COURTYARD
Definition: drc_item.h:67
@ DRCE_NPTH_IN_COURTYARD
Definition: drc_item.h:70
@ COURTYARD_CLEARANCE_CONSTRAINT
Definition: drc_rule.h:55
#define _(s)
#define MALFORMED_COURTYARDS
@ FP_ALLOW_MISSING_COURTYARD
Definition: footprint.h:86
@ F_CrtYd
Definition: layer_ids.h:116
@ B_Cu
Definition: layer_ids.h:65
@ B_CrtYd
Definition: layer_ids.h:115
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
@ F_Cu
Definition: layer_ids.h:64
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
@ RPT_SEVERITY_IGNORE
int clearance
int actual