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 (C) 2004-2024 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 for( auto itA = m_board->Footprints().begin(); itA != m_board->Footprints().end(); itA++ )
157 {
158 if( !reportProgress( ii++, m_board->Footprints().size(), progressDelta ) )
159 return false; // DRC cancelled
160
161 // Ensure tests realted to courtyard constraints are not fully disabled:
165 {
166 return true; // continue with other tests
167 }
168
169 FOOTPRINT* fpA = *itA;
170 const SHAPE_POLY_SET& frontA = fpA->GetCourtyard( F_CrtYd );
171 const SHAPE_POLY_SET& backA = fpA->GetCourtyard( B_CrtYd );
172
173 if( frontA.OutlineCount() == 0 && backA.OutlineCount() == 0
176 {
177 // No courtyards defined and no hole testing against other footprint's courtyards
178 continue;
179 }
180
181 BOX2I frontA_worstCaseBBox = frontA.BBoxFromCaches();
182 BOX2I backA_worstCaseBBox = backA.BBoxFromCaches();
183
184 frontA_worstCaseBBox.Inflate( m_largestCourtyardClearance );
185 backA_worstCaseBBox.Inflate( m_largestCourtyardClearance );
186
187 BOX2I fpA_bbox = fpA->GetBoundingBox();
188
189 for( auto itB = itA + 1; itB != m_board->Footprints().end(); itB++ )
190 {
191 FOOTPRINT* fpB = *itB;
192 const SHAPE_POLY_SET& frontB = fpB->GetCourtyard( F_CrtYd );
193 const SHAPE_POLY_SET& backB = fpB->GetCourtyard( B_CrtYd );
194
195 if( frontB.OutlineCount() == 0 && backB.OutlineCount() == 0
198 {
199 // No courtyards defined and no hole testing against other footprint's courtyards
200 continue;
201 }
202
203 BOX2I frontB_worstCaseBBox = frontB.BBoxFromCaches();
204 BOX2I backB_worstCaseBBox = backB.BBoxFromCaches();
205
206 frontB_worstCaseBBox.Inflate( m_largestCourtyardClearance );
207 backB_worstCaseBBox.Inflate( m_largestCourtyardClearance );
208
209 BOX2I fpB_bbox = fpB->GetBoundingBox();
210 DRC_CONSTRAINT constraint;
211 int clearance;
212 int actual;
213 VECTOR2I pos;
214
215 // Check courtyard-to-courtyard collisions on front of board,
216 // if DRCE_OVERLAPPING_FOOTPRINTS is not diasbled
217 if( frontA.OutlineCount() > 0 && frontB.OutlineCount() > 0
218 && frontA_worstCaseBBox.Intersects( frontB.BBoxFromCaches() )
220 {
221 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, F_Cu );
222 clearance = constraint.GetValue().Min();
223
224 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
225 {
226 if( frontA.Collide( &frontB, clearance, &actual, &pos ) )
227 {
229
230 if( clearance > 0 )
231 {
232 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
233 constraint.GetName(),
234 clearance,
235 actual );
236
237 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
238 }
239
240 drce->SetViolatingRule( constraint.GetParentRule() );
241 drce->SetItems( fpA, fpB );
242 reportViolation( drce, pos, F_CrtYd );
243 }
244 }
245 }
246
247 // Check courtyard-to-courtyard collisions on back of board,
248 // if DRCE_OVERLAPPING_FOOTPRINTS is not disabled
249 if( backA.OutlineCount() > 0 && backB.OutlineCount() > 0
250 && backA_worstCaseBBox.Intersects( backB.BBoxFromCaches() )
252 {
253 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, B_Cu );
254 clearance = constraint.GetValue().Min();
255
256 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
257 {
258 if( backA.Collide( &backB, clearance, &actual, &pos ) )
259 {
261
262 if( clearance > 0 )
263 {
264 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
265 constraint.GetName(),
266 clearance,
267 actual );
268
269 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
270 }
271
272 drce->SetViolatingRule( constraint.GetParentRule() );
273 drce->SetItems( fpA, fpB );
274 reportViolation( drce, pos, B_CrtYd );
275 }
276 }
277 }
278
279 //
280 // Check pad-hole-to-courtyard collisions on front and back of board.
281 //
282 // NB: via holes are not checked. There is a presumption that a physical object goes
283 // through a pad hole, which is not the case for via holes.
284 //
285 bool checkFront = false;
286 bool checkBack = false;
287
288 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, F_Cu );
289 clearance = constraint.GetValue().Min();
290
291 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
292 checkFront = true;
293
294 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpB, fpA, F_Cu );
295 clearance = constraint.GetValue().Min();
296
297 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
298 checkFront = true;
299
300 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, B_Cu );
301 clearance = constraint.GetValue().Min();
302
303 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
304 checkBack = true;
305
306 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpB, fpA, B_Cu );
307 clearance = constraint.GetValue().Min();
308
309 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
310 checkBack = true;
311
312 auto testPadAgainstCourtyards =
313 [&]( const PAD* pad, const FOOTPRINT* fp )
314 {
315 int errorCode = 0;
316
317 if( pad->GetAttribute() == PAD_ATTRIB::PTH )
318 errorCode = DRCE_PTH_IN_COURTYARD;
319 else if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
320 errorCode = DRCE_NPTH_IN_COURTYARD;
321 else
322 return;
323
324 if( m_drcEngine->IsErrorLimitExceeded( errorCode ) )
325 return;
326
327 if( pad->HasHole() )
328 {
329 std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
330 const SHAPE_POLY_SET& front = fp->GetCourtyard( F_CrtYd );
331 const SHAPE_POLY_SET& back = fp->GetCourtyard( B_CrtYd );
332
333 if( checkFront && front.OutlineCount() > 0 && front.Collide( hole.get(), 0 ) )
334 {
335 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( errorCode );
336 drce->SetItems( pad, fp );
337 reportViolation( drce, pad->GetPosition(), F_CrtYd );
338 }
339 else if( checkBack && back.OutlineCount() > 0 && back.Collide( hole.get(), 0 ) )
340 {
341 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( errorCode );
342 drce->SetItems( pad, fp );
343 reportViolation( drce, pad->GetPosition(), B_CrtYd );
344 }
345 }
346 };
347
348 if( ( frontA.OutlineCount() > 0 && frontA_worstCaseBBox.Intersects( fpB_bbox ) )
349 || ( backA.OutlineCount() > 0 && backA_worstCaseBBox.Intersects( fpB_bbox ) ) )
350 {
351 for( const PAD* padB : fpB->Pads() )
352 testPadAgainstCourtyards( padB, fpA );
353 }
354
355 if( ( frontB.OutlineCount() > 0 && frontB.BBoxFromCaches().Intersects( fpA_bbox ) )
356 || ( backB.OutlineCount() > 0 && backB.BBoxFromCaches().Intersects( fpA_bbox ) ) )
357 {
358 for( const PAD* padA : fpA->Pads() )
359 testPadAgainstCourtyards( padA, fpB );
360 }
361
362 if( m_drcEngine->IsCancelled() )
363 return false;
364 }
365 }
366
367 return !m_drcEngine->IsCancelled();
368}
369
370
372{
374 DRC_CONSTRAINT constraint;
375
378
379 reportAux( wxT( "Worst courtyard clearance : %d nm" ), m_largestCourtyardClearance );
380
382 return false;
383
385 return false;
386
387 return true;
388}
389
390
391namespace detail
392{
394}
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:331
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:99
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:679
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:372
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_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:206
const SHAPE_POLY_SET & GetCourtyard(PCB_LAYER_ID aLayer) const
Used in DRC to test the courtyard area (a complex polygon).
Definition: footprint.cpp:2847
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: footprint.cpp:1252
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:82
@ 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