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:
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
71 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_MALFORMED_COURTYARD)
72 || !m_drcEngine->IsErrorLimitExceeded( DRCE_MISSING_COURTYARD) )
73 {
74 if( !reportPhase( _( "Checking footprint courtyard definitions..." ) ) )
75 return false; // DRC cancelled
76 }
77 else if( !m_drcEngine->IsErrorLimitExceeded( DRCE_OVERLAPPING_FOOTPRINTS) )
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 {
98 if( m_drcEngine->IsErrorLimitExceeded( DRCE_MALFORMED_COURTYARD) )
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->SetErrorDetail( 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 {
116 if( m_drcEngine->IsErrorLimitExceeded( DRCE_MISSING_COURTYARD ) )
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:
164 if( m_drcEngine->IsErrorLimitExceeded( DRCE_OVERLAPPING_FOOTPRINTS)
165 && m_drcEngine->IsErrorLimitExceeded( DRCE_PTH_IN_COURTYARD )
166 && m_drcEngine->IsErrorLimitExceeded( DRCE_NPTH_IN_COURTYARD ) )
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
176 && m_drcEngine->IsErrorLimitExceeded( DRCE_PTH_IN_COURTYARD )
177 && m_drcEngine->IsErrorLimitExceeded( DRCE_NPTH_IN_COURTYARD ) )
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
198 && m_drcEngine->IsErrorLimitExceeded( DRCE_PTH_IN_COURTYARD )
199 && m_drcEngine->IsErrorLimitExceeded( DRCE_NPTH_IN_COURTYARD ) )
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() )
221 && !m_drcEngine->IsErrorLimitExceeded( DRCE_OVERLAPPING_FOOTPRINTS ) )
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 {
230 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_OVERLAPPING_FOOTPRINTS );
231
232 if( clearance > 0 )
233 {
234 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
235 constraint.GetName(),
236 clearance,
237 actual ) );
238 }
239
240 drcItem->SetViolatingRule( constraint.GetParentRule() );
241 drcItem->SetItems( fpA, fpB );
242 reportTwoShapeGeometry( drcItem, pos, &frontA, &frontB, F_CrtYd, actual );
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() )
251 && !m_drcEngine->IsErrorLimitExceeded( DRCE_OVERLAPPING_FOOTPRINTS ) )
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 {
260 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_OVERLAPPING_FOOTPRINTS );
261
262 if( clearance > 0 )
263 {
264 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
265 constraint.GetName(),
266 clearance,
267 actual ) );
268 }
269
270 drcItem->SetViolatingRule( constraint.GetParentRule() );
271 drcItem->SetItems( fpA, fpB );
272 reportTwoShapeGeometry( drcItem, pos, &backA, &backB, B_CrtYd, actual );
273 }
274 }
275 }
276
277 //
278 // Check pad-hole-to-courtyard collisions on front and back of board.
279 //
280 // NB: via holes are not checked. There is a presumption that a physical object goes
281 // through a pad hole, which is not the case for via holes.
282 //
283 bool checkFront = false;
284 bool checkBack = false;
285
286 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, F_Cu );
287 clearance = constraint.GetValue().Min();
288
289 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
290 checkFront = true;
291
292 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpB, fpA, F_Cu );
293 clearance = constraint.GetValue().Min();
294
295 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
296 checkFront = true;
297
298 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, B_Cu );
299 clearance = constraint.GetValue().Min();
300
301 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
302 checkBack = true;
303
304 constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpB, fpA, B_Cu );
305 clearance = constraint.GetValue().Min();
306
307 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
308 checkBack = true;
309
310 auto testPadAgainstCourtyards =
311 [&]( const PAD* pad, const FOOTPRINT* fp )
312 {
313 int errorCode = 0;
314
315 if( pad->GetProperty() == PAD_PROP::HEATSINK )
316 return;
317 else 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{
373 m_board = m_drcEngine->GetBoard();
374 DRC_CONSTRAINT constraint;
375
376 if( m_drcEngine->QueryWorstConstraint( COURTYARD_CLEARANCE_CONSTRAINT, constraint ) )
378
380 return false;
381
383 return false;
384
385 return true;
386}
387
388
389namespace detail
390{
392}
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:83
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:194
SEVERITY GetSeverity() const
Definition drc_rule.h:207
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:186
DRC_RULE * GetParentRule() const
Definition drc_rule.h:190
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:400
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)
void reportTwoShapeGeometry(std::shared_ptr< DRC_ITEM > &aDrcItem, const VECTOR2I &aMarkerPos, const SHAPE *aShape1, const SHAPE *aShape2, PCB_LAYER_ID aLayer, int aDistance)
void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer, const std::function< void(PCB_MARKER *)> &aPathGenerator=[](PCB_MARKER *){})
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:224
const SHAPE_POLY_SET & GetCourtyard(PCB_LAYER_ID aLayer) const
Used in DRC to test the courtyard area (a complex polygon).
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
T Min() const
Definition minoptmax.h:33
Definition pad.h:55
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:66
@ DRCE_MISSING_COURTYARD
Definition drc_item.h:67
@ DRCE_PTH_IN_COURTYARD
Definition drc_item.h:70
@ DRCE_MALFORMED_COURTYARD
Definition drc_item.h:68
@ DRCE_NPTH_IN_COURTYARD
Definition drc_item.h:71
@ 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
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:103
@ PTH
Plated through hole pad.
Definition padstack.h:98
@ HEATSINK
a pad used as heat sink, usually in SMD footprints
Definition padstack.h:120
@ RPT_SEVERITY_IGNORE
int clearance
int actual
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695