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 <pad.h>
30#include <footprint.h>
31
32/*
33 Couartyard clearance. Tests for malformed component courtyards and overlapping footprints.
34 Generated errors:
35 - DRCE_OVERLAPPING_FOOTPRINTS
36 - DRCE_MISSING_COURTYARD
37 - DRCE_MALFORMED_COURTYARD
38 - DRCE_PTH_IN_COURTYARD,
39 - DRCE_NPTH_IN_COURTYARD,
40*/
41
43{
44public:
48 {
49 m_isRuleDriven = false;
50 }
51
53
54 virtual bool Run() override;
55
56 virtual const wxString GetName() const override { return wxT( "courtyard_clearance" ); }
57
58private:
60
62
63private:
65};
66
67
69{
70 // Detects missing (or malformed) footprint courtyards
73 {
74 if( !reportPhase( _( "Checking footprint courtyard definitions..." ) ) )
75 return false; // DRC cancelled
76 }
78 {
79 if( !reportPhase( _( "Gathering footprint courtyards..." ) ) )
80 return false; // DRC cancelled
81 }
82 else
83 {
84 REPORT_AUX( wxT( "All courtyard violations ignored. Tests not run." ) );
85 return true; // continue with other tests
86 }
87
88 const int progressDelta = 500;
89 int ii = 0;
90
91 for( FOOTPRINT* footprint : m_board->Footprints() )
92 {
93 if( !reportProgress( ii++, m_board->Footprints().size(), progressDelta ) )
94 return false; // DRC cancelled
95
96 if( ( footprint->GetFlags() & MALFORMED_COURTYARDS ) != 0 )
97 {
99 continue;
100
101 OUTLINE_ERROR_HANDLER errorHandler =
102 [&]( const wxString& msg, BOARD_ITEM*, BOARD_ITEM*, const VECTOR2I& pt )
103 {
104 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MALFORMED_COURTYARD );
105 drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
106 drcItem->SetItems( footprint );
107 reportViolation( drcItem, pt, UNDEFINED_LAYER );
108 };
109
110 // Re-run courtyard tests to generate DRC_ITEMs
111 footprint->BuildCourtyardCaches( &errorHandler );
112 }
113 else if( footprint->GetCourtyard( F_CrtYd ).OutlineCount() == 0
114 && footprint->GetCourtyard( B_CrtYd ).OutlineCount() == 0 )
115 {
117 continue;
118
119 if( footprint->AllowMissingCourtyard() )
120 continue;
121
122 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MISSING_COURTYARD );
123 drcItem->SetItems( footprint );
124 reportViolation( drcItem, footprint->GetPosition(), UNDEFINED_LAYER );
125 }
126 else
127 {
128 footprint->GetCourtyard( F_CrtYd ).BuildBBoxCaches();
129 footprint->GetCourtyard( B_CrtYd ).BuildBBoxCaches();
130 }
131 }
132
133 return !m_drcEngine->IsCancelled();
134}
135
136
138{
139 if( !reportPhase( _( "Checking footprints for overlapping courtyards..." ) ) )
140 return false; // DRC cancelled
141
142 const int progressDelta = 100;
143 int ii = 0;
144
145 // Stable sorting gives stable violation generation (and stable comparisons to previously-
146 // generated violations for exclusion checking).
147 std::vector<FOOTPRINT*> footprints;
148
149 footprints.insert( footprints.begin(), m_board->Footprints().begin(),
150 m_board->Footprints().end() );
151
152 std::sort( footprints.begin(), footprints.end(),
153 []( const FOOTPRINT* a, const FOOTPRINT* b )
154 {
155 return a->m_Uuid < b->m_Uuid;
156 } );
157
158 for( auto itA = footprints.begin(); itA != footprints.end(); itA++ )
159 {
160 if( !reportProgress( ii++, footprints.size(), progressDelta ) )
161 return false; // DRC cancelled
162
163 // Ensure tests realted to courtyard constraints are not fully disabled:
167 {
168 return true; // continue with other tests
169 }
170
171 FOOTPRINT* fpA = *itA;
172 const SHAPE_POLY_SET& frontA = fpA->GetCourtyard( F_CrtYd );
173 const SHAPE_POLY_SET& backA = fpA->GetCourtyard( B_CrtYd );
174
175 if( frontA.OutlineCount() == 0 && backA.OutlineCount() == 0
178 {
179 // No courtyards defined and no hole testing against other footprint's courtyards
180 continue;
181 }
182
183 BOX2I frontA_worstCaseBBox = frontA.BBoxFromCaches();
184 BOX2I backA_worstCaseBBox = backA.BBoxFromCaches();
185
186 frontA_worstCaseBBox.Inflate( m_largestCourtyardClearance );
187 backA_worstCaseBBox.Inflate( m_largestCourtyardClearance );
188
189 BOX2I fpA_bbox = fpA->GetBoundingBox();
190
191 for( auto itB = itA + 1; itB != footprints.end(); itB++ )
192 {
193 FOOTPRINT* fpB = *itB;
194 const SHAPE_POLY_SET& frontB = fpB->GetCourtyard( F_CrtYd );
195 const SHAPE_POLY_SET& backB = fpB->GetCourtyard( B_CrtYd );
196
197 if( frontB.OutlineCount() == 0 && backB.OutlineCount() == 0
200 {
201 // No courtyards defined and no hole testing against other footprint's courtyards
202 continue;
203 }
204
205 BOX2I frontB_worstCaseBBox = frontB.BBoxFromCaches();
206 BOX2I backB_worstCaseBBox = backB.BBoxFromCaches();
207
208 frontB_worstCaseBBox.Inflate( m_largestCourtyardClearance );
209 backB_worstCaseBBox.Inflate( m_largestCourtyardClearance );
210
211 BOX2I fpB_bbox = fpB->GetBoundingBox();
212 DRC_CONSTRAINT constraint;
213 int clearance;
214 int actual;
215 VECTOR2I pos;
216
217 // Check courtyard-to-courtyard collisions on front of board,
218 // if DRCE_OVERLAPPING_FOOTPRINTS is not diasbled
219 if( frontA.OutlineCount() > 0 && frontB.OutlineCount() > 0
220 && frontA_worstCaseBBox.Intersects( frontB.BBoxFromCaches() )
222 {
223 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, F_Cu );
224 clearance = constraint.GetValue().Min();
225
226 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
227 {
228 if( frontA.Collide( &frontB, clearance, &actual, &pos ) )
229 {
231
232 if( clearance > 0 )
233 {
234 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
235 constraint.GetName(),
236 clearance,
237 actual );
238
239 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
240 }
241
242 drce->SetViolatingRule( constraint.GetParentRule() );
243 drce->SetItems( fpA, fpB );
244 reportViolation( drce, pos, F_CrtYd );
245 }
246 }
247 }
248
249 // Check courtyard-to-courtyard collisions on back of board,
250 // if DRCE_OVERLAPPING_FOOTPRINTS is not disabled
251 if( backA.OutlineCount() > 0 && backB.OutlineCount() > 0
252 && backA_worstCaseBBox.Intersects( backB.BBoxFromCaches() )
254 {
255 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, B_Cu );
256 clearance = constraint.GetValue().Min();
257
258 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
259 {
260 if( backA.Collide( &backB, clearance, &actual, &pos ) )
261 {
263
264 if( clearance > 0 )
265 {
266 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
267 constraint.GetName(),
268 clearance,
269 actual );
270
271 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
272 }
273
274 drce->SetViolatingRule( constraint.GetParentRule() );
275 drce->SetItems( fpA, fpB );
276 reportViolation( drce, pos, B_CrtYd );
277 }
278 }
279 }
280
281 //
282 // Check pad-hole-to-courtyard collisions on front and back of board.
283 //
284 // NB: via holes are not checked. There is a presumption that a physical object goes
285 // through a pad hole, which is not the case for via holes.
286 //
287 bool checkFront = false;
288 bool checkBack = false;
289
290 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, F_Cu );
291 clearance = constraint.GetValue().Min();
292
293 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
294 checkFront = true;
295
296 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpB, fpA, F_Cu );
297 clearance = constraint.GetValue().Min();
298
299 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
300 checkFront = true;
301
302 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, B_Cu );
303 clearance = constraint.GetValue().Min();
304
305 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
306 checkBack = true;
307
308 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpB, fpA, B_Cu );
309 clearance = constraint.GetValue().Min();
310
311 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
312 checkBack = true;
313
314 auto testPadAgainstCourtyards =
315 [&]( const PAD* pad, const FOOTPRINT* fp )
316 {
317 int errorCode = 0;
318
319 if( pad->GetProperty() == PAD_PROP::HEATSINK )
320 return;
321 else if( pad->GetAttribute() == PAD_ATTRIB::PTH )
322 errorCode = DRCE_PTH_IN_COURTYARD;
323 else if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
324 errorCode = DRCE_NPTH_IN_COURTYARD;
325 else
326 return;
327
328 if( m_drcEngine->IsErrorLimitExceeded( errorCode ) )
329 return;
330
331 if( pad->HasHole() )
332 {
333 std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
334 const SHAPE_POLY_SET& front = fp->GetCourtyard( F_CrtYd );
335 const SHAPE_POLY_SET& back = fp->GetCourtyard( B_CrtYd );
336
337 if( checkFront && front.OutlineCount() > 0 && front.Collide( hole.get(), 0 ) )
338 {
339 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( errorCode );
340 drce->SetItems( pad, fp );
341 reportViolation( drce, pad->GetPosition(), F_CrtYd );
342 }
343 else if( checkBack && back.OutlineCount() > 0 && back.Collide( hole.get(), 0 ) )
344 {
345 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( errorCode );
346 drce->SetItems( pad, fp );
347 reportViolation( drce, pad->GetPosition(), B_CrtYd );
348 }
349 }
350 };
351
352 if( ( frontA.OutlineCount() > 0 && frontA_worstCaseBBox.Intersects( fpB_bbox ) )
353 || ( backA.OutlineCount() > 0 && backA_worstCaseBBox.Intersects( fpB_bbox ) ) )
354 {
355 for( const PAD* padB : fpB->Pads() )
356 testPadAgainstCourtyards( padB, fpA );
357 }
358
359 if( ( frontB.OutlineCount() > 0 && frontB.BBoxFromCaches().Intersects( fpA_bbox ) )
360 || ( backB.OutlineCount() > 0 && backB.BBoxFromCaches().Intersects( fpA_bbox ) ) )
361 {
362 for( const PAD* padA : fpA->Pads() )
363 testPadAgainstCourtyards( padA, fpB );
364 }
365
366 if( m_drcEngine->IsCancelled() )
367 return false;
368 }
369 }
370
371 return !m_drcEngine->IsCancelled();
372}
373
374
376{
378 DRC_CONSTRAINT constraint;
379
382
384 return false;
385
387 return false;
388
389 return true;
390}
391
392
393namespace detail
394{
396}
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:338
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:168
SEVERITY GetSeverity() const
Definition: drc_rule.h:181
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:160
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:164
BOARD * GetBoard() const
Definition: drc_engine.h:95
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:693
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:393
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
virtual ~DRC_TEST_PROVIDER_COURTYARD_CLEARANCE()=default
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
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)
std::deque< PAD * > & Pads()
Definition: footprint.h:209
const SHAPE_POLY_SET & GetCourtyard(PCB_LAYER_ID aLayer) const
Used in DRC to test the courtyard area (a complex polygon).
Definition: footprint.cpp:2990
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: footprint.cpp:1334
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 REPORT_AUX(s)
#define _(s)
#define MALFORMED_COURTYARDS
@ 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