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
287 auto testPadAgainstCourtyards =
288 [&]( const PAD* pad, const FOOTPRINT* fp )
289 {
290 int errorCode = 0;
291
292 if( pad->GetAttribute() == PAD_ATTRIB::PTH )
293 errorCode = DRCE_PTH_IN_COURTYARD;
294 else if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
295 errorCode = DRCE_NPTH_IN_COURTYARD;
296 else
297 return;
298
299 if( m_drcEngine->IsErrorLimitExceeded( errorCode ) )
300 return;
301
302 if( pad->HasHole() )
303 {
304 std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
305 const SHAPE_POLY_SET& front = fp->GetCourtyard( F_CrtYd );
306 const SHAPE_POLY_SET& back = fp->GetCourtyard( B_CrtYd );
307
308 if( front.OutlineCount() > 0 && front.Collide( hole.get(), 0 ) )
309 {
310 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( errorCode );
311 drce->SetItems( pad, fp );
312 reportViolation( drce, pad->GetPosition(), F_CrtYd );
313 }
314 else if( back.OutlineCount() > 0 && back.Collide( hole.get(), 0 ) )
315 {
316 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( errorCode );
317 drce->SetItems( pad, fp );
318 reportViolation( drce, pad->GetPosition(), B_CrtYd );
319 }
320 }
321 };
322
323 if( ( frontA.OutlineCount() > 0 && frontA_worstCaseBBox.Intersects( fpB_bbox ) )
324 || ( backA.OutlineCount() > 0 && backA_worstCaseBBox.Intersects( fpB_bbox ) ) )
325 {
326 for( const PAD* padB : fpB->Pads() )
327 testPadAgainstCourtyards( padB, fpA );
328 }
329
330 if( ( frontB.OutlineCount() > 0 && frontB.BBoxFromCaches().Intersects( fpA_bbox ) )
331 || ( backB.OutlineCount() > 0 && backB.BBoxFromCaches().Intersects( fpA_bbox ) ) )
332 {
333 for( const PAD* padA : fpA->Pads() )
334 testPadAgainstCourtyards( padA, fpB );
335 }
336
337 if( m_drcEngine->IsCancelled() )
338 return false;
339 }
340 }
341
342 return !m_drcEngine->IsCancelled();
343}
344
345
347{
349 DRC_CONSTRAINT constraint;
350
353
354 reportAux( wxT( "Worst courtyard clearance : %d nm" ), m_largestCourtyardClearance );
355
357 return false;
358
360 return false;
361
362 return true;
363}
364
365
366namespace detail
367{
369}
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:317
bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:270
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:507
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:331
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:188
const SHAPE_POLY_SET & GetCourtyard(PCB_LAYER_ID aLayer) const
Used in DRC to test the courtyard area (a complex polygon).
Definition: footprint.cpp:2554
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: footprint.cpp:978
T Min() const
Definition: minoptmax.h:33
Definition: pad.h:59
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:118
@ B_Cu
Definition: layer_ids.h:96
@ B_CrtYd
Definition: layer_ids.h:117
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
@ F_Cu
Definition: layer_ids.h:65
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
@ RPT_SEVERITY_IGNORE