KiCad PCB EDA Suite
drc_test_provider_hole_to_hole.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 <board_design_settings.h>
26 #include <footprint.h>
27 #include <pad.h>
28 #include <pcb_track.h>
29 #include <geometry/shape_segment.h>
30 #include <geometry/shape_circle.h>
31 #include <drc/drc_engine.h>
32 #include <drc/drc_item.h>
33 #include <drc/drc_rule.h>
35 #include "drc_rtree.h"
36 
37 /*
38  Holes clearance test. Checks pad and via holes for their mechanical clearances.
39  Generated errors:
40  - DRCE_DRILLED_HOLES_TOO_CLOSE
41  - DRCE_DRILLED_HOLES_COLOCATED
42 
43  TODO: vias-in-smd-pads check
44 */
45 
47 {
48 public:
51  m_board( nullptr )
52  {
53  }
54 
56  {
57  }
58 
59  virtual bool Run() override;
60 
61  virtual const wxString GetName() const override
62  {
63  return "hole_to_hole_clearance";
64  };
65 
66  virtual const wxString GetDescription() const override
67  {
68  return "Tests hole to hole spacing";
69  }
70 
71  virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override;
72 
73  int GetNumPhases() const override;
74 
75 private:
76  bool testHoleAgainstHole( BOARD_ITEM* aItem, SHAPE_CIRCLE* aHole, BOARD_ITEM* aOther );
77 
80 };
81 
82 
83 static std::shared_ptr<SHAPE_CIRCLE> getDrilledHoleShape( BOARD_ITEM* aItem )
84 {
85  if( aItem->Type() == PCB_VIA_T )
86  {
87  PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
88  return std::make_shared<SHAPE_CIRCLE>( via->GetCenter(), via->GetDrillValue() / 2 );
89  }
90  else if( aItem->Type() == PCB_PAD_T )
91  {
92  PAD* pad = static_cast<PAD*>( aItem );
93  return std::make_shared<SHAPE_CIRCLE>( pad->GetPosition(), pad->GetDrillSize().x / 2 );
94  }
95 
96  return std::make_shared<SHAPE_CIRCLE>( VECTOR2I( 0, 0 ), 0 );
97 }
98 
99 
101 {
104  {
105  reportAux( "Hole to hole violations ignored. Tests not run." );
106  return true; // continue with other tests
107  }
108 
110 
111  DRC_CONSTRAINT worstClearanceConstraint;
112 
113  if( m_drcEngine->QueryWorstConstraint( HOLE_TO_HOLE_CONSTRAINT, worstClearanceConstraint ) )
114  {
115  m_largestClearance = worstClearanceConstraint.GetValue().Min();
116  reportAux( "Worst hole to hole : %d nm", m_largestClearance );
117  }
118  else
119  {
120  reportAux( "No hole to hole constraints found. Skipping check." );
121  return true; // continue with other tests
122  }
123 
124  if( !reportPhase( _( "Checking hole to hole clearances..." ) ) )
125  return false; // DRC cancelled
126 
127  // This is the number of tests between 2 calls to the progress bar
128  const size_t delta = 50;
129  size_t count = 0;
130  size_t ii = 0;
131 
132  m_holeTree.clear();
133 
134  auto countItems =
135  [&]( BOARD_ITEM* item ) -> bool
136  {
137  if( item->Type() == PCB_PAD_T )
138  ++count;
139  else if( item->Type() == PCB_VIA_T )
140  ++count;
141 
142  return true;
143  };
144 
145  auto addToHoleTree =
146  [&]( BOARD_ITEM* item ) -> bool
147  {
148  if( !reportProgress( ii++, count, delta ) )
149  return false;
150 
151  if( item->Type() == PCB_PAD_T )
152  {
153  PAD* pad = static_cast<PAD*>( item );
154 
155  // We only care about drilled (ie: round) holes
156  if( pad->GetDrillSize().x && pad->GetDrillSize().x == pad->GetDrillSize().y )
158  }
159  else if( item->Type() == PCB_VIA_T )
160  {
161  PCB_VIA* via = static_cast<PCB_VIA*>( item );
162 
163  // We only care about mechanically drilled (ie: non-laser) holes
164  if( via->GetViaType() == VIATYPE::THROUGH )
166  }
167 
168  return true;
169  };
170 
172 
173  count *= 2; // One for adding to tree; one for checking
174 
176 
177  std::map< std::pair<BOARD_ITEM*, BOARD_ITEM*>, int> checkedPairs;
178 
179  for( PCB_TRACK* track : m_board->Tracks() )
180  {
181  if( track->Type() != PCB_VIA_T )
182  continue;
183 
184  PCB_VIA* via = static_cast<PCB_VIA*>( track );
185 
186  if( !reportProgress( ii++, count, delta ) )
187  return false; // DRC cancelled
188 
189  // We only care about mechanically drilled (ie: non-laser) holes
190  if( via->GetViaType() == VIATYPE::THROUGH )
191  {
192  std::shared_ptr<SHAPE_CIRCLE> holeShape = getDrilledHoleShape( via );
193 
195  // Filter:
196  [&]( BOARD_ITEM* other ) -> bool
197  {
198  BOARD_ITEM* a = via;
199  BOARD_ITEM* b = other;
200 
201  // store canonical order so we don't collide in both directions
202  // (a:b and b:a)
203  if( static_cast<void*>( a ) > static_cast<void*>( b ) )
204  std::swap( a, b );
205 
206  if( checkedPairs.count( { a, b } ) )
207  {
208  return false;
209  }
210  else
211  {
212  checkedPairs[ { a, b } ] = 1;
213  return true;
214  }
215  },
216  // Visitor:
217  [&]( BOARD_ITEM* other ) -> bool
218  {
219  return testHoleAgainstHole( via, holeShape.get(), other );
220  },
222  }
223  }
224 
225  checkedPairs.clear();
226 
227  for( FOOTPRINT* footprint : m_board->Footprints() )
228  {
229  for( PAD* pad : footprint->Pads() )
230  {
231  if( !reportProgress( ii++, count, delta ) )
232  return false; // DRC cancelled
233 
234  // We only care about drilled (ie: round) holes
235  if( pad->GetDrillSize().x && pad->GetDrillSize().x == pad->GetDrillSize().y )
236  {
237  std::shared_ptr<SHAPE_CIRCLE> holeShape = getDrilledHoleShape( pad );
238 
240  // Filter:
241  [&]( BOARD_ITEM* other ) -> bool
242  {
243  BOARD_ITEM* a = pad;
244  BOARD_ITEM* b = other;
245 
246  // store canonical order so we don't collide in both directions
247  // (a:b and b:a)
248  if( static_cast<void*>( a ) > static_cast<void*>( b ) )
249  std::swap( a, b );
250 
251  if( checkedPairs.count( { a, b } ) )
252  {
253  return false;
254  }
255  else
256  {
257  checkedPairs[ { a, b } ] = 1;
258  return true;
259  }
260  },
261  // Visitor:
262  [&]( BOARD_ITEM* other ) -> bool
263  {
264  return testHoleAgainstHole( pad, holeShape.get(), other );
265  },
267  }
268  }
269  }
270 
272 
273  return true;
274 }
275 
276 
278  BOARD_ITEM* aOther )
279 {
282 
283  if( !reportCoLocation && !reportHole2Hole )
284  return false;
285 
286  std::shared_ptr<SHAPE_CIRCLE> otherHole = getDrilledHoleShape( aOther );
287  int epsilon = m_board->GetDesignSettings().GetDRCEpsilon();
288  SEG::ecoord epsilon_sq = SEG::Square( epsilon );
289 
290  // Holes at same location generate a separate violation
291  if( ( aHole->GetCenter() - otherHole->GetCenter() ).SquaredEuclideanNorm() < epsilon_sq )
292  {
293  if( reportCoLocation )
294  {
295  std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_DRILLED_HOLES_COLOCATED );
296  drce->SetItems( aItem, aOther );
297  reportViolation( drce, (wxPoint) aHole->GetCenter() );
298  }
299  }
300  else if( reportHole2Hole )
301  {
302  int actual = ( aHole->GetCenter() - otherHole->GetCenter() ).EuclideanNorm();
303  actual = std::max( 0, actual - aHole->GetRadius() - otherHole->GetRadius() );
304 
305  auto constraint = m_drcEngine->EvalRules( HOLE_TO_HOLE_CONSTRAINT, aItem, aOther,
306  UNDEFINED_LAYER /* holes pierce all layers */ );
307  int minClearance = constraint.GetValue().Min() - epsilon;
308 
309  if( minClearance >= 0 && actual < minClearance )
310  {
311  std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_DRILLED_HOLES_TOO_CLOSE );
312 
313  m_msg.Printf( _( "(%s min %s; actual %s)" ),
314  constraint.GetName(),
315  MessageTextFromValue( userUnits(), minClearance ),
316  MessageTextFromValue( userUnits(), actual ) );
317 
318  drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
319  drce->SetItems( aItem, aOther );
320  drce->SetViolatingRule( constraint.GetParentRule() );
321 
322  reportViolation( drce, (wxPoint) aHole->GetCenter() );
323  }
324  }
325 
326  return true;
327 }
328 
329 
331 {
332  return 1;
333 }
334 
335 
336 std::set<DRC_CONSTRAINT_T> DRC_TEST_PROVIDER_HOLE_TO_HOLE::GetConstraintTypes() const
337 {
338  return { HOLE_TO_HOLE_CONSTRAINT };
339 }
340 
341 
342 namespace detail
343 {
345 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:146
static std::shared_ptr< SHAPE_CIRCLE > getDrilledHoleShape(BOARD_ITEM *aItem)
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
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition: drc_item.cpp:266
int GetRadius() const
Definition: shape_circle.h:107
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)
bool IsErrorLimitExceeded(int error_code)
VECTOR2I::extended_type ecoord
Definition: seg.h:43
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:112
T Min() const
Definition: minoptmax.h:33
VECTOR2< int > VECTOR2I
Definition: vector2d.h:623
static SEG::ecoord Square(int a)
Definition: seg.h:122
virtual std::set< DRC_CONSTRAINT_T > GetConstraintTypes() const override
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:590
virtual void reportRuleStatistics()
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
virtual const wxString GetDescription() const override
bool QueryWorstConstraint(DRC_CONSTRAINT_T aRuleId, DRC_CONSTRAINT &aConstraint)
BOARD * GetBoard() const
Definition: drc_engine.h:88
virtual bool reportPhase(const wxString &aStageName)
FOOTPRINTS & Footprints()
Definition: board.h:233
void clear()
Remove all items from the RTree.
Definition: drc_rtree.h:120
#define _(s)
static LSET AllLayersMask()
Definition: lset.cpp:787
EDA_UNITS userUnits() const
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:765
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:122
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
DRC_ENGINE * m_drcEngine
Definition: layer_ids.h:71
The common library.
void Insert(BOARD_ITEM *aItem, PCB_LAYER_ID aLayer, int aWorstClearance=0)
Insert an item into the tree on a particular layer with an optional worst clearance.
Definition: drc_rtree.h:86
constexpr int delta
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
Definition: pad.h:57
Implement an R-tree for fast spatial and layer indexing of connectable items.
Definition: drc_rtree.h:44
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
TRACKS & Tracks()
Definition: board.h:230
virtual void reportAux(wxString fmt,...)
bool testHoleAgainstHole(BOARD_ITEM *aItem, SHAPE_CIRCLE *aHole, BOARD_ITEM *aOther)
virtual const wxString GetName() const override
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:113
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:165