KiCad PCB EDA Suite
drc_test_provider_edge_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 
24 #include <common.h>
25 #include <pcb_shape.h>
26 #include <geometry/seg.h>
27 #include <geometry/shape_segment.h>
28 #include <drc/drc_engine.h>
29 #include <drc/drc_item.h>
30 #include <drc/drc_rule.h>
32 #include "drc_rtree.h"
33 
34 /*
35  Board edge clearance test. Checks all items for their mechanical clearances against the board
36  edge.
37  Errors generated:
38  - DRCE_EDGE_CLEARANCE
39 
40  TODO:
41  - separate holes to edge check
42  - tester only looks for edge crossings. it doesn't check if items are inside/outside the board
43  area.
44  - pad test missing!
45 */
46 
48 {
49 public:
52  {
53  }
54 
56  {
57  }
58 
59  virtual bool Run() override;
60 
61  virtual const wxString GetName() const override
62  {
63  return wxT( "edge_clearance" );
64  }
65 
66  virtual const wxString GetDescription() const override
67  {
68  return wxT( "Tests items vs board edge clearance" );
69  }
70 
71  virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override;
72 
73  int GetNumPhases() const override;
74 
75 private:
76  bool testAgainstEdge( BOARD_ITEM* item, SHAPE* itemShape, BOARD_ITEM* other,
77  DRC_CONSTRAINT_T aConstraintType, PCB_DRC_CODE aErrorCode );
78 };
79 
80 
82  BOARD_ITEM* edge,
83  DRC_CONSTRAINT_T aConstraintType,
84  PCB_DRC_CODE aErrorCode )
85 {
86  const std::shared_ptr<SHAPE>& edgeShape = edge->GetEffectiveShape( Edge_Cuts );
87 
88  auto constraint = m_drcEngine->EvalRules( aConstraintType, edge, item, item->GetLayer() );
89  int minClearance = constraint.GetValue().Min();
90  int actual;
91  VECTOR2I pos;
92 
93  if( minClearance >= 0 && itemShape->Collide( edgeShape.get(), minClearance, &actual, &pos ) )
94  {
95  std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( aErrorCode );
96 
97  // Only report clearance info if there is any; otherwise it's just a straight collision
98  if( minClearance > 0 )
99  {
100  wxString msg;
101  msg.Printf( _( "(%s clearance %s; actual %s)" ),
102  constraint.GetName(),
103  MessageTextFromValue( userUnits(), minClearance ),
104  MessageTextFromValue( userUnits(), actual ) );
105 
106  drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
107  }
108 
109  drce->SetItems( edge->m_Uuid, item->m_Uuid );
110  drce->SetViolatingRule( constraint.GetParentRule() );
111 
112  reportViolation( drce, (wxPoint) pos );
113  return false; // don't report violations with multiple edges; one is enough
114  }
115 
116  return true;
117 }
118 
119 
121 {
123  {
124  if( !reportPhase( _( "Checking copper to board edge clearances..." ) ) )
125  return false; // DRC cancelled
126  }
128  {
129  if( !reportPhase( _( "Checking silk to board edge clearances..." ) ) )
130  return false; // DRC cancelled
131  }
132  else
133  {
134  reportAux( wxT( "Edge clearance violations ignored. Tests not run." ) );
135  return true; // continue with other tests
136  }
137 
139 
140  DRC_CONSTRAINT worstClearanceConstraint;
141 
142  if( m_drcEngine->QueryWorstConstraint( EDGE_CLEARANCE_CONSTRAINT, worstClearanceConstraint ) )
143  m_largestClearance = worstClearanceConstraint.GetValue().Min();
144 
145  reportAux( wxT( "Worst clearance : %d nm" ), m_largestClearance );
146 
147  std::vector<std::unique_ptr<PCB_SHAPE>> edges; // we own these
148  DRC_RTREE edgesTree;
149  std::vector<BOARD_ITEM*> boardItems; // we don't own these
150 
151  auto queryBoardOutlineItems =
152  [&]( BOARD_ITEM *item ) -> bool
153  {
154  PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
155 
156  if( shape->GetShape() == SHAPE_T::RECT )
157  {
158  // A single rectangle for the board would make the RTree useless, so convert
159  // to 4 edges
160  edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
161  edges.back()->SetShape( SHAPE_T::SEGMENT );
162  edges.back()->SetEndX( shape->GetStartX() );
163  edges.back()->SetWidth( 0 );
164  edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
165  edges.back()->SetShape( SHAPE_T::SEGMENT );
166  edges.back()->SetEndY( shape->GetStartY() );
167  edges.back()->SetWidth( 0 );
168  edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
169  edges.back()->SetShape( SHAPE_T::SEGMENT );
170  edges.back()->SetStartX( shape->GetEndX() );
171  edges.back()->SetWidth( 0 );
172  edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
173  edges.back()->SetShape( SHAPE_T::SEGMENT );
174  edges.back()->SetStartY( shape->GetEndY() );
175  edges.back()->SetWidth( 0 );
176  return true;
177  }
178  else if( shape->GetShape() == SHAPE_T::POLY )
179  {
180  // A single polygon for the board would make the RTree useless, so convert
181  // to n edges.
182  SHAPE_LINE_CHAIN poly = shape->GetPolyShape().Outline( 0 );
183 
184  for( size_t ii = 0; ii < poly.GetSegmentCount(); ++ii )
185  {
186  SEG seg = poly.CSegment( ii );
187  edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
188  edges.back()->SetShape( SHAPE_T::SEGMENT );
189  edges.back()->SetStart((wxPoint) seg.A );
190  edges.back()->SetEnd((wxPoint) seg.B );
191  edges.back()->SetWidth( 0 );
192  }
193  }
194 
195  edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
196  edges.back()->SetWidth( 0 );
197  return true;
198  };
199 
200  auto queryBoardGeometryItems =
201  [&]( BOARD_ITEM *item ) -> bool
202  {
203  if( !isInvisibleText( item ) )
204  boardItems.push_back( item );
205 
206  return true;
207  };
208 
210  queryBoardOutlineItems );
211  forEachGeometryItem( s_allBasicItemsButZones, LSET::AllLayersMask(), queryBoardGeometryItems );
212 
213  for( const std::unique_ptr<PCB_SHAPE>& edge : edges )
214  {
215  for( PCB_LAYER_ID layer : { Edge_Cuts, Margin } )
216  {
217  if( edge->IsOnLayer( layer ) )
218  edgesTree.Insert( edge.get(), layer, m_largestClearance );
219  }
220  }
221 
222  wxString val;
223  wxGetEnv( "WXTRACE", &val );
224 
225  drc_dbg( 2, "outline: %d items, board: %d items\n",
226  (int) edges.size(), (int) boardItems.size() );
227 
228  // This is the number of tests between 2 calls to the progress bar
229  const int delta = 50;
230  int ii = 0;
231 
232  for( BOARD_ITEM* item : boardItems )
233  {
236 
237  if( !testCopper && !testSilk )
238  break;
239 
240  if( !reportProgress( ii++, boardItems.size(), delta ) )
241  return false; // DRC cancelled
242 
243  const std::shared_ptr<SHAPE>& itemShape = item->GetEffectiveShape();
244 
245  for( PCB_LAYER_ID testLayer : { Edge_Cuts, Margin } )
246  {
247  if( testCopper && item->IsOnCopperLayer() )
248  {
249  edgesTree.QueryColliding( item, UNDEFINED_LAYER, testLayer, nullptr,
250  [&]( BOARD_ITEM* edge ) -> bool
251  {
252  return testAgainstEdge( item, itemShape.get(), edge,
255  },
257  }
258 
259  if( testSilk && ( item->GetLayer() == F_SilkS || item->GetLayer() == B_SilkS ) )
260  {
261  if( edgesTree.QueryColliding( item, UNDEFINED_LAYER, testLayer, nullptr,
262  [&]( BOARD_ITEM* edge ) -> bool
263  {
264  return testAgainstEdge( item, itemShape.get(), edge,
265  SILK_CLEARANCE_CONSTRAINT,
266  DRCE_SILK_MASK_CLEARANCE );
267  },
269  {
270  // violations reported during QueryColliding
271  }
272  else
273  {
274  // TODO: check postion being outside board boundary
275  }
276  }
277  }
278  }
279 
281 
282  return true;
283 }
284 
285 
287 {
288  return 1;
289 }
290 
291 
293 {
295 }
296 
297 
298 namespace detail
299 {
301 }
int GetStartY()
Definition: eda_shape.h:107
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
virtual size_t GetSegmentCount() const override
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition: drc_item.cpp:266
bool isInvisibleText(const BOARD_ITEM *aItem) const
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:49
virtual const wxString GetName() const override
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const wxPoint &aMarkerPos)
virtual EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
Definition: pcb_shape.cpp:175
virtual std::set< DRC_CONSTRAINT_T > GetConstraintTypes() const override
bool IsErrorLimitExceeded(int error_code)
int GetEndY()
Definition: eda_shape.h:132
virtual bool reportProgress(int aCount, int aSize, int aDelta)
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:93
T Min() const
Definition: minoptmax.h:33
PCB_DRC_CODE
Definition: drc_item.h:34
int GetStartX()
Definition: eda_shape.h:108
virtual void reportRuleStatistics()
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT) const
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Definition: board_item.cpp:181
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const
Check if the boundary of shape (this) lies closer to the point aP than aClearance,...
Definition: shape.h:165
int GetEndX()
Definition: eda_shape.h:133
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
DRC_CONSTRAINT_T
Definition: drc_rule.h:41
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:516
bool QueryWorstConstraint(DRC_CONSTRAINT_T aRuleId, DRC_CONSTRAINT &aConstraint)
bool testAgainstEdge(BOARD_ITEM *item, SHAPE *itemShape, BOARD_ITEM *other, DRC_CONSTRAINT_T aConstraintType, PCB_DRC_CODE aErrorCode)
BOARD * GetBoard() const
Definition: drc_engine.h:88
virtual bool reportPhase(const wxString &aStageName)
SHAPE_LINE_CHAIN & Outline(int aIndex)
virtual const wxString GetDescription() const override
#define _(s)
static LSET AllLayersMask()
Definition: lset.cpp:796
An abstract shape on 2D plane.
Definition: shape.h:116
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:759
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:227
const KIID m_Uuid
Definition: eda_item.h:474
Definition: seg.h:40
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, LSET aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:122
const SEG CSegment(int aIndex) const
Return a constant copy of the aIndex segment in the line chain.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:65
static std::vector< KICAD_T > s_allBasicItemsButZones
DRC_ENGINE * m_drcEngine
VECTOR2I A
Definition: seg.h:48
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:93
constexpr int delta
SHAPE_T GetShape() const
Definition: eda_shape.h:101
#define drc_dbg(level, fmt,...)
Definition: drc_engine.h:57
Implement an R-tree for fast spatial and layer indexing of connectable items.
Definition: drc_rtree.h:46
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:90
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:143
virtual void reportAux(wxString fmt,...)
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:183
VECTOR2I B
Definition: seg.h:49