KiCad PCB EDA Suite
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;
233 msg.Printf( _( "(%s clearance %s; actual %s)" ),
234 constraint.GetName(),
235 MessageTextFromValue( clearance ),
236 MessageTextFromValue( actual ) );
237
238 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
239 drce->SetViolatingRule( constraint.GetParentRule() );
240 }
241
242 drce->SetItems( fpA, fpB );
243 reportViolation( drce, pos, F_CrtYd );
244 }
245 }
246 }
247
248 //
249 // Check courtyard-to-courtyard collisions on back of board.
250 //
251
252 if( backA.OutlineCount() > 0 && backB.OutlineCount() > 0
253 && 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;
267 msg.Printf( _( "(%s clearance %s; actual %s)" ),
268 constraint.GetName(),
269 MessageTextFromValue( clearance ),
270 MessageTextFromValue( actual ) );
271
272 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
273 drce->SetViolatingRule( constraint.GetParentRule() );
274 }
275
276 drce->SetItems( fpA, fpB );
277 reportViolation( drce, pos, B_CrtYd );
278 }
279 }
280 }
281
282 //
283 // Check pad-hole-to-courtyard collisions on front and back of board.
284 //
285 // NB: via holes are not checked. There is a presumption that a physical object goes
286 // through a pad hole, which is not the case for via holes.
287 //
288
289 auto testPadAgainstCourtyards =
290 [&]( const PAD* pad, const FOOTPRINT* fp )
291 {
292 int errorCode = 0;
293
294 if( pad->GetAttribute() == PAD_ATTRIB::PTH )
295 errorCode = DRCE_PTH_IN_COURTYARD;
296 else if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
297 errorCode = DRCE_NPTH_IN_COURTYARD;
298 else
299 return;
300
301 if( m_drcEngine->IsErrorLimitExceeded( errorCode ) )
302 return;
303
304 if( pad->HasHole() )
305 {
306 std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
307 const SHAPE_POLY_SET& front = fp->GetCourtyard( F_CrtYd );
308 const SHAPE_POLY_SET& back = fp->GetCourtyard( B_CrtYd );
309
310 if( front.OutlineCount() > 0 && front.Collide( hole.get(), 0 ) )
311 {
312 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( errorCode );
313 drce->SetItems( pad, fp );
314 reportViolation( drce, pad->GetPosition(), F_CrtYd );
315 }
316 else if( back.OutlineCount() > 0 && back.Collide( hole.get(), 0 ) )
317 {
318 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( errorCode );
319 drce->SetItems( pad, fp );
320 reportViolation( drce, pad->GetPosition(), B_CrtYd );
321 }
322 }
323 };
324
325 if( ( frontA.OutlineCount() > 0 && frontA_worstCaseBBox.Intersects( fpB_bbox ) )
326 || ( backA.OutlineCount() > 0 && backA_worstCaseBBox.Intersects( fpB_bbox ) ) )
327 {
328 for( const PAD* padB : fpB->Pads() )
329 testPadAgainstCourtyards( padB, fpA );
330 }
331
332 if( ( frontB.OutlineCount() > 0 && frontB.BBoxFromCaches().Intersects( fpA_bbox ) )
333 || ( backB.OutlineCount() > 0 && backB.BBoxFromCaches().Intersects( fpA_bbox ) ) )
334 {
335 for( const PAD* padA : fpA->Pads() )
336 testPadAgainstCourtyards( padA, fpB );
337 }
338
339 if( m_drcEngine->IsCancelled() )
340 return false;
341 }
342 }
343
344 return !m_drcEngine->IsCancelled();
345}
346
347
349{
351 DRC_CONSTRAINT constraint;
352
355
356 reportAux( wxT( "Worst courtyard clearance : %d nm" ), m_largestCourtyardClearance );
357
359 return false;
360
362 return false;
363
364 return true;
365}
366
367
368namespace detail
369{
371}
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:50
FOOTPRINTS & Footprints()
Definition: board.h:307
bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:269
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:506
wxString GetName() const
Definition: drc_rule.h:147
SEVERITY GetSeverity() const
Definition: drc_rule.h:160
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:139
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:143
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:671
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:325
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
virtual const wxString GetDescription() const override
virtual bool reportPhase(const wxString &aStageName)
virtual bool reportProgress(int aCount, int aSize, int aDelta)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer)
virtual void reportAux(wxString fmt,...)
DRC_ENGINE * m_drcEngine
PADS & Pads()
Definition: footprint.h:174
const SHAPE_POLY_SET & GetCourtyard(PCB_LAYER_ID aLayer) const
Used in DRC to test the courtyard area (a complex polygon).
Definition: footprint.cpp:2183
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: footprint.cpp:782
T Min() const
Definition: minoptmax.h:33
Definition: pad.h:58
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 vertices in a given outline/hole.
const BOX2I BBoxFromCaches() const
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
A lower-precision version of StringFromValue().
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:51
#define _(s)
#define MALFORMED_COURTYARDS
@ FP_ALLOW_MISSING_COURTYARD
Definition: footprint.h:74
@ 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:60
@ F_Cu
Definition: layer_ids.h:64
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
@ NPTH
like PAD_PTH, but not plated
@ PTH
Plated through hole pad.
@ RPT_SEVERITY_IGNORE