KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drc_test_provider_silk_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 <unordered_map>
25
26#include <common.h>
27#include <board.h>
28#include <pcb_board_outline.h>
29#include <pcb_track.h>
30#include <zone.h>
32#include <geometry/seg.h>
33#include <drc/drc_engine.h>
34#include <drc/drc_item.h>
35#include <drc/drc_rule.h>
37#include <drc/drc_rtree.h>
39
40/*
41 Silk to silk clearance test. Check all silkscreen features against each other.
42 Errors generated:
43 - DRCE_SILK_CLEARANCE
44
45*/
46
48{
49public:
54
56
57 virtual bool Run() override;
58
59 virtual const wxString GetName() const override { return wxT( "silk_clearance" ); };
60
61private:
62
65};
66
67
69{
70 const int progressDelta = 500;
71
72 m_board = m_drcEngine->GetBoard();
73
74 // If the soldermask min width is greater than 0 then we must use a healing algorithm to generate
75 // a whole-board soldermask poly, and then test against that. However, that can't deal well with
76 // DRC exclusions (as any change anywhere on the board that affects the soldermask will null the
77 // associated exclusions), so we only use that when soldermask min width is > 0.
78 bool checkIndividualMaskItems = m_board->GetDesignSettings().m_SolderMaskMinWidth <= 0;
79
80 if( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_CLEARANCE )
81 && m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_MASK_CLEARANCE) )
82 {
83 return true; // continue with other tests
84 }
85
86 DRC_CONSTRAINT worstClearanceConstraint;
88
89 if( m_drcEngine->QueryWorstConstraint( SILK_CLEARANCE_CONSTRAINT, worstClearanceConstraint ) )
90 m_largestClearance = worstClearanceConstraint.m_Value.Min();
91
92 if( !reportPhase( _( "Checking silkscreen for overlapping items..." ) ) )
93 return false; // DRC cancelled
94
95 DRC_RTREE silkTree;
96 DRC_RTREE targetTree;
97 int ii = 0;
98 int items = 0;
99 LSET silkLayers = LSET( { F_SilkS, B_SilkS } );
100 LSET targetLayers = LSET::FrontMask() | LSET::BackMask() | LSET( { Edge_Cuts, Margin } );
101
102 auto countItems =
103 [&]( BOARD_ITEM* item ) -> bool
104 {
105 ++items;
106 return true;
107 };
108
109 // Rule areas are purely logical (no physical copper, mask, or silk) so they must never
110 // participate in silk-clearance collisions. Their effective shape now follows the
111 // outline (so disallow checks can collide against them), which would otherwise cause
112 // false silk-to-rule-area violations.
113 auto isRuleArea =
114 [&]( BOARD_ITEM* item ) -> bool
115 {
116 return item->Type() == PCB_ZONE_T && static_cast<ZONE*>( item )->GetIsRuleArea();
117 };
118
119 auto addToSilkTree =
120 [&]( BOARD_ITEM* item ) -> bool
121 {
122 if( !reportProgress( ii++, items, progressDelta ) )
123 return false;
124
125 if( isRuleArea( item ) )
126 return true;
127
128 for( PCB_LAYER_ID layer : { F_SilkS, B_SilkS } )
129 {
130 if( item->IsOnLayer( layer ) )
131 silkTree.Insert( item, layer, 0, ATOMIC_TABLES );
132 }
133
134 return true;
135 };
136
137 auto addToTargetTree =
138 [&]( BOARD_ITEM* item ) -> bool
139 {
140 if( !reportProgress( ii++, items, progressDelta ) )
141 return false;
142
143 if( isRuleArea( item ) )
144 return true;
145
146 for( PCB_LAYER_ID layer : LSET( item->GetLayerSet() & targetLayers ) )
147 targetTree.Insert( item, layer, 0, ATOMIC_TABLES );
148
149 return true;
150 };
151
152 forEachGeometryItem( s_allBasicItems, silkLayers, countItems );
153 forEachGeometryItem( s_allBasicItems, targetLayers, countItems );
154
155 forEachGeometryItem( s_allBasicItems, silkLayers, addToSilkTree );
156 forEachGeometryItem( s_allBasicItems, targetLayers, addToTargetTree );
157
158 silkTree.Build();
159 targetTree.Build();
160
161 REPORT_AUX( wxString::Format( wxT( "Testing %d silkscreen features against %d board items." ),
162 silkTree.size(),
163 targetTree.size() ) );
164
165 // Cache the board-outline bounding box and per-subshape collision results so that each
166 // subshape is only tested against the outline once during the visitor sweep. Without
167 // caching, QueryCollidingPairs invokes the visitor O(silk * target) times and the outline
168 // Collide (which walks the outline's triangulation) was dominating DRC runtime on boards
169 // with many silkscreen/mask polygons (see issue 24007).
170 PCB_BOARD_OUTLINE* boardOutline = m_board->BoardOutline();
171 BOX2I outlineBBox;
172
173 if( boardOutline && !boardOutline->HasOutline() )
174 boardOutline = nullptr;
175
176 if( boardOutline )
177 outlineBBox = boardOutline->GetOutline().BBoxFromCaches();
178
179 std::unordered_map<const SHAPE*, bool> outlineCollisionCache;
180
181 const std::vector<DRC_RTREE::LAYER_PAIR> layerPairs =
182 {
201 };
202
203 targetTree.QueryCollidingPairs( &silkTree, layerPairs,
204 [&]( const DRC_RTREE::LAYER_PAIR& aLayers, DRC_RTREE::ITEM_WITH_SHAPE* aRefItemShape,
205 DRC_RTREE::ITEM_WITH_SHAPE* aTestItemShape, bool* aCollisionDetected ) -> bool
206 {
207 BOARD_ITEM* refItem = aRefItemShape->parent;
208 const SHAPE* refShape = aRefItemShape->shape;
209 BOARD_ITEM* testItem = aTestItemShape->parent;
210 const SHAPE* testShape = aTestItemShape->shape;
211
212 std::shared_ptr<SHAPE> hole;
213
214 if( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_CLEARANCE )
215 && m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_MASK_CLEARANCE ) )
216 {
217 return false;
218 }
219
220 if( isInvisibleText( refItem ) || isInvisibleText( testItem ) )
221 return true;
222
223 if( testItem->IsTented( aLayers.first ) )
224 {
225 if( testItem->HasHole() )
226 {
227 hole = testItem->GetEffectiveHoleShape();
228 testShape = hole.get();
229 }
230 else
231 {
232 return true;
233 }
234 }
235
236 if( boardOutline )
237 {
238 if( !testItem->GetBoundingBox().Intersects( outlineBBox ) )
239 return true;
240
241 // Only cache for shapes owned by the R-tree (stable pointers). Hole
242 // shapes are freshly created per visitor call via shared_ptr, so their
243 // raw addresses cannot be safely used as cache keys.
244 bool collidesOutline;
245
246 if( testShape == aTestItemShape->shape )
247 {
248 auto [it, inserted] = outlineCollisionCache.try_emplace( testShape, false );
249
250 if( inserted )
251 it->second = testShape->Collide( &boardOutline->GetOutline() );
252
253 collidesOutline = it->second;
254 }
255 else
256 {
257 collidesOutline = testShape->Collide( &boardOutline->GetOutline() );
258 }
259
260 if( !collidesOutline )
261 return true;
262 }
263
264 int errorCode = DRCE_SILK_CLEARANCE;
266 refItem, testItem, aLayers.second );
267 int minClearance = -1;
268
269 if( !constraint.IsNull() && constraint.GetSeverity() != RPT_SEVERITY_IGNORE )
270 minClearance = constraint.GetValue().Min();
271
272 if( aLayers.second == F_Mask || aLayers.second == B_Mask )
273 {
274 if( checkIndividualMaskItems )
275 minClearance = std::max( minClearance, 0 );
276
277 errorCode = DRCE_SILK_MASK_CLEARANCE;
278 }
279
280 if( minClearance < 0 || m_drcEngine->IsErrorLimitExceeded( errorCode ) )
281 return true;
282
283 int actual;
284 VECTOR2I pos;
285
286 // Graphics are often compound shapes so ignore collisions between shapes in a
287 // single footprint or on the board (both parent footprints will be nullptr).
288 if( refItem->Type() == PCB_SHAPE_T && testItem->Type() == PCB_SHAPE_T
289 && refItem->GetParentFootprint() == testItem->GetParentFootprint() )
290 {
291 return true;
292 }
293
294 // Collide (and generate violations) based on a well-defined order so that
295 // exclusion checking against previously-generated violations will work.
296 if( aLayers.first == aLayers.second )
297 {
298 if( refItem->m_Uuid > testItem->m_Uuid )
299 {
300 std::swap( refItem, testItem );
301 std::swap( refShape, testShape );
302 }
303 }
304
305 if( refShape->Collide( testShape, minClearance, &actual, &pos ) )
306 {
307 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( errorCode );
308
309 if( minClearance > 0 )
310 {
311 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
312 constraint.GetName(),
313 minClearance,
314 actual ) );
315 }
316
317 drcItem->SetItems( refItem, testItem );
318 drcItem->SetViolatingRule( constraint.GetParentRule() );
319 reportTwoShapeGeometry( drcItem, pos, refShape, testShape, aLayers.second, actual );
320 *aCollisionDetected = true;
321 }
322
323 return true;
324 },
326 [&]( int aCount, int aSize ) -> bool
327 {
328 return reportProgress( aCount, aSize, progressDelta );
329 } );
330
331 return !m_drcEngine->IsCancelled();
332}
333
334
335namespace detail
336{
338}
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
FOOTPRINT * GetParentFootprint() const
virtual bool IsTented(PCB_LAYER_ID aLayer) const
Checks if the given object is tented (its copper shape is covered by solder mask) on a given side of ...
Definition board_item.h:197
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
virtual bool HasHole() const
Definition board_item.h:180
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:311
wxString GetName() const
Definition drc_rule.h:208
SEVERITY GetSeverity() const
Definition drc_rule.h:221
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:200
MINOPTMAX< int > m_Value
Definition drc_rule.h:244
DRC_RULE * GetParentRule() const
Definition drc_rule.h:204
bool IsNull() const
Definition drc_rule.h:195
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:417
Implement an R-tree for fast spatial and layer indexing of connectable items.
Definition drc_rtree.h:49
size_t size() const
Return the number of items in the tree.
Definition drc_rtree.h:554
std::pair< PCB_LAYER_ID, PCB_LAYER_ID > LAYER_PAIR
Definition drc_rtree.h:459
int QueryCollidingPairs(DRC_RTREE *aRefTree, std::vector< LAYER_PAIR > aLayerPairs, std::function< bool(const LAYER_PAIR &, ITEM_WITH_SHAPE *, ITEM_WITH_SHAPE *, bool *aCollision)> aVisitor, int aMaxClearance, std::function< bool(int, int)> aProgressReporter) const
Definition drc_rtree.h:474
void Build()
Finalize all pending inserts by bulk-building packed R-trees from the staged items.
Definition drc_rtree.h:168
void Insert(BOARD_ITEM *aItem, PCB_LAYER_ID aLayer, int aWorstClearance=0, bool aAtomicTables=false)
Insert an item into the tree on a particular layer with an optional worst clearance.
Definition drc_rtree.h:95
virtual const wxString GetName() const override
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
virtual ~DRC_TEST_PROVIDER_SILK_CLEARANCE()=default
virtual bool reportPhase(const wxString &aStageName)
void reportTwoShapeGeometry(std::shared_ptr< DRC_ITEM > &aDrcItem, const VECTOR2I &aMarkerPos, const SHAPE *aShape1, const SHAPE *aShape2, PCB_LAYER_ID aLayer, int aDistance)
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, const LSET &aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
static std::vector< KICAD_T > s_allBasicItems
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)
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:139
const KIID m_Uuid
Definition eda_item.h:535
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:112
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & FrontMask()
Return a mask holding all technical layers and the external CU layer on front side.
Definition lset.cpp:722
static const LSET & BackMask()
Return a mask holding all technical layers and the external CU layer on back side.
Definition lset.cpp:729
T Min() const
Definition minoptmax.h:33
bool HasOutline() const
const SHAPE_POLY_SET & GetOutline() const
const BOX2I BBoxFromCaches() const
An abstract shape on 2D plane.
Definition shape.h:128
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:183
Handle a list of polygons defining a copper zone.
Definition zone.h:74
The common library.
@ DRCE_SILK_MASK_CLEARANCE
Definition drc_item.h:97
@ DRCE_SILK_CLEARANCE
Definition drc_item.h:100
#define ATOMIC_TABLES
Definition drc_rtree.h:42
@ SILK_CLEARANCE_CONSTRAINT
Definition drc_rule.h:62
#define REPORT_AUX(s)
#define _(s)
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ F_CrtYd
Definition layer_ids.h:116
@ B_Adhes
Definition layer_ids.h:103
@ Edge_Cuts
Definition layer_ids.h:112
@ F_Paste
Definition layer_ids.h:104
@ F_Adhes
Definition layer_ids.h:102
@ B_Mask
Definition layer_ids.h:98
@ B_Cu
Definition layer_ids.h:65
@ F_Mask
Definition layer_ids.h:97
@ B_Paste
Definition layer_ids.h:105
@ F_Fab
Definition layer_ids.h:119
@ Margin
Definition layer_ids.h:113
@ F_SilkS
Definition layer_ids.h:100
@ B_CrtYd
Definition layer_ids.h:115
@ B_SilkS
Definition layer_ids.h:101
@ F_Cu
Definition layer_ids.h:64
@ B_Fab
Definition layer_ids.h:118
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
@ RPT_SEVERITY_IGNORE
int actual
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:85
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:105
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687