KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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
44{
45public:
49 {
50 }
51
53 {
54 }
55
56 virtual bool Run() override;
57
58 virtual const wxString GetName() const override
59 {
60 return wxT( "edge_clearance" );
61 }
62
63 virtual const wxString GetDescription() const override
64 {
65 return wxT( "Tests items vs board edge clearance" );
66 }
67
68private:
69 bool testAgainstEdge( BOARD_ITEM* item, SHAPE* itemShape, BOARD_ITEM* other,
70 DRC_CONSTRAINT_T aConstraintType, PCB_DRC_CODE aErrorCode );
71
72private:
73 std::vector<PAD*> m_castellatedPads;
75};
76
77
79 BOARD_ITEM* edge,
80 DRC_CONSTRAINT_T aConstraintType,
81 PCB_DRC_CODE aErrorCode )
82{
83 std::shared_ptr<SHAPE> shape;
84
85 if( edge->Type() == PCB_PAD_T )
86 shape = edge->GetEffectiveHoleShape();
87 else
88 shape = edge->GetEffectiveShape( Edge_Cuts );
89
90 auto constraint = m_drcEngine->EvalRules( aConstraintType, edge, item, UNDEFINED_LAYER );
91 int minClearance = constraint.GetValue().Min();
92 int actual;
93 VECTOR2I pos;
94
95 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && minClearance >= 0 )
96 {
97 if( itemShape->Collide( shape.get(), minClearance, &actual, &pos ) )
98 {
99 // Exact clearance is allowed
100 if( minClearance > 0 && actual == minClearance )
101 return true;
102
103 if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T )
104 {
105 // Edge collisions are allowed inside the holes of castellated pads
106 for( PAD* castellatedPad : m_castellatedPads )
107 {
108 if( castellatedPad->GetEffectiveHoleShape()->Collide( pos ) )
109 return true;
110 }
111 }
112
113 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( aErrorCode );
114
115 // Only report clearance info if there is any; otherwise it's just a straight collision
116 if( minClearance > 0 )
117 {
118 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
119 constraint.GetName(),
120 minClearance,
121 actual );
122
123 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
124 }
125
126 drce->SetItems( edge->m_Uuid, item->m_Uuid );
127 drce->SetViolatingRule( constraint.GetParentRule() );
128
129 reportViolation( drce, pos, Edge_Cuts );
130
131 if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T )
132 {
134 }
135 else
136 {
137 return false; // don't report violations with multiple edges; one is enough
138 }
139 }
140 }
141
142 return true;
143}
144
145
147{
149 {
150 if( !reportPhase( _( "Checking copper to board edge clearances..." ) ) )
151 return false; // DRC cancelled
152 }
154 {
155 if( !reportPhase( _( "Checking silk to board edge clearances..." ) ) )
156 return false; // DRC cancelled
157 }
158 else
159 {
160 reportAux( wxT( "Edge clearance violations ignored. Tests not run." ) );
161 return true; // continue with other tests
162 }
163
165 m_castellatedPads.clear();
166
167 DRC_CONSTRAINT worstClearanceConstraint;
168
169 if( m_drcEngine->QueryWorstConstraint( EDGE_CLEARANCE_CONSTRAINT, worstClearanceConstraint ) )
170 m_largestEdgeClearance = worstClearanceConstraint.GetValue().Min();
171
172 reportAux( wxT( "Worst clearance : %d nm" ), m_largestEdgeClearance );
173
174 /*
175 * Build an RTree of the various edges (including NPTH holes) and margins found on the board.
176 */
177 std::vector<std::unique_ptr<PCB_SHAPE>> edges;
178 DRC_RTREE edgesTree;
179
181 [&]( BOARD_ITEM *item ) -> bool
182 {
183 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
184 STROKE_PARAMS stroke = shape->GetStroke();
185
186 if( item->IsOnLayer( Edge_Cuts ) )
187 stroke.SetWidth( 0 );
188
189 if( shape->GetShape() == SHAPE_T::RECTANGLE && !shape->IsFilled() )
190 {
191 // A single rectangle for the board would make the RTree useless, so convert
192 // to 4 edges
193 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
194 edges.back()->SetShape( SHAPE_T::SEGMENT );
195 edges.back()->SetEndX( shape->GetStartX() );
196 edges.back()->SetStroke( stroke );
197 edges.back()->SetParentGroup( nullptr );
198 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
199 edges.back()->SetShape( SHAPE_T::SEGMENT );
200 edges.back()->SetEndY( shape->GetStartY() );
201 edges.back()->SetStroke( stroke );
202 edges.back()->SetParentGroup( nullptr );
203 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
204 edges.back()->SetShape( SHAPE_T::SEGMENT );
205 edges.back()->SetStartX( shape->GetEndX() );
206 edges.back()->SetStroke( stroke );
207 edges.back()->SetParentGroup( nullptr );
208 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
209 edges.back()->SetShape( SHAPE_T::SEGMENT );
210 edges.back()->SetStartY( shape->GetEndY() );
211 edges.back()->SetStroke( stroke );
212 edges.back()->SetParentGroup( nullptr );
213 }
214 else if( shape->GetShape() == SHAPE_T::POLY && !shape->IsFilled() )
215 {
216 // A single polygon for the board would make the RTree useless, so convert
217 // to n edges.
218 SHAPE_LINE_CHAIN poly = shape->GetPolyShape().Outline( 0 );
219
220 for( size_t ii = 0; ii < poly.GetSegmentCount(); ++ii )
221 {
222 SEG seg = poly.CSegment( ii );
223 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
224 edges.back()->SetShape( SHAPE_T::SEGMENT );
225 edges.back()->SetStart( seg.A );
226 edges.back()->SetEnd( seg.B );
227 edges.back()->SetStroke( stroke );
228 edges.back()->SetParentGroup( nullptr );
229 }
230 }
231 else
232 {
233 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
234 edges.back()->SetStroke( stroke );
235 edges.back()->SetParentGroup( nullptr );
236 }
237
238 return true;
239 } );
240
241 for( const std::unique_ptr<PCB_SHAPE>& edge : edges )
242 {
243 for( PCB_LAYER_ID layer : { Edge_Cuts, Margin } )
244 {
245 if( edge->IsOnLayer( layer ) )
246 edgesTree.Insert( edge.get(), layer, m_largestEdgeClearance );
247 }
248 }
249
250 for( FOOTPRINT* footprint : m_board->Footprints() )
251 {
252 for( PAD* pad : footprint->Pads() )
253 {
254 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && pad->HasHole() )
255 {
256 // edge-clearances are for milling tolerances (drilling tolerances are handled
257 // by hole-clearances)
258 if( pad->GetDrillSizeX() != pad->GetDrillSizeY() )
260 }
261
262 if( pad->GetProperty() == PAD_PROP::CASTELLATED )
263 m_castellatedPads.push_back( pad );
264 }
265 }
266
267 /*
268 * Test copper and silk items against the set of edges.
269 */
270 const int progressDelta = 200;
271 int count = 0;
272 int ii = 0;
273
275 [&]( BOARD_ITEM *item ) -> bool
276 {
277 count++;
278 return true;
279 } );
280
282 [&]( BOARD_ITEM *item ) -> bool
283 {
286
287 if( !testCopper && !testSilk )
288 return false; // All limits exceeded; we're done
289
290 if( !reportProgress( ii++, count, progressDelta ) )
291 return false; // DRC cancelled; we're done
292
293 if( isInvisibleText( item ) )
294 return true; // Continue with other items
295
296 if( item->Type() == PCB_PAD_T )
297 {
298 PAD* pad = static_cast<PAD*>( item );
299
300 if( pad->GetProperty() == PAD_PROP::CASTELLATED
301 || pad->GetAttribute() == PAD_ATTRIB::CONN )
302 {
303 return true; // Continue with other items
304 }
305 }
306
307 std::vector<PCB_LAYER_ID> layersToTest;
308
309 switch( item->Type() )
310 {
311 case PCB_PAD_T:
312 layersToTest = static_cast<PAD*>( item )->Padstack().UniqueLayers();
313 break;
314
315 case PCB_VIA_T:
316 layersToTest = static_cast<PCB_VIA*>( item )->Padstack().UniqueLayers();
317 break;
318
319 default:
320 layersToTest = { UNDEFINED_LAYER };
321 }
322
323 for( PCB_LAYER_ID shapeLayer : layersToTest )
324 {
325 const std::shared_ptr<SHAPE>& itemShape = item->GetEffectiveShape( shapeLayer );
326
327 for( PCB_LAYER_ID testLayer : { Edge_Cuts, Margin } )
328 {
329 if( testCopper && item->IsOnCopperLayer() )
330 {
331 edgesTree.QueryColliding( item, shapeLayer, testLayer, nullptr,
332 [&]( BOARD_ITEM* edge ) -> bool
333 {
334 return testAgainstEdge( item, itemShape.get(), edge,
335 EDGE_CLEARANCE_CONSTRAINT,
336 DRCE_EDGE_CLEARANCE );
337 },
338 m_largestEdgeClearance );
339 }
340
341 if( testSilk && ( item->IsOnLayer( F_SilkS ) || item->IsOnLayer( B_SilkS ) ) )
342 {
343 if( edgesTree.QueryColliding( item, shapeLayer, testLayer, nullptr,
344 [&]( BOARD_ITEM* edge ) -> bool
345 {
346 return testAgainstEdge( item, itemShape.get(), edge,
347 SILK_CLEARANCE_CONSTRAINT,
348 DRCE_SILK_EDGE_CLEARANCE );
349 },
350 m_largestEdgeClearance ) )
351 {
352 // violations reported during QueryColliding
353 }
354 else
355 {
356 // TODO: check postion being outside board boundary
357 }
358 }
359 }
360 }
361
362 return true;
363 } );
364
365 reportRuleStatistics();
366
367 return !m_drcEngine->IsCancelled();
368}
369
370
371namespace detail
372{
374}
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:79
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition: board_item.h:319
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:278
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
Definition: board_item.cpp:288
const FOOTPRINTS & Footprints() const
Definition: board.h:331
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:152
BOARD * GetBoard() const
Definition: drc_engine.h:96
bool GetReportAllTrackErrors() const
Definition: drc_engine.h:170
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:693
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:395
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)
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, double aConstraint, double aActual)
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 void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer, DRC_CUSTOM_MARKER_HANDLER *aCustomHandler=nullptr)
DRC_ENGINE * m_drcEngine
bool isInvisibleText(const BOARD_ITEM *aItem) const
void reportAux(const wxString &aMsg)
virtual bool reportProgress(size_t aCount, size_t aSize, size_t aDelta=1)
const KIID m_Uuid
Definition: eda_item.h:489
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
int GetStartY() const
Definition: eda_shape.h:138
int GetEndX() const
Definition: eda_shape.h:176
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:286
bool IsFilled() const
Definition: eda_shape.h:98
SHAPE_T GetShape() const
Definition: eda_shape.h:132
int GetEndY() const
Definition: eda_shape.h:175
int GetStartX() const
Definition: eda_shape.h:139
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:36
static LSET AllLayersMask()
Definition: lset.cpp:711
T Min() const
Definition: minoptmax.h:33
Definition: pad.h:54
STROKE_PARAMS GetStroke() const override
Definition: pcb_shape.h:89
EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
Definition: pcb_shape.cpp:709
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)
Return the reference to aIndex-th outline in the set.
An abstract shape on 2D plane.
Definition: shape.h:126
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:181
Simple container to manage line stroke parameters.
Definition: stroke_params.h:79
void SetWidth(int aWidth)
Definition: stroke_params.h:90
The common library.
PCB_DRC_CODE
Definition: drc_item.h:37
@ DRCE_SILK_EDGE_CLEARANCE
Definition: drc_item.h:98
@ DRCE_EDGE_CLEARANCE
Definition: drc_item.h:46
DRC_CONSTRAINT_T
Definition: drc_rule.h:47
@ EDGE_CLEARANCE_CONSTRAINT
Definition: drc_rule.h:53
#define _(s)
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ Edge_Cuts
Definition: layer_ids.h:112
@ Margin
Definition: layer_ids.h:113
@ F_SilkS
Definition: layer_ids.h:100
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
@ B_SilkS
Definition: layer_ids.h:101
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
@ RPT_SEVERITY_IGNORE
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ 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:98
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:96