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-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
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
164 {
165 return true; // continue with other tests
166 }
167
168 FOOTPRINT* fpA = *itA;
169 const SHAPE_POLY_SET& frontA = fpA->GetCourtyard( F_CrtYd );
170 const SHAPE_POLY_SET& backA = fpA->GetCourtyard( B_CrtYd );
171
172 if( frontA.OutlineCount() == 0 && backA.OutlineCount() == 0
175 {
176 // No courtyards defined and no hole testing against other footprint's courtyards
177 continue;
178 }
179
180 BOX2I frontA_worstCaseBBox = frontA.BBoxFromCaches();
181 BOX2I backA_worstCaseBBox = backA.BBoxFromCaches();
182
183 frontA_worstCaseBBox.Inflate( m_largestCourtyardClearance );
184 backA_worstCaseBBox.Inflate( m_largestCourtyardClearance );
185
186 BOX2I fpA_bbox = fpA->GetBoundingBox();
187
188 for( auto itB = itA + 1; itB != m_board->Footprints().end(); itB++ )
189 {
190 FOOTPRINT* fpB = *itB;
191 const SHAPE_POLY_SET& frontB = fpB->GetCourtyard( F_CrtYd );
192 const SHAPE_POLY_SET& backB = fpB->GetCourtyard( B_CrtYd );
193
194 if( frontB.OutlineCount() == 0 && backB.OutlineCount() == 0
197 {
198 // No courtyards defined and no hole testing against other footprint's courtyards
199 continue;
200 }
201
202 BOX2I frontB_worstCaseBBox = frontB.BBoxFromCaches();
203 BOX2I backB_worstCaseBBox = backB.BBoxFromCaches();
204
205 frontB_worstCaseBBox.Inflate( m_largestCourtyardClearance );
206 backB_worstCaseBBox.Inflate( m_largestCourtyardClearance );
207
208 BOX2I fpB_bbox = fpB->GetBoundingBox();
209 DRC_CONSTRAINT constraint;
210 int clearance;
211 int actual;
212 VECTOR2I pos;
213
214 //
215 // Check courtyard-to-courtyard collisions on front of board.
216 //
217
218 if( frontA.OutlineCount() > 0 && frontB.OutlineCount() > 0
219 && 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 //
248 // Check courtyard-to-courtyard collisions on back of board.
249 //
250
251 if( backA.OutlineCount() > 0 && backB.OutlineCount() > 0
252 && backA_worstCaseBBox.Intersects( backB.BBoxFromCaches() ) )
253 {
254 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, B_Cu );
255 clearance = constraint.GetValue().Min();
256
257 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
258 {
259 if( backA.Collide( &backB, clearance, &actual, &pos ) )
260 {
262
263 if( clearance > 0 )
264 {
265 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
266 constraint.GetName(),
267 clearance,
268 actual );
269
270 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
271 }
272
273 drce->SetViolatingRule( constraint.GetParentRule() );
274 drce->SetItems( fpA, fpB );
275 reportViolation( drce, pos, B_CrtYd );
276 }
277 }
278 }
279
280 //
281 // Check pad-hole-to-courtyard collisions on front and back of board.
282 //
283 // NB: via holes are not checked. There is a presumption that a physical object goes
284 // through a pad hole, which is not the case for via holes.
285 //
286 bool checkFront = false;
287 bool checkBack = false;
288
289 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, F_Cu );
290 clearance = constraint.GetValue().Min();
291
292 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
293 checkFront = true;
294
295 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpB, fpA, F_Cu );
296 clearance = constraint.GetValue().Min();
297
298 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
299 checkFront = true;
300
301 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, B_Cu );
302 clearance = constraint.GetValue().Min();
303
304 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
305 checkBack = true;
306
307 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpB, fpA, B_Cu );
308 clearance = constraint.GetValue().Min();
309
310 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
311 checkBack = true;
312
313 auto testPadAgainstCourtyards =
314 [&]( const PAD* pad, const FOOTPRINT* fp )
315 {
316 int errorCode = 0;
317
318 if( pad->GetAttribute() == PAD_ATTRIB::PTH )
319 errorCode = DRCE_PTH_IN_COURTYARD;
320 else if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
321 errorCode = DRCE_NPTH_IN_COURTYARD;
322 else
323 return;
324
325 if( m_drcEngine->IsErrorLimitExceeded( errorCode ) )
326 return;
327
328 if( pad->HasHole() )
329 {
330 std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
331 const SHAPE_POLY_SET& front = fp->GetCourtyard( F_CrtYd );
332 const SHAPE_POLY_SET& back = fp->GetCourtyard( B_CrtYd );
333
334 if( checkFront && front.OutlineCount() > 0 && front.Collide( hole.get(), 0 ) )
335 {
336 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( errorCode );
337 drce->SetItems( pad, fp );
338 reportViolation( drce, pad->GetPosition(), F_CrtYd );
339 }
340 else if( checkBack && back.OutlineCount() > 0 && back.Collide( hole.get(), 0 ) )
341 {
342 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( errorCode );
343 drce->SetItems( pad, fp );
344 reportViolation( drce, pad->GetPosition(), B_CrtYd );
345 }
346 }
347 };
348
349 if( ( frontA.OutlineCount() > 0 && frontA_worstCaseBBox.Intersects( fpB_bbox ) )
350 || ( backA.OutlineCount() > 0 && backA_worstCaseBBox.Intersects( fpB_bbox ) ) )
351 {
352 for( const PAD* padB : fpB->Pads() )
353 testPadAgainstCourtyards( padB, fpA );
354 }
355
356 if( ( frontB.OutlineCount() > 0 && frontB.BBoxFromCaches().Intersects( fpA_bbox ) )
357 || ( backB.OutlineCount() > 0 && backB.BBoxFromCaches().Intersects( fpA_bbox ) ) )
358 {
359 for( const PAD* padA : fpA->Pads() )
360 testPadAgainstCourtyards( padA, fpB );
361 }
362
363 if( m_drcEngine->IsCancelled() )
364 return false;
365 }
366 }
367
368 return !m_drcEngine->IsCancelled();
369}
370
371
373{
375 DRC_CONSTRAINT constraint;
376
379
380 reportAux( wxT( "Worst courtyard clearance : %d nm" ), m_largestCourtyardClearance );
381
383 return false;
384
386 return false;
387
388 return true;
389}
390
391
392namespace detail
393{
395}
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:77
const FOOTPRINTS & Footprints() const
Definition: board.h:323
bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:294
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:541
wxString GetName() const
Definition: drc_rule.h:149
SEVERITY GetSeverity() const
Definition: drc_rule.h:162
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:141
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:145
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:675
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:332
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)
PADS & Pads()
Definition: footprint.h:191
const SHAPE_POLY_SET & GetCourtyard(PCB_LAYER_ID aLayer) const
Used in DRC to test the courtyard area (a complex polygon).
Definition: footprint.cpp:2812
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: footprint.cpp:1223
T Min() const
Definition: minoptmax.h:33
Definition: pad.h:53
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:61
@ DRCE_MISSING_COURTYARD
Definition: drc_item.h:62
@ DRCE_PTH_IN_COURTYARD
Definition: drc_item.h:65
@ DRCE_MALFORMED_COURTYARD
Definition: drc_item.h:63
@ DRCE_NPTH_IN_COURTYARD
Definition: drc_item.h:66
@ COURTYARD_CLEARANCE_CONSTRAINT
Definition: drc_rule.h:52
#define _(s)
#define MALFORMED_COURTYARDS
@ FP_ALLOW_MISSING_COURTYARD
Definition: footprint.h:79
@ F_CrtYd
Definition: layer_ids.h:117
@ B_Cu
Definition: layer_ids.h:95
@ B_CrtYd
Definition: layer_ids.h:116
@ 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