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 The 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 <pcb_track.h>
28#include <geometry/seg.h>
30#include <drc/drc_engine.h>
31#include <drc/drc_item.h>
32#include <drc/drc_rule.h>
34#include "drc_rtree.h"
35
36/*
37 Board edge clearance test. Checks all items for their mechanical clearances against the board
38 edge.
39 Errors generated:
40 - DRCE_EDGE_CLEARANCE
41 - DRCE_SILK_EDGE_CLEARANCE
42*/
43
45{
46public:
51
53
54 virtual bool Run() override;
55
56 virtual const wxString GetName() const override { return wxT( "edge_clearance" ); }
57
58private:
59 bool testAgainstEdge( BOARD_ITEM* item, SHAPE* itemShape, BOARD_ITEM* other,
60 DRC_CONSTRAINT_T aConstraintType, PCB_DRC_CODE aErrorCode );
61
62private:
63 std::vector<PAD*> m_castellatedPads;
65};
66
67
69 BOARD_ITEM* edge,
70 DRC_CONSTRAINT_T aConstraintType,
71 PCB_DRC_CODE aErrorCode )
72{
73 std::shared_ptr<SHAPE> shape;
74
75 if( edge->Type() == PCB_PAD_T )
76 shape = edge->GetEffectiveHoleShape();
77 else
78 shape = edge->GetEffectiveShape( Edge_Cuts );
79
80 auto constraint = m_drcEngine->EvalRules( aConstraintType, edge, item, UNDEFINED_LAYER );
81 int minClearance = constraint.GetValue().Min();
82 int actual;
83 VECTOR2I pos;
84
85 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && minClearance >= 0 )
86 {
87 if( itemShape->Collide( shape.get(), minClearance, &actual, &pos ) )
88 {
89 // Exact clearance is allowed
90 if( minClearance > 0 && actual == minClearance )
91 return true;
92
93 if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T )
94 {
95 // Edge collisions are allowed inside the holes of castellated pads
96 for( PAD* castellatedPad : m_castellatedPads )
97 {
98 if( castellatedPad->GetEffectiveHoleShape()->Collide( pos ) )
99 return true;
100 }
101 }
102
103 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( aErrorCode );
104
105 // Only report clearance info if there is any; otherwise it's just a straight collision
106 if( minClearance > 0 )
107 {
108 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
109 constraint.GetName(),
110 minClearance,
111 actual );
112
113 drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
114 }
115
116 drcItem->SetItems( edge->m_Uuid, item->m_Uuid );
117 drcItem->SetViolatingRule( constraint.GetParentRule() );
118 reportTwoItemGeometry( drcItem, pos, edge, item, Edge_Cuts, actual );
119
120 if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T )
121 return m_drcEngine->GetReportAllTrackErrors();
122 else
123 return false; // don't report violations with multiple edges; one is enough
124 }
125 }
126
127 return true;
128}
129
130
132{
133 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_EDGE_CLEARANCE ) )
134 {
135 if( !reportPhase( _( "Checking copper to board edge clearances..." ) ) )
136 return false; // DRC cancelled
137 }
138 else if( !m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_EDGE_CLEARANCE ) )
139 {
140 if( !reportPhase( _( "Checking silk to board edge clearances..." ) ) )
141 return false; // DRC cancelled
142 }
143 else
144 {
145 REPORT_AUX( wxT( "Edge clearance violations ignored. Tests not run." ) );
146 return true; // continue with other tests
147 }
148
149 m_board = m_drcEngine->GetBoard();
150 m_castellatedPads.clear();
151
152 DRC_CONSTRAINT worstClearanceConstraint;
153
154 if( m_drcEngine->QueryWorstConstraint( EDGE_CLEARANCE_CONSTRAINT, worstClearanceConstraint ) )
155 m_largestEdgeClearance = worstClearanceConstraint.GetValue().Min();
156
157 /*
158 * Build an RTree of the various edges (including NPTH holes) and margins found on the board.
159 */
160 std::vector<std::unique_ptr<PCB_SHAPE>> edges;
161 DRC_RTREE edgesTree;
162
164 [&]( BOARD_ITEM *item ) -> bool
165 {
166 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
167 STROKE_PARAMS stroke = shape->GetStroke();
168
169 if( item->IsOnLayer( Edge_Cuts ) )
170 stroke.SetWidth( 0 );
171
172 if( shape->GetShape() == SHAPE_T::RECTANGLE && !shape->IsSolidFill() )
173 {
174 // A single rectangle for the board would defeat the RTree, so convert to edges
175 if( shape->GetCornerRadius() > 0 )
176 {
177 for( SHAPE* seg : shape->MakeEffectiveShapes( true ) )
178 {
179 wxCHECK2( dynamic_cast<SHAPE_SEGMENT*>( seg ), continue );
180
181 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
182 edges.back()->SetShape( SHAPE_T::SEGMENT );
183 edges.back()->SetStart( seg->GetStart() );
184 edges.back()->SetEnd( seg->GetEnd() );
185 edges.back()->SetStroke( stroke );
186 }
187 }
188 else
189 {
190 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
191 edges.back()->SetShape( SHAPE_T::SEGMENT );
192 edges.back()->SetEndX( shape->GetStartX() );
193 edges.back()->SetStroke( stroke );
194 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
195 edges.back()->SetShape( SHAPE_T::SEGMENT );
196 edges.back()->SetEndY( shape->GetStartY() );
197 edges.back()->SetStroke( stroke );
198 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
199 edges.back()->SetShape( SHAPE_T::SEGMENT );
200 edges.back()->SetStartX( shape->GetEndX() );
201 edges.back()->SetStroke( stroke );
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 }
207 }
208 else if( shape->GetShape() == SHAPE_T::POLY && !shape->IsSolidFill() )
209 {
210 // A single polygon for the board would defeat the RTree, so convert to edges.
211 SHAPE_LINE_CHAIN poly = shape->GetPolyShape().Outline( 0 );
212
213 for( size_t ii = 0; ii < poly.GetSegmentCount(); ++ii )
214 {
215 SEG seg = poly.CSegment( ii );
216 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
217 edges.back()->SetShape( SHAPE_T::SEGMENT );
218 edges.back()->SetStart( seg.A );
219 edges.back()->SetEnd( seg.B );
220 edges.back()->SetStroke( stroke );
221 }
222 }
223 else
224 {
225 edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
226 edges.back()->SetStroke( stroke );
227 }
228
229 return true;
230 } );
231
232 for( const std::unique_ptr<PCB_SHAPE>& edge : edges )
233 {
234 for( PCB_LAYER_ID layer : { Edge_Cuts, Margin } )
235 {
236 if( edge->IsOnLayer( layer ) )
237 edgesTree.Insert( edge.get(), layer, m_largestEdgeClearance );
238 }
239 }
240
241 for( FOOTPRINT* footprint : m_board->Footprints() )
242 {
243 for( PAD* pad : footprint->Pads() )
244 {
245 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && pad->HasHole() )
246 {
247 // edge-clearances are for milling tolerances (drilling tolerances are handled
248 // by hole-clearances)
249 if( pad->GetDrillSizeX() != pad->GetDrillSizeY() )
251 }
252
253 if( pad->GetProperty() == PAD_PROP::CASTELLATED )
254 m_castellatedPads.push_back( pad );
255 }
256 }
257
258 /*
259 * Test copper and silk items against the set of edges.
260 */
261 const int progressDelta = 200;
262 int count = 0;
263 int ii = 0;
264
266 [&]( BOARD_ITEM *item ) -> bool
267 {
268 count++;
269 return true;
270 } );
271
273 [&]( BOARD_ITEM *item ) -> bool
274 {
275 bool testCopper = !m_drcEngine->IsErrorLimitExceeded( DRCE_EDGE_CLEARANCE );
276 bool testSilk = !m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_EDGE_CLEARANCE );
277
278 if( !testCopper && !testSilk )
279 return false; // All limits exceeded; we're done
280
281 if( !reportProgress( ii++, count, progressDelta ) )
282 return false; // DRC cancelled; we're done
283
284 if( isInvisibleText( item ) )
285 return true; // Continue with other items
286
287 if( item->Type() == PCB_PAD_T )
288 {
289 PAD* pad = static_cast<PAD*>( item );
290
291 if( pad->GetProperty() == PAD_PROP::CASTELLATED || pad->GetAttribute() == PAD_ATTRIB::CONN )
292 return true; // Continue with other items
293 }
294
295 std::vector<PCB_LAYER_ID> layersToTest;
296
297 switch( item->Type() )
298 {
299 case PCB_PAD_T:
300 layersToTest = static_cast<PAD*>( item )->Padstack().UniqueLayers();
301 break;
302
303 case PCB_VIA_T:
304 layersToTest = static_cast<PCB_VIA*>( item )->Padstack().UniqueLayers();
305 break;
306
307 default:
308 layersToTest = { UNDEFINED_LAYER };
309 }
310
311 for( PCB_LAYER_ID shapeLayer : layersToTest )
312 {
313 const std::shared_ptr<SHAPE>& itemShape = item->GetEffectiveShape( shapeLayer );
314
315 for( PCB_LAYER_ID testLayer : { Edge_Cuts, Margin } )
316 {
317 if( testCopper && item->IsOnCopperLayer() )
318 {
319 edgesTree.QueryColliding( item, shapeLayer, testLayer, nullptr,
320 [&]( BOARD_ITEM* edge ) -> bool
321 {
322 return testAgainstEdge( item, itemShape.get(), edge,
325 },
327 }
328
329 if( testSilk && ( item->IsOnLayer( F_SilkS ) || item->IsOnLayer( B_SilkS ) ) )
330 {
331 if( edgesTree.QueryColliding( item, shapeLayer, testLayer, nullptr,
332 [&]( BOARD_ITEM* edge ) -> bool
333 {
334 return testAgainstEdge( item, itemShape.get(), edge,
335 SILK_CLEARANCE_CONSTRAINT,
336 DRCE_SILK_EDGE_CLEARANCE );
337 },
339 {
340 // violations reported during QueryColliding
341 }
342 else
343 {
344 // TODO: check postion being outside board boundary
345 }
346 }
347 }
348 }
349
350 return true;
351 } );
352
353 return !m_drcEngine->IsCancelled();
354}
355
356
357namespace detail
358{
360}
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:314
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.
virtual bool IsOnCopperLayer() const
Definition board_item.h:151
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:166
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:381
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 bool Run() override
Run this provider against the given PCB with configured options (if any).
virtual ~DRC_TEST_PROVIDER_EDGE_CLEARANCE()=default
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, const LSET &aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
void reportTwoItemGeometry(std::shared_ptr< DRC_ITEM > &aDrcItem, const VECTOR2I &aMarkerPos, const BOARD_ITEM *aItem1, const BOARD_ITEM *aItem2, PCB_LAYER_ID aLayer, int aDistance)
bool isInvisibleText(const BOARD_ITEM *aItem) const
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, double aConstraint, double aActual, EDA_DATA_TYPE aDataType=EDA_DATA_TYPE::DISTANCE)
virtual bool reportProgress(size_t aCount, size_t aSize, size_t aDelta=1)
const KIID m_Uuid
Definition eda_item.h:516
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
int GetStartY() const
Definition eda_shape.h:174
int GetEndX() const
Definition eda_shape.h:217
virtual std::vector< SHAPE * > MakeEffectiveShapes(bool aEdgeOnly=false) const
Make a set of SHAPE objects representing the EDA_SHAPE.
Definition eda_shape.h:379
SHAPE_POLY_SET & GetPolyShape()
Definition eda_shape.h:337
SHAPE_T GetShape() const
Definition eda_shape.h:168
int GetEndY() const
Definition eda_shape.h:216
bool IsSolidFill() const
Definition eda_shape.h:117
int GetStartX() const
Definition eda_shape.h:175
int GetCornerRadius() const
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllLayersMask()
Definition lset.cpp:624
T Min() const
Definition minoptmax.h:33
Definition pad.h:54
STROKE_PARAMS GetStroke() const override
Definition pcb_shape.h:91
EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
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.
void SetWidth(int aWidth)
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 REPORT_AUX(s)
#define _(s)
@ SEGMENT
Definition eda_shape.h:45
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:46
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
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:87
@ CONN
Like smd, does not appear on the solder paste layer (default) Note: also has a special attribute in G...
Definition padstack.h:84
@ CASTELLATED
a pad with a castellated through hole
Definition padstack.h:105
@ RPT_SEVERITY_IGNORE
int actual
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:88
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:97
@ 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
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695