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 <footprint.h>
27#include <geometry/seg.h>
29#include <drc/drc_engine.h>
30#include <drc/drc_item.h>
31#include <drc/drc_rule.h>
33#include "drc_rtree.h"
34
35/*
36 Board edge clearance test. Checks all items for their mechanical clearances against the board
37 edge.
38 Errors generated:
39 - DRCE_EDGE_CLEARANCE
40 - DRCE_SILK_EDGE_CLEARANCE
41
42 TODO:
43 - separate holes to edge check
44 - tester only looks for edge crossings. it doesn't check if items are inside/outside the board
45 area.
46 - pad test missing!
47*/
48
50{
51public:
55 {
56 }
57
59 {
60 }
61
62 virtual bool Run() override;
63
64 virtual const wxString GetName() const override
65 {
66 return wxT( "edge_clearance" );
67 }
68
69 virtual const wxString GetDescription() const override
70 {
71 return wxT( "Tests items vs board edge clearance" );
72 }
73
74private:
75 bool testAgainstEdge( BOARD_ITEM* item, SHAPE* itemShape, BOARD_ITEM* other,
76 DRC_CONSTRAINT_T aConstraintType, PCB_DRC_CODE aErrorCode );
77
78private:
79 std::vector<PAD*> m_castellatedPads;
81};
82
83
85 BOARD_ITEM* edge,
86 DRC_CONSTRAINT_T aConstraintType,
87 PCB_DRC_CODE aErrorCode )
88{
89 std::shared_ptr<SHAPE> shape;
90
91 if( edge->Type() == PCB_PAD_T )
92 shape = edge->GetEffectiveHoleShape();
93 else
94 shape = edge->GetEffectiveShape( Edge_Cuts );
95
96 auto constraint = m_drcEngine->EvalRules( aConstraintType, edge, item, UNDEFINED_LAYER );
97 int minClearance = constraint.GetValue().Min();
98 int actual;
99 VECTOR2I pos;
100
101 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && minClearance >= 0 )
102 {
103 if( itemShape->Collide( shape.get(), minClearance, &actual, &pos ) )
104 {
105 // Exact clearance is allowed
106 if( minClearance > 0 && actual == minClearance )
107 return true;
108
109 if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T )
110 {
111 // Edge collisions are allowed inside the holes of castellated pads
112 for( PAD* castellatedPad : m_castellatedPads )
113 {
114 if( castellatedPad->GetEffectiveHoleShape()->Collide( pos ) )
115 return true;
116 }
117 }
118
119 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( aErrorCode );
120
121 // Only report clearance info if there is any; otherwise it's just a straight collision
122 if( minClearance > 0 )
123 {
124 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
125 constraint.GetName(),
126 minClearance,
127 actual );
128
129 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
130 }
131
132 drce->SetItems( edge->m_Uuid, item->m_Uuid );
133 drce->SetViolatingRule( constraint.GetParentRule() );
134
135 reportViolation( drce, pos, Edge_Cuts );
136 return false; // don't report violations with multiple edges; one is enough
137 }
138 }
139
140 return true;
141}
142
143
145{
147 {
148 if( !reportPhase( _( "Checking copper to board edge clearances..." ) ) )
149 return false; // DRC cancelled
150 }
152 {
153 if( !reportPhase( _( "Checking silk to board edge clearances..." ) ) )
154 return false; // DRC cancelled
155 }
156 else
157 {
158 reportAux( wxT( "Edge clearance violations ignored. Tests not run." ) );
159 return true; // continue with other tests
160 }
161
163 m_castellatedPads.clear();
164
165 DRC_CONSTRAINT worstClearanceConstraint;
166
167 if( m_drcEngine->QueryWorstConstraint( EDGE_CLEARANCE_CONSTRAINT, worstClearanceConstraint ) )
168 m_largestEdgeClearance = worstClearanceConstraint.GetValue().Min();
169
170 reportAux( wxT( "Worst clearance : %d nm" ), m_largestEdgeClearance );
171
172 /*
173 * Build an RTree of the various edges (including NPTH holes) and margins found on the board.
174 */
175 std::vector<std::unique_ptr<PCB_SHAPE>> edges;
176 DRC_RTREE edgesTree;
177
179 [&]( BOARD_ITEM *item ) -> bool
180 {
181 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
182 STROKE_PARAMS stroke = shape->GetStroke();
183
184 if( item->IsOnLayer( Edge_Cuts ) )
185 stroke.SetWidth( 0 );
186
187 if( shape->GetShape() == SHAPE_T::RECT && !shape->IsFilled() )
188 {
189 // A single rectangle for the board would make the RTree useless, so convert
190 // to 4 edges
191 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
192 edges.back()->SetShape( SHAPE_T::SEGMENT );
193 edges.back()->SetEndX( shape->GetStartX() );
194 edges.back()->SetStroke( stroke );
195 edges.back()->SetParentGroup( nullptr );
196 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
197 edges.back()->SetShape( SHAPE_T::SEGMENT );
198 edges.back()->SetEndY( shape->GetStartY() );
199 edges.back()->SetStroke( stroke );
200 edges.back()->SetParentGroup( nullptr );
201 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
202 edges.back()->SetShape( SHAPE_T::SEGMENT );
203 edges.back()->SetStartX( shape->GetEndX() );
204 edges.back()->SetStroke( stroke );
205 edges.back()->SetParentGroup( nullptr );
206 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
207 edges.back()->SetShape( SHAPE_T::SEGMENT );
208 edges.back()->SetStartY( shape->GetEndY() );
209 edges.back()->SetStroke( stroke );
210 edges.back()->SetParentGroup( nullptr );
211 }
212 else if( shape->GetShape() == SHAPE_T::POLY && !shape->IsFilled() )
213 {
214 // A single polygon for the board would make the RTree useless, so convert
215 // to n edges.
216 SHAPE_LINE_CHAIN poly = shape->GetPolyShape().Outline( 0 );
217
218 for( size_t ii = 0; ii < poly.GetSegmentCount(); ++ii )
219 {
220 SEG seg = poly.CSegment( ii );
221 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
222 edges.back()->SetShape( SHAPE_T::SEGMENT );
223 edges.back()->SetStart( seg.A );
224 edges.back()->SetEnd( seg.B );
225 edges.back()->SetStroke( stroke );
226 edges.back()->SetParentGroup( nullptr );
227 }
228 }
229 else
230 {
231 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
232 edges.back()->SetStroke( stroke );
233 edges.back()->SetParentGroup( nullptr );
234 }
235
236 return true;
237 } );
238
239 for( const std::unique_ptr<PCB_SHAPE>& edge : edges )
240 {
241 for( PCB_LAYER_ID layer : { Edge_Cuts, Margin } )
242 {
243 if( edge->IsOnLayer( layer ) )
244 edgesTree.Insert( edge.get(), layer, m_largestEdgeClearance );
245 }
246 }
247
248 for( FOOTPRINT* footprint : m_board->Footprints() )
249 {
250 for( PAD* pad : footprint->Pads() )
251 {
252 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && pad->HasHole() )
253 {
254 // edge-clearances are for milling tolerances (drilling tolerances are handled
255 // by hole-clearances)
256 if( pad->GetDrillSizeX() != pad->GetDrillSizeY() )
258 }
259
260 if( pad->GetProperty() == PAD_PROP::CASTELLATED )
261 m_castellatedPads.push_back( pad );
262 }
263 }
264
265 /*
266 * Test copper and silk items against the set of edges.
267 */
268 const int progressDelta = 200;
269 int count = 0;
270 int ii = 0;
271
273 [&]( BOARD_ITEM *item ) -> bool
274 {
275 count++;
276 return true;
277 } );
278
280 [&]( BOARD_ITEM *item ) -> bool
281 {
284
285 if( !testCopper && !testSilk )
286 return false; // We're done
287
288 if( !reportProgress( ii++, count, progressDelta ) )
289 return false; // DRC cancelled; we're done
290
291 if( isInvisibleText( item ) )
292 return true; // Continue with other items
293
294 if( item->Type() == PCB_PAD_T
295 && static_cast<PAD*>( item )->GetProperty() == PAD_PROP::CASTELLATED )
296 {
297 return true;
298 }
299
300 const std::shared_ptr<SHAPE>& itemShape = item->GetEffectiveShape();
301
302 for( PCB_LAYER_ID testLayer : { Edge_Cuts, Margin } )
303 {
304 if( testCopper && item->IsOnCopperLayer() )
305 {
306 edgesTree.QueryColliding( item, UNDEFINED_LAYER, testLayer, nullptr,
307 [&]( BOARD_ITEM* edge ) -> bool
308 {
309 return testAgainstEdge( item, itemShape.get(), edge,
310 EDGE_CLEARANCE_CONSTRAINT,
311 DRCE_EDGE_CLEARANCE );
312 },
313 m_largestEdgeClearance );
314 }
315
316 if( testSilk && ( item->IsOnLayer( F_SilkS ) || item->IsOnLayer( B_SilkS ) ) )
317 {
318 if( edgesTree.QueryColliding( item, UNDEFINED_LAYER, testLayer, nullptr,
319 [&]( BOARD_ITEM* edge ) -> bool
320 {
321 return testAgainstEdge( item, itemShape.get(), edge,
322 SILK_CLEARANCE_CONSTRAINT,
323 DRCE_SILK_EDGE_CLEARANCE );
324 },
326 {
327 // violations reported during QueryColliding
328 }
329 else
330 {
331 // TODO: check postion being outside board boundary
332 }
333 }
334 }
335
336 return true;
337 } );
338
339 reportRuleStatistics();
340
341 return !m_drcEngine->IsCancelled();
342}
343
344
345namespace detail
346{
348}
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:70
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:219
virtual bool IsOnLayer(PCB_LAYER_ID aLayer, bool aIncludeCourtyards=false) const
Test to see if this object is on the given layer.
Definition: board_item.h:257
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
Definition: board_item.cpp:229
FOOTPRINTS & Footprints()
Definition: board.h:311
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:141
BOARD * GetBoard() const
Definition: drc_engine.h:89
bool IsErrorLimitExceeded(int error_code)
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:669
bool QueryWorstConstraint(DRC_CONSTRAINT_T aRuleId, DRC_CONSTRAINT &aConstraint)
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition: drc_item.cpp:325
Implement an R-tree for fast spatial and layer indexing of connectable items.
Definition: drc_rtree.h:48
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:104
virtual const wxString GetDescription() const override
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
virtual const wxString GetName() const override
bool testAgainstEdge(BOARD_ITEM *item, SHAPE *itemShape, BOARD_ITEM *other, DRC_CONSTRAINT_T aConstraintType, PCB_DRC_CODE aErrorCode)
static std::vector< KICAD_T > s_allBasicItemsButZones
virtual bool reportPhase(const wxString &aStageName)
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, LSET aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
virtual bool reportProgress(int aCount, int aSize, int aDelta)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer)
virtual void reportAux(wxString fmt,...)
DRC_ENGINE * m_drcEngine
bool isInvisibleText(const BOARD_ITEM *aItem) const
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, int aConstraint, int aActual)
const KIID m_Uuid
Definition: eda_item.h:492
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
int GetStartY() const
Definition: eda_shape.h:121
int GetEndX() const
Definition: eda_shape.h:147
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:247
bool IsFilled() const
Definition: eda_shape.h:90
SHAPE_T GetShape() const
Definition: eda_shape.h:113
int GetEndY() const
Definition: eda_shape.h:146
int GetStartX() const
Definition: eda_shape.h:122
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:532
static LSET AllLayersMask()
Definition: lset.cpp:808
T Min() const
Definition: minoptmax.h:33
Definition: pad.h:60
PAD_PROP GetProperty() const
Definition: pad.h:404
STROKE_PARAMS GetStroke() const override
Definition: pcb_shape.h:71
virtual EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
Definition: pcb_shape.cpp:327
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
virtual size_t GetSegmentCount() const override
const SEG CSegment(int aIndex) const
Return a constant copy of the aIndex segment in the line chain.
SHAPE_LINE_CHAIN & Outline(int aIndex)
An abstract shape on 2D plane.
Definition: shape.h:124
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:179
Simple container to manage line stroke parameters.
Definition: stroke_params.h:88
void SetWidth(int aWidth)
Definition: stroke_params.h:99
The common library.
PCB_DRC_CODE
Definition: drc_item.h:37
@ DRCE_SILK_EDGE_CLEARANCE
Definition: drc_item.h:90
@ DRCE_EDGE_CLEARANCE
Definition: drc_item.h:45
DRC_CONSTRAINT_T
Definition: drc_rule.h:45
@ EDGE_CLEARANCE_CONSTRAINT
Definition: drc_rule.h:50
#define _(s)
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
@ Edge_Cuts
Definition: layer_ids.h:113
@ Margin
Definition: layer_ids.h:114
@ F_SilkS
Definition: layer_ids.h:104
@ UNDEFINED_LAYER
Definition: layer_ids.h:60
@ B_SilkS
Definition: layer_ids.h:103
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
@ NPTH
like PAD_PTH, but not plated
@ CASTELLATED
a pad with a castellated through hole
@ RPT_SEVERITY_IGNORE
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ PCB_FP_SHAPE_T
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:94
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:103
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:101