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 if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T )
106 {
107 // Edge collisions are allowed inside the holes of castellated pads
108 for( PAD* castellatedPad : m_castellatedPads )
109 {
110 if( castellatedPad->GetEffectiveHoleShape()->Collide( pos ) )
111 return true;
112 }
113 }
114
115 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( aErrorCode );
116
117 // Only report clearance info if there is any; otherwise it's just a straight collision
118 if( minClearance > 0 )
119 {
120 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
121 constraint.GetName(),
122 minClearance,
123 actual );
124
125 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
126 }
127
128 drce->SetItems( edge->m_Uuid, item->m_Uuid );
129 drce->SetViolatingRule( constraint.GetParentRule() );
130
131 reportViolation( drce, pos, Edge_Cuts );
132 return false; // don't report violations with multiple edges; one is enough
133 }
134 }
135
136 return true;
137}
138
139
141{
143 {
144 if( !reportPhase( _( "Checking copper to board edge clearances..." ) ) )
145 return false; // DRC cancelled
146 }
148 {
149 if( !reportPhase( _( "Checking silk to board edge clearances..." ) ) )
150 return false; // DRC cancelled
151 }
152 else
153 {
154 reportAux( wxT( "Edge clearance violations ignored. Tests not run." ) );
155 return true; // continue with other tests
156 }
157
159 m_castellatedPads.clear();
160
161 DRC_CONSTRAINT worstClearanceConstraint;
162
163 if( m_drcEngine->QueryWorstConstraint( EDGE_CLEARANCE_CONSTRAINT, worstClearanceConstraint ) )
164 m_largestEdgeClearance = worstClearanceConstraint.GetValue().Min();
165
166 reportAux( wxT( "Worst clearance : %d nm" ), m_largestEdgeClearance );
167
168 /*
169 * Build an RTree of the various edges (including NPTH holes) and margins found on the board.
170 */
171 std::vector<std::unique_ptr<PCB_SHAPE>> edges;
172 DRC_RTREE edgesTree;
173
175 [&]( BOARD_ITEM *item ) -> bool
176 {
177 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
178 STROKE_PARAMS stroke = shape->GetStroke();
179
180 if( item->IsOnLayer( Edge_Cuts ) )
181 stroke.SetWidth( 0 );
182
183 if( shape->GetShape() == SHAPE_T::RECT && !shape->IsFilled() )
184 {
185 // A single rectangle for the board would make the RTree useless, so convert
186 // to 4 edges
187 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
188 edges.back()->SetShape( SHAPE_T::SEGMENT );
189 edges.back()->SetEndX( shape->GetStartX() );
190 edges.back()->SetStroke( stroke );
191 edges.back()->SetParentGroup( nullptr );
192 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
193 edges.back()->SetShape( SHAPE_T::SEGMENT );
194 edges.back()->SetEndY( shape->GetStartY() );
195 edges.back()->SetStroke( stroke );
196 edges.back()->SetParentGroup( nullptr );
197 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
198 edges.back()->SetShape( SHAPE_T::SEGMENT );
199 edges.back()->SetStartX( shape->GetEndX() );
200 edges.back()->SetStroke( stroke );
201 edges.back()->SetParentGroup( nullptr );
202 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
203 edges.back()->SetShape( SHAPE_T::SEGMENT );
204 edges.back()->SetStartY( shape->GetEndY() );
205 edges.back()->SetStroke( stroke );
206 edges.back()->SetParentGroup( nullptr );
207 }
208 else if( shape->GetShape() == SHAPE_T::POLY && !shape->IsFilled() )
209 {
210 // A single polygon for the board would make the RTree useless, so convert
211 // to n edges.
212 SHAPE_LINE_CHAIN poly = shape->GetPolyShape().Outline( 0 );
213
214 for( size_t ii = 0; ii < poly.GetSegmentCount(); ++ii )
215 {
216 SEG seg = poly.CSegment( ii );
217 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
218 edges.back()->SetShape( SHAPE_T::SEGMENT );
219 edges.back()->SetStart( seg.A );
220 edges.back()->SetEnd( seg.B );
221 edges.back()->SetStroke( stroke );
222 edges.back()->SetParentGroup( nullptr );
223 }
224 }
225 else
226 {
227 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
228 edges.back()->SetStroke( stroke );
229 edges.back()->SetParentGroup( nullptr );
230 }
231
232 return true;
233 } );
234
235 for( const std::unique_ptr<PCB_SHAPE>& edge : edges )
236 {
237 for( PCB_LAYER_ID layer : { Edge_Cuts, Margin } )
238 {
239 if( edge->IsOnLayer( layer ) )
240 edgesTree.Insert( edge.get(), layer, m_largestEdgeClearance );
241 }
242 }
243
244 for( FOOTPRINT* footprint : m_board->Footprints() )
245 {
246 for( PAD* pad : footprint->Pads() )
247 {
248 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && pad->HasHole() )
249 {
250 // edge-clearances are for milling tolerances (drilling tolerances are handled
251 // by hole-clearances)
252 if( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG )
254 }
255
256 if( pad->GetProperty() == PAD_PROP::CASTELLATED )
257 m_castellatedPads.push_back( pad );
258 }
259 }
260
261 /*
262 * Test copper and silk items against the set of edges.
263 */
264 const int progressDelta = 200;
265 int count = 0;
266 int ii = 0;
267
269 [&]( BOARD_ITEM *item ) -> bool
270 {
271 count++;
272 return true;
273 } );
274
276 [&]( BOARD_ITEM *item ) -> bool
277 {
280
281 if( !testCopper && !testSilk )
282 return false; // We're done
283
284 if( !reportProgress( ii++, count, progressDelta ) )
285 return false; // DRC cancelled; we're done
286
287 if( isInvisibleText( item ) )
288 return true; // Continue with other items
289
290 if( item->Type() == PCB_PAD_T
291 && static_cast<PAD*>( item )->GetProperty() == PAD_PROP::CASTELLATED )
292 {
293 return true;
294 }
295
296 const std::shared_ptr<SHAPE>& itemShape = item->GetEffectiveShape();
297
298 for( PCB_LAYER_ID testLayer : { Edge_Cuts, Margin } )
299 {
300 if( testCopper && item->IsOnCopperLayer() )
301 {
302 edgesTree.QueryColliding( item, UNDEFINED_LAYER, testLayer, nullptr,
303 [&]( BOARD_ITEM* edge ) -> bool
304 {
305 return testAgainstEdge( item, itemShape.get(), edge,
306 EDGE_CLEARANCE_CONSTRAINT,
307 DRCE_EDGE_CLEARANCE );
308 },
309 m_largestEdgeClearance );
310 }
311
312 if( testSilk && ( item->IsOnLayer( F_SilkS ) || item->IsOnLayer( B_SilkS ) ) )
313 {
314 if( edgesTree.QueryColliding( item, UNDEFINED_LAYER, testLayer, nullptr,
315 [&]( BOARD_ITEM* edge ) -> bool
316 {
317 return testAgainstEdge( item, itemShape.get(), edge,
318 SILK_CLEARANCE_CONSTRAINT,
319 DRCE_SILK_EDGE_CLEARANCE );
320 },
322 {
323 // violations reported during QueryColliding
324 }
325 else
326 {
327 // TODO: check postion being outside board boundary
328 }
329 }
330 }
331
332 return true;
333 } );
334
335 reportRuleStatistics();
336
337 return !m_drcEngine->IsCancelled();
338}
339
340
341namespace detail
342{
344}
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:58
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition: board_item.h:245
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 std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
Definition: board_item.cpp:229
FOOTPRINTS & Footprints()
Definition: board.h:307
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:139
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:671
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:494
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:530
static LSET AllLayersMask()
Definition: lset.cpp:808
T Min() const
Definition: minoptmax.h:33
Definition: pad.h:59
PAD_PROP GetProperty() const
Definition: pad.h:398
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:123
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:178
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:44
@ EDGE_CLEARANCE_CONSTRAINT
Definition: drc_rule.h:49
#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
@ PAD_DRILL_SHAPE_OBLONG
Definition: pad_shapes.h:71
@ 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