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 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::RECTANGLE && !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; // All limits exceeded; 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 {
296 PAD* pad = static_cast<PAD*>( item );
297
298 if( pad->GetProperty() == PAD_PROP::CASTELLATED
299 || pad->GetAttribute() == PAD_ATTRIB::CONN )
300 {
301 return true; // Continue with other items
302 }
303 }
304
305 const std::shared_ptr<SHAPE>& itemShape = item->GetEffectiveShape();
306
307 for( PCB_LAYER_ID testLayer : { Edge_Cuts, Margin } )
308 {
309 if( testCopper && item->IsOnCopperLayer() )
310 {
311 edgesTree.QueryColliding( item, UNDEFINED_LAYER, testLayer, nullptr,
312 [&]( BOARD_ITEM* edge ) -> bool
313 {
314 return testAgainstEdge( item, itemShape.get(), edge,
315 EDGE_CLEARANCE_CONSTRAINT,
316 DRCE_EDGE_CLEARANCE );
317 },
319 }
320
321 if( testSilk && ( item->IsOnLayer( F_SilkS ) || item->IsOnLayer( B_SilkS ) ) )
322 {
323 if( edgesTree.QueryColliding( item, UNDEFINED_LAYER, testLayer, nullptr,
324 [&]( BOARD_ITEM* edge ) -> bool
325 {
326 return testAgainstEdge( item, itemShape.get(), edge,
327 SILK_CLEARANCE_CONSTRAINT,
328 DRCE_SILK_EDGE_CLEARANCE );
329 },
331 {
332 // violations reported during QueryColliding
333 }
334 else
335 {
336 // TODO: check postion being outside board boundary
337 }
338 }
339 }
340
341 return true;
342 } );
343
344 reportRuleStatistics();
345
346 return !m_drcEngine->IsCancelled();
347}
348
349
350namespace detail
351{
353}
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:77
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition: board_item.h:269
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:227
virtual bool IsOnCopperLayer() const
Definition: board_item.h:142
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
Definition: board_item.cpp:237
FOOTPRINTS & Footprints()
Definition: board.h:313
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:675
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
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:214
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)
DRC_ENGINE * m_drcEngine
bool isInvisibleText(const BOARD_ITEM *aItem) const
void reportAux(const wxString &aMsg)
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, int aConstraint, int aActual)
const KIID m_Uuid
Definition: eda_item.h:482
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
int GetStartY() const
Definition: eda_shape.h:125
int GetEndX() const
Definition: eda_shape.h:151
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:256
bool IsFilled() const
Definition: eda_shape.h:91
SHAPE_T GetShape() const
Definition: eda_shape.h:117
int GetEndY() const
Definition: eda_shape.h:150
int GetStartX() const
Definition: eda_shape.h:126
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:552
static LSET AllLayersMask()
Definition: lset.cpp:808
T Min() const
Definition: minoptmax.h:33
Definition: pad.h:58
STROKE_PARAMS GetStroke() const override
Definition: pcb_shape.h:82
virtual EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
Definition: pcb_shape.cpp:505
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:81
void SetWidth(int aWidth)
Definition: stroke_params.h:92
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:60
@ Edge_Cuts
Definition: layer_ids.h:114
@ Margin
Definition: layer_ids.h:115
@ F_SilkS
Definition: layer_ids.h:105
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
@ B_SilkS
Definition: layer_ids.h:104
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:95
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:93