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-2020 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>
29 #include <geometry/shape_segment.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  TODO: do an actual clearance check instead of polygon intersection. Treat closed outlines
43  as filled and allow open curves in the courtyard.
44 */
45 
47 {
48 public:
50  {
51  m_isRuleDriven = false;
52  }
53 
55  {
56  }
57 
58  virtual bool Run() override;
59 
60  virtual const wxString GetName() const override
61  {
62  return "courtyard_clearance";
63  }
64 
65  virtual const wxString GetDescription() const override
66  {
67  return "Tests footprints' courtyard clearance";
68  }
69 
70  virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override;
71 
72  int GetNumPhases() const override;
73 
74 private:
76 
78 };
79 
80 
82 {
83  const int delta = 100; // This is the number of tests between 2 calls to the progress bar
84 
85  // Detects missing (or malformed) footprint courtyards
88  {
89  if( !reportPhase( _( "Checking footprint courtyard definitions..." ) ) )
90  return false; // DRC cancelled
91  }
93  {
94  if( !reportPhase( _( "Gathering footprint courtyards..." ) ) )
95  return false; // DRC cancelled
96  }
97  else
98  {
99  reportAux( "All courtyard violations ignored. Tests not run." );
100  return true; // continue with other tests
101  }
102 
103  int ii = 0;
104 
105  for( FOOTPRINT* footprint : m_board->Footprints() )
106  {
107  if( !reportProgress( ii++, m_board->Footprints().size(), delta ) )
108  return false; // DRC cancelled
109 
110  if( ( footprint->GetFlags() & MALFORMED_COURTYARDS ) != 0 )
111  {
113  continue;
114 
115  OUTLINE_ERROR_HANDLER errorHandler =
116  [&]( const wxString& msg, BOARD_ITEM* , BOARD_ITEM* , const wxPoint& pt )
117  {
118  std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MALFORMED_COURTYARD );
119  drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
120  drcItem->SetItems( footprint );
121  reportViolation( drcItem, pt );
122  };
123 
124  // Re-run courtyard tests to generate DRC_ITEMs
125  footprint->BuildPolyCourtyards( &errorHandler );
126  }
127  else if( footprint->GetPolyCourtyard( F_CrtYd ).OutlineCount() == 0
128  && footprint->GetPolyCourtyard( B_CrtYd ).OutlineCount() == 0 )
129  {
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() );
136  }
137  else
138  {
139  footprint->GetPolyCourtyard( F_CrtYd ).BuildBBoxCaches();
140  footprint->GetPolyCourtyard( B_CrtYd ).BuildBBoxCaches();
141  }
142  }
143 
144  return true;
145 }
146 
147 
149 {
150  const int delta = 100; // This is the number of tests between 2 calls to the progress bar
151 
152  if( !reportPhase( _( "Checking footprints for overlapping courtyards..." ) ) )
153  return false; // DRC cancelled
154 
155  int ii = 0;
156 
157  for( auto itA = m_board->Footprints().begin(); itA != m_board->Footprints().end(); itA++ )
158  {
159  if( !reportProgress( ii++, m_board->Footprints().size(), delta ) )
160  return false; // DRC cancelled
161 
165  {
166  return true; // continue with other tests
167  }
168 
169  FOOTPRINT* fpA = *itA;
170  const SHAPE_POLY_SET& frontA = fpA->GetPolyCourtyard( F_CrtYd );
171  const SHAPE_POLY_SET& backA = fpA->GetPolyCourtyard( B_CrtYd );
172 
173  if( frontA.OutlineCount() == 0 && backA.OutlineCount() == 0
176  {
177  // No courtyards defined and no hole testing against other footprint's courtyards
178  continue;
179  }
180 
181  BOX2I frontBBox = frontA.BBoxFromCaches();
182  BOX2I backBBox = backA.BBoxFromCaches();
183 
184  frontBBox.Inflate( m_largestClearance );
185  backBBox.Inflate( m_largestClearance );
186 
187  EDA_RECT fpABBox = fpA->GetBoundingBox();
188 
189  for( auto itB = itA + 1; itB != m_board->Footprints().end(); itB++ )
190  {
191  FOOTPRINT* fpB = *itB;
192  EDA_RECT fpBBBox = fpB->GetBoundingBox();
193  const SHAPE_POLY_SET& frontB = fpB->GetPolyCourtyard( F_CrtYd );
194  const SHAPE_POLY_SET& backB = fpB->GetPolyCourtyard( B_CrtYd );
195  DRC_CONSTRAINT constraint;
196  int clearance;
197  int actual;
198  VECTOR2I pos;
199 
200  if( frontA.OutlineCount() > 0 && frontB.OutlineCount() > 0
201  && frontBBox.Intersects( frontB.BBoxFromCaches() ) )
202  {
203  constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB,
204  F_Cu );
205  clearance = constraint.GetValue().Min();
206 
207  if( clearance >= 0 && frontA.Collide( &frontB, clearance, &actual, &pos ) )
208  {
210 
211  if( clearance > 0 )
212  {
213  m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
214  constraint.GetName(),
215  MessageTextFromValue( userUnits(), clearance ),
216  MessageTextFromValue( userUnits(), actual ) );
217 
218  drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
219  drce->SetViolatingRule( constraint.GetParentRule() );
220  }
221 
222  drce->SetItems( fpA, fpB );
223  reportViolation( drce, (wxPoint) pos );
224  }
225  }
226 
227  if( backA.OutlineCount() > 0 && backB.OutlineCount() > 0
228  && backBBox.Intersects( backB.BBoxFromCaches() ) )
229  {
230  constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB,
231  B_Cu );
232  clearance = constraint.GetValue().Min();
233 
234  if( clearance >= 0 && backA.Collide( &backB, clearance, &actual, &pos ) )
235  {
237 
238  if( clearance > 0 )
239  {
240  m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
241  constraint.GetName(),
242  MessageTextFromValue( userUnits(), clearance ),
243  MessageTextFromValue( userUnits(), actual ) );
244 
245  drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
246  drce->SetViolatingRule( constraint.GetParentRule() );
247  }
248 
249  drce->SetItems( fpA, fpB );
250  reportViolation( drce, (wxPoint) pos );
251  }
252  }
253 
254  auto testPadAgainstCourtyards =
255  [&]( const PAD* pad, const FOOTPRINT* footprint )
256  {
257  int errorCode = 0;
258 
259  if( pad->GetAttribute() == PAD_ATTRIB::PTH )
260  errorCode = DRCE_PTH_IN_COURTYARD;
261  else if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
262  errorCode = DRCE_NPTH_IN_COURTYARD;
263  else
264  return;
265 
266  if( m_drcEngine->IsErrorLimitExceeded( errorCode ) )
267  return;
268 
269  const SHAPE_SEGMENT* hole = pad->GetEffectiveHoleShape();
270  const SHAPE_POLY_SET& front = footprint->GetPolyCourtyard( F_CrtYd );
271  const SHAPE_POLY_SET& back = footprint->GetPolyCourtyard( B_CrtYd );
272 
273  if( ( front.OutlineCount() > 0 && front.Collide( hole, 0 ) )
274  || ( back.OutlineCount() > 0 && back.Collide( hole, 0 ) ) )
275  {
276  std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( errorCode );
277  drce->SetItems( pad, footprint );
278  reportViolation( drce, pad->GetPosition() );
279  }
280  };
281 
282  if( ( frontA.OutlineCount() > 0 && frontA.BBoxFromCaches().Intersects( fpBBBox ) )
283  || ( backA.OutlineCount() > 0 && backA.BBoxFromCaches().Intersects( fpBBBox ) ) )
284  {
285  for( const PAD* padB : fpB->Pads() )
286  testPadAgainstCourtyards( padB, fpA );
287  }
288 
289  if( ( frontB.OutlineCount() > 0 && frontB.BBoxFromCaches().Intersects( fpABBox ) )
290  || ( backB.OutlineCount() > 0 && backB.BBoxFromCaches().Intersects( fpABBox ) ) )
291  {
292  for( const PAD* padA : fpA->Pads() )
293  testPadAgainstCourtyards( padA, fpB );
294  }
295  }
296  }
297 
298  return true;
299 }
300 
301 
303 {
305  DRC_CONSTRAINT constraint;
306 
308  m_largestClearance = constraint.GetValue().Min();
309 
310  reportAux( "Worst courtyard clearance : %d nm", m_largestClearance );
311 
313  return false;
314 
315  if( !testCourtyardClearances() )
316  return false;
317 
318  return true;
319 }
320 
321 
323 {
324  return 2;
325 }
326 
327 
329 {
331 }
332 
333 
334 namespace detail
335 {
337 }
wxString MessageTextFromValue(EDA_UNITS aUnits, int aValue, bool aAddUnitLabel, EDA_DATA_TYPE aType)
Convert a value to a string using double notation.
Definition: base_units.cpp:104
int OutlineCount() const
Return the number of vertices in a given outline/hole.
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition: drc_item.cpp:266
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:49
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const wxPoint &aMarkerPos)
const BOX2I BBoxFromCaches() const
bool IsErrorLimitExceeded(int error_code)
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
virtual bool reportProgress(int aCount, int aSize, int aDelta)
T Min() const
Definition: minoptmax.h:33
wxString GetName() const
Definition: drc_rule.h:130
PADS & Pads()
Definition: footprint.h:168
Plated through hole pad.
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:217
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:126
like PAD_PTH, but not plated
bool QueryWorstConstraint(DRC_CONSTRAINT_T aRuleId, DRC_CONSTRAINT &aConstraint)
BOARD * GetBoard() const
Definition: drc_engine.h:88
virtual bool reportPhase(const wxString &aStageName)
Represent a set of closed polygons.
FOOTPRINTS & Footprints()
Definition: board.h:234
#define _(s)
const SHAPE_POLY_SET & GetPolyCourtyard(PCB_LAYER_ID aLayer) const
Used in DRC to test the courtyard area (a complex polygon).
Definition: footprint.h:695
EDA_UNITS userUnits() const
virtual std::set< DRC_CONSTRAINT_T > GetConstraintTypes() const override
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:760
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,...
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:281
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:122
virtual const wxString GetDescription() const override
DRC_ENGINE * m_drcEngine
const EDA_RECT GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: footprint.cpp:736
Definition: layer_ids.h:71
Handle the component boundary box.
Definition: eda_rect.h:42
constexpr int delta
const std::function< void(const wxString &msg, BOARD_ITEM *itemA, BOARD_ITEM *itemB, const wxPoint &pt)> OUTLINE_ERROR_HANDLER
Definition: pad.h:57
#define MALFORMED_COURTYARDS
virtual void reportAux(wxString fmt,...)