KiCad PCB EDA Suite
drc_test_provider_hole_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 
24 #include <common.h>
25 #include <pad.h>
26 #include <track.h>
27 #include <geometry/shape_segment.h>
28 #include <geometry/shape_circle.h>
29 #include <drc/drc_item.h>
30 #include <drc/drc_rule.h>
32 #include "drc_rtree.h"
33 
34 /*
35  Holes clearance test. Checks pad and via holes for their mechanical clearances.
36  Generated errors:
37  - DRCE_DRILLED_HOLES_TOO_CLOSE
38 
39  TODO: vias-in-smd-pads check
40 */
41 
43 {
44 public:
47  m_board( nullptr )
48  {
49  }
50 
52  {
53  }
54 
55  virtual bool Run() override;
56 
57  virtual const wxString GetName() const override
58  {
59  return "hole_clearance";
60  };
61 
62  virtual const wxString GetDescription() const override
63  {
64  return "Tests hole to hole spacing";
65  }
66 
67  virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override;
68 
69  int GetNumPhases() const override;
70 
71 private:
72  bool testHoleAgainstHole( BOARD_ITEM* aItem, SHAPE_CIRCLE* aHole, BOARD_ITEM* aOther );
73 
76 };
77 
78 
79 static std::shared_ptr<SHAPE_CIRCLE> getDrilledHoleShape( BOARD_ITEM* aItem )
80 {
81  if( aItem->Type() == PCB_VIA_T )
82  {
83  VIA* via = static_cast<VIA*>( aItem );
84  return std::make_shared<SHAPE_CIRCLE>( via->GetCenter(), via->GetDrillValue() / 2 );
85  }
86  else if( aItem->Type() == PCB_PAD_T )
87  {
88  PAD* pad = static_cast<PAD*>( aItem );
89  return std::make_shared<SHAPE_CIRCLE>( pad->GetPosition(), pad->GetDrillSize().x / 2 );
90  }
91 
92  return std::make_shared<SHAPE_CIRCLE>( VECTOR2I( 0, 0 ), 0 );
93 }
94 
95 
97 {
99  {
100  reportAux( "Hole-to-hole violations ignored. Tests not run." );
101  return true; // continue with other tests
102  }
103 
105 
106  DRC_CONSTRAINT worstClearanceConstraint;
107 
108  if( m_drcEngine->QueryWorstConstraint( HOLE_TO_HOLE_CONSTRAINT, worstClearanceConstraint ) )
109  {
110  m_largestClearance = worstClearanceConstraint.GetValue().Min();
111  reportAux( "Worst hole to hole : %d nm", m_largestClearance );
112  }
113  else
114  {
115  reportAux( "No hole to hole constraints found. Skipping check." );
116  return true; // continue with other tests
117  }
118 
119  if( !reportPhase( _( "Checking hole to hole clearances..." ) ) )
120  return false; // DRC cancelled
121 
122  // This is the number of tests between 2 calls to the progress bar
123  const size_t delta = 50;
124  size_t count = 0;
125  size_t ii = 0;
126 
127  m_holeTree.clear();
128 
129  auto countItems =
130  [&]( BOARD_ITEM* item ) -> bool
131  {
132  if( item->Type() == PCB_PAD_T )
133  ++count;
134  else if( item->Type() == PCB_VIA_T )
135  ++count;
136 
137  return true;
138  };
139 
140  auto addToHoleTree =
141  [&]( BOARD_ITEM* item ) -> bool
142  {
143  if( !reportProgress( ii++, count, delta ) )
144  return false;
145 
146  if( item->Type() == PCB_PAD_T )
147  {
148  PAD* pad = static_cast<PAD*>( item );
149 
150  // We only care about drilled (ie: round) holes
151  if( pad->GetDrillSize().x && pad->GetDrillSize().x == pad->GetDrillSize().y )
153  }
154  else if( item->Type() == PCB_VIA_T )
155  {
156  VIA* via = static_cast<VIA*>( item );
157 
158  // We only care about mechanically drilled (ie: non-laser) holes
159  if( via->GetViaType() == VIATYPE::THROUGH )
161  }
162 
163  return true;
164  };
165 
167 
168  count *= 2; // One for adding to tree; one for checking
169 
171 
172  std::map< std::pair<BOARD_ITEM*, BOARD_ITEM*>, int> checkedPairs;
173 
174  for( TRACK* track : m_board->Tracks() )
175  {
176  if( track->Type() != PCB_VIA_T )
177  continue;
178 
179  VIA* via = static_cast<VIA*>( track );
180 
181  if( !reportProgress( ii++, count, delta ) )
182  return false; // DRC cancelled
183 
184  // We only care about mechanically drilled (ie: non-laser) holes
185  if( via->GetViaType() == VIATYPE::THROUGH )
186  {
187  std::shared_ptr<SHAPE_CIRCLE> holeShape = getDrilledHoleShape( via );
188 
190  // Filter:
191  [&]( BOARD_ITEM* other ) -> bool
192  {
193  BOARD_ITEM* a = via;
194  BOARD_ITEM* b = other;
195 
196  // store canonical order so we don't collide in both directions
197  // (a:b and b:a)
198  if( static_cast<void*>( a ) > static_cast<void*>( b ) )
199  std::swap( a, b );
200 
201  if( checkedPairs.count( { a, b } ) )
202  {
203  return false;
204  }
205  else
206  {
207  checkedPairs[ { a, b } ] = 1;
208  return true;
209  }
210  },
211  // Visitor:
212  [&]( BOARD_ITEM* other ) -> bool
213  {
214  return testHoleAgainstHole( via, holeShape.get(), other );
215  },
217  }
218  }
219 
220  checkedPairs.clear();
221 
222  for( FOOTPRINT* footprint : m_board->Footprints() )
223  {
224  for( PAD* pad : footprint->Pads() )
225  {
226  if( !reportProgress( ii++, count, delta ) )
227  return false; // DRC cancelled
228 
229  // We only care about drilled (ie: round) holes
230  if( pad->GetDrillSize().x && pad->GetDrillSize().x == pad->GetDrillSize().y )
231  {
232  std::shared_ptr<SHAPE_CIRCLE> holeShape = getDrilledHoleShape( pad );
233 
235  // Filter:
236  [&]( BOARD_ITEM* other ) -> bool
237  {
238  BOARD_ITEM* a = pad;
239  BOARD_ITEM* b = other;
240 
241  // store canonical order so we don't collide in both directions
242  // (a:b and b:a)
243  if( static_cast<void*>( a ) > static_cast<void*>( b ) )
244  std::swap( a, b );
245 
246  if( checkedPairs.count( { a, b } ) )
247  {
248  return false;
249  }
250  else
251  {
252  checkedPairs[ { a, b } ] = 1;
253  return true;
254  }
255  },
256  // Visitor:
257  [&]( BOARD_ITEM* other ) -> bool
258  {
259  return testHoleAgainstHole( pad, holeShape.get(), other );
260  },
262  }
263  }
264  }
265 
267 
268  return true;
269 }
270 
271 
273  BOARD_ITEM* aOther )
274 {
276  return false;
277 
278  std::shared_ptr<SHAPE_CIRCLE> otherHole = getDrilledHoleShape( aOther );
279 
280  // Holes with identical locations are allowable
281  if( aHole->GetCenter() == otherHole->GetCenter() )
282  return true;
283 
284  int actual = ( aHole->GetCenter() - otherHole->GetCenter() ).EuclideanNorm();
285  actual = std::max( 0, actual - aHole->GetRadius() - otherHole->GetRadius() );
286 
287  auto constraint = m_drcEngine->EvalRules( HOLE_TO_HOLE_CONSTRAINT, aItem, aOther,
288  UNDEFINED_LAYER /* holes pierce all layers */ );
289  int minClearance = constraint.GetValue().Min();
290 
291  if( minClearance >= 0 && actual < minClearance )
292  {
293  std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_DRILLED_HOLES_TOO_CLOSE );
294 
295  m_msg.Printf( _( "(%s min %s; actual %s)" ),
296  constraint.GetName(),
297  MessageTextFromValue( userUnits(), minClearance ),
298  MessageTextFromValue( userUnits(), actual ) );
299 
300  drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
301  drce->SetItems( aItem, aOther );
302  drce->SetViolatingRule( constraint.GetParentRule() );
303 
304  reportViolation( drce, (wxPoint) aHole->GetCenter() );
305  }
306 
307  return true;
308 }
309 
310 
312 {
313  return 1;
314 }
315 
316 
318 {
319  return { HOLE_TO_HOLE_CONSTRAINT };
320 }
321 
322 
323 namespace detail
324 {
326 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:148
virtual const wxString GetName() const override
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:125
Definition: track.h:343
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition: drc_item.cpp:245
virtual std::set< DRC_CONSTRAINT_T > GetConstraintTypes() const override
int GetRadius() const
Definition: shape_circle.h:102
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:82
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULUS > dummy
bool IsErrorLimitExceeded(int error_code)
virtual bool reportProgress(int aCount, int aSize, int aDelta)
class PAD, a pad in a footprint
Definition: typeinfo.h:89
const VECTOR2I GetCenter() const
Definition: shape_circle.h:107
T Min() const
Definition: minoptmax.h:33
VECTOR2< int > VECTOR2I
Definition: vector2d.h:623
virtual void reportRuleStatistics()
void Insert(BOARD_ITEM *aItem, int aWorstClearance=0, int aLayer=UNDEFINED_LAYER)
Function Insert() Inserts an item into the tree.
Definition: drc_rtree.h:86
const wxSize & GetDrillSize() const
Definition: pad.h:242
bool QueryWorstConstraint(DRC_CONSTRAINT_T aRuleId, DRC_CONSTRAINT &aConstraint)
int GetDrillValue() const
Function GetDrillValue "calculates" the drill value for vias (m-Drill if > 0, or default drill value ...
Definition: track.cpp:173
BOARD * GetBoard() const
Definition: drc_engine.h:87
virtual bool reportPhase(const wxString &aStageName)
virtual const wxString GetDescription() const override
FOOTPRINTS & Footprints()
Definition: board.h:303
void clear()
Function RemoveAll() Removes all items from the RTree.
Definition: drc_rtree.h:145
virtual wxPoint GetCenter() const
This defaults to the center of the bounding box if not overridden.
Definition: board_item.h:114
static LSET AllLayersMask()
Definition: lset.cpp:787
EDA_UNITS userUnits() const
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintId, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
Definition: drc_engine.cpp:736
bool testHoleAgainstHole(BOARD_ITEM *aItem, SHAPE_CIRCLE *aHole, BOARD_ITEM *aOther)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, wxPoint aMarkerPos)
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, LSET aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:121
wxPoint GetPosition() const override
Definition: pad.h:177
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
#define _(s)
Definition: 3d_actions.cpp:33
DRC_ENGINE * m_drcEngine
VIATYPE GetViaType() const
Definition: track.h:373
The common library.
virtual bool Run() override
Runs this provider against the given PCB with configured options (if any).
static std::shared_ptr< SHAPE_CIRCLE > getDrilledHoleShape(BOARD_ITEM *aItem)
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
Definition: pad.h:60
DRC_RTREE - Implements an R-tree for fast spatial and layer indexing of connectable items.
Definition: drc_rtree.h:43
TRACKS & Tracks()
Definition: board.h:300
virtual void reportAux(wxString fmt,...)
Definition: track.h:83
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:162
int QueryColliding(BOARD_ITEM *aRefItem, PCB_LAYER_ID aRefLayer, PCB_LAYER_ID aTargetLayer, std::function< bool(BOARD_ITEM *)> aFilter=nullptr, std::function< bool(BOARD_ITEM *)> aVisitor=nullptr, int aClearance=0) const
This is a fast test which essentially does bounding-box overlap given a worst-case clearance.
Definition: drc_rtree.h:190