KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drc_engine.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-2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2014 Dick Hollenbeck, [email protected]
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include <atomic>
27#include <set>
28#include <wx/log.h>
29#include <reporter.h>
30#include <common.h>
31#include <progress_reporter.h>
32#include <string_utils.h>
36#include <drc/drc_engine.h>
37#include <drc/drc_rtree.h>
38#include <drc/drc_rule_parser.h>
39#include <drc/drc_rule.h>
42#include <drc/drc_item.h>
44#include <board.h>
45#include <pcb_marker.h>
46#include <footprint.h>
47#include <pad.h>
48#include <pcb_track.h>
49#include <pcb_shape.h>
50#include <core/profile.h>
51#include <thread_pool.h>
52#include <zone.h>
57
58
59// wxListBox's performance degrades horrifically with very large datasets. It's not clear
60// they're useful to the user anyway.
61#define ERROR_LIMIT 199
62#define EXTENDED_ERROR_LIMIT 499
63
64
72static const wxChar* traceDrcProfile = wxT( "KICAD_DRC_PROFILE" );
73
74
75void drcPrintDebugMessage( int level, const wxString& msg, const char *function, int line )
76{
77 wxString valueStr;
78
79 if( wxGetEnv( wxT( "DRC_DEBUG" ), &valueStr ) )
80 {
81 int setLevel = wxAtoi( valueStr );
82
83 if( level <= setLevel )
84 printf( "%-30s:%d | %s\n", function, line, (const char *) msg.c_str() );
85 }
86}
87
88
91 m_designSettings ( aSettings ),
92 m_board( aBoard ),
93 m_drawingSheet( nullptr ),
94 m_schematicNetlist( nullptr ),
95 m_rulesValid( false ),
97 m_testFootprints( false ),
98 m_logReporter( nullptr ),
99 m_progressReporter( nullptr )
100{
101 m_errorLimits.resize( DRCE_LAST + 1 );
102
103 for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
105}
106
107
109{
110 m_rules.clear();
111
112 for( std::pair<DRC_CONSTRAINT_T, std::vector<DRC_ENGINE_CONSTRAINT*>*> pair : m_constraintMap )
113 {
114 for( DRC_ENGINE_CONSTRAINT* constraint : *pair.second )
115 delete constraint;
116
117 delete pair.second;
118 }
119}
120
121
122static bool isKeepoutZone( const BOARD_ITEM* aItem, bool aCheckFlags )
123{
124 if( !aItem || aItem->Type() != PCB_ZONE_T )
125 return false;
126
127 const ZONE* zone = static_cast<const ZONE*>( aItem );
128
129 if( !zone->GetIsRuleArea() )
130 return false;
131
132 if( !zone->HasKeepoutParametersSet() )
133 return false;
134
135 if( aCheckFlags )
136 {
137 if( !zone->GetDoNotAllowTracks()
138 && !zone->GetDoNotAllowVias()
139 && !zone->GetDoNotAllowPads()
140 && !zone->GetDoNotAllowZoneFills()
141 && !zone->GetDoNotAllowFootprints() )
142 {
143 return false;
144 }
145 }
146
147 return true;
148}
149
150
151std::shared_ptr<DRC_RULE> DRC_ENGINE::createImplicitRule( const wxString& name,
152 const DRC_IMPLICIT_SOURCE aImplicitSource )
153{
154 std::shared_ptr<DRC_RULE> rule = std::make_shared<DRC_RULE>();
155
156 rule->m_Name = name;
157 rule->SetImplicitSource( aImplicitSource );
158
159 addRule( rule );
160
161 return rule;
162}
163
164
166{
167 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
168 wxString expr, expr2, ncName;
169
170 // 1) global defaults
171
172 std::shared_ptr<DRC_RULE> rule =
174
175 DRC_CONSTRAINT widthConstraint( TRACK_WIDTH_CONSTRAINT );
176 widthConstraint.Value().SetMin( bds.m_TrackMinWidth );
177 rule->AddConstraint( widthConstraint );
178
179 DRC_CONSTRAINT connectionConstraint( CONNECTION_WIDTH_CONSTRAINT );
180 connectionConstraint.Value().SetMin( bds.m_MinConn );
181 rule->AddConstraint( connectionConstraint );
182
183 DRC_CONSTRAINT drillConstraint( HOLE_SIZE_CONSTRAINT );
184 drillConstraint.Value().SetMin( bds.m_MinThroughDrill );
185 rule->AddConstraint( drillConstraint );
186
187 DRC_CONSTRAINT annulusConstraint( ANNULAR_WIDTH_CONSTRAINT );
188 annulusConstraint.Value().SetMin( bds.m_ViasMinAnnularWidth );
189 rule->AddConstraint( annulusConstraint );
190
191 DRC_CONSTRAINT diameterConstraint( VIA_DIAMETER_CONSTRAINT );
192 diameterConstraint.Value().SetMin( bds.m_ViasMinSize );
193 rule->AddConstraint( diameterConstraint );
194
195 DRC_CONSTRAINT holeToHoleConstraint( HOLE_TO_HOLE_CONSTRAINT );
196 holeToHoleConstraint.Value().SetMin( bds.m_HoleToHoleMin );
197 rule->AddConstraint( holeToHoleConstraint );
198
199 rule = createImplicitRule( _( "board setup constraints zone fill strategy" ),
201 DRC_CONSTRAINT thermalSpokeCountConstraint( MIN_RESOLVED_SPOKES_CONSTRAINT );
202 thermalSpokeCountConstraint.Value().SetMin( bds.m_MinResolvedSpokes );
203 rule->AddConstraint( thermalSpokeCountConstraint );
204
205 rule = createImplicitRule( _( "board setup constraints silk" ), DRC_IMPLICIT_SOURCE::BOARD_SETUP_CONSTRAINT );
206 rule->m_LayerCondition = LSET( { F_SilkS, B_SilkS } );
207 DRC_CONSTRAINT silkClearanceConstraint( SILK_CLEARANCE_CONSTRAINT );
208 silkClearanceConstraint.Value().SetMin( bds.m_SilkClearance );
209 rule->AddConstraint( silkClearanceConstraint );
210
211 rule = createImplicitRule( _( "board setup constraints silk text height" ),
213 rule->m_LayerCondition = LSET( { F_SilkS, B_SilkS } );
214 DRC_CONSTRAINT silkTextHeightConstraint( TEXT_HEIGHT_CONSTRAINT );
215 silkTextHeightConstraint.Value().SetMin( bds.m_MinSilkTextHeight );
216 rule->AddConstraint( silkTextHeightConstraint );
217
218 rule = createImplicitRule( _( "board setup constraints silk text thickness" ),
220 rule->m_LayerCondition = LSET( { F_SilkS, B_SilkS } );
221 DRC_CONSTRAINT silkTextThicknessConstraint( TEXT_THICKNESS_CONSTRAINT );
222 silkTextThicknessConstraint.Value().SetMin( bds.m_MinSilkTextThickness );
223 rule->AddConstraint( silkTextThicknessConstraint );
224
225 rule = createImplicitRule( _( "board setup constraints hole" ), DRC_IMPLICIT_SOURCE::BOARD_SETUP_CONSTRAINT );
226 DRC_CONSTRAINT holeClearanceConstraint( HOLE_CLEARANCE_CONSTRAINT );
227 holeClearanceConstraint.Value().SetMin( bds.m_HoleClearance );
228 rule->AddConstraint( holeClearanceConstraint );
229
230 rule = createImplicitRule( _( "board setup constraints edge" ), DRC_IMPLICIT_SOURCE::BOARD_SETUP_CONSTRAINT );
231 DRC_CONSTRAINT edgeClearanceConstraint( EDGE_CLEARANCE_CONSTRAINT );
232 edgeClearanceConstraint.Value().SetMin( bds.m_CopperEdgeClearance );
233 rule->AddConstraint( edgeClearanceConstraint );
234
235 rule = createImplicitRule( _( "board setup constraints courtyard" ), DRC_IMPLICIT_SOURCE::BOARD_SETUP_CONSTRAINT );
236 DRC_CONSTRAINT courtyardClearanceConstraint( COURTYARD_CLEARANCE_CONSTRAINT );
237 holeToHoleConstraint.Value().SetMin( 0 );
238 rule->AddConstraint( courtyardClearanceConstraint );
239
240 // 2a) micro-via specific defaults (new DRC doesn't treat microvias in any special way)
241
242 std::shared_ptr<DRC_RULE> uViaRule =
243 createImplicitRule( _( "board setup constraints micro-via" ), DRC_IMPLICIT_SOURCE::BOARD_SETUP_CONSTRAINT );
244
245 uViaRule->m_Condition = new DRC_RULE_CONDITION( wxT( "A.Via_Type == 'Micro'" ) );
246
247 DRC_CONSTRAINT uViaDrillConstraint( HOLE_SIZE_CONSTRAINT );
248 uViaDrillConstraint.Value().SetMin( bds.m_MicroViasMinDrill );
249 uViaRule->AddConstraint( uViaDrillConstraint );
250
251 DRC_CONSTRAINT uViaDiameterConstraint( VIA_DIAMETER_CONSTRAINT );
252 uViaDiameterConstraint.Value().SetMin( bds.m_MicroViasMinSize );
253 uViaRule->AddConstraint( uViaDiameterConstraint );
254
255 // 2b) barcode-specific defaults
256
257 std::shared_ptr<DRC_RULE> barcodeRule =
258 createImplicitRule( _( "barcode visual separation default" ), DRC_IMPLICIT_SOURCE::BARCODE_DEFAULTS );
259 DRC_CONSTRAINT barcodeSeparationConstraint( PHYSICAL_CLEARANCE_CONSTRAINT );
260 barcodeSeparationConstraint.Value().SetMin( GetIuScale().mmToIU( 1.0 ) );
261 barcodeRule->AddConstraint( barcodeSeparationConstraint );
262 barcodeRule->m_Condition = new DRC_RULE_CONDITION( wxT( "A.Type == 'Barcode'" ) );
263
264 // 3) per-netclass rules
265
266 std::vector<std::shared_ptr<DRC_RULE>> netclassClearanceRules;
267 std::vector<std::shared_ptr<DRC_RULE>> netclassItemSpecificRules;
268
269 auto makeNetclassRules =
270 [&]( const std::shared_ptr<NETCLASS>& nc, bool isDefault )
271 {
272 ncName = nc->GetName();
273 ncName.Replace( "'", "\\'" );
274
275 if( nc->HasClearance() )
276 {
277 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
278 netclassRule->m_Name = wxString::Format( _( "netclass '%s'" ),
279 nc->GetClearanceParent()->GetHumanReadableName() );
280 netclassRule->SetImplicitSource( DRC_IMPLICIT_SOURCE::NET_CLASS );
281
282 expr = wxString::Format( wxT( "A.hasExactNetclass('%s')" ), ncName );
283 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
284 netclassClearanceRules.push_back( netclassRule );
285
287 constraint.Value().SetMin( nc->GetClearance() );
288 netclassRule->AddConstraint( constraint );
289
290 {
291 std::unique_lock<std::shared_mutex> writeLock( m_clearanceCacheMutex );
292 m_netclassClearances[nc->GetName()] = nc->GetClearance();
293 }
294 }
295
296 if( nc->HasTrackWidth() )
297 {
298 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
299 netclassRule->m_Name = wxString::Format( _( "netclass '%s'" ),
300 nc->GetTrackWidthParent()->GetHumanReadableName() );
301 netclassRule->SetImplicitSource( DRC_IMPLICIT_SOURCE::NET_CLASS );
302
303 expr = wxString::Format( wxT( "A.hasExactNetclass('%s')" ), ncName );
304 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
305 netclassClearanceRules.push_back( netclassRule );
306
308 constraint.Value().SetMin( bds.m_TrackMinWidth );
309 constraint.Value().SetOpt( nc->GetTrackWidth() );
310 netclassRule->AddConstraint( constraint );
311 }
312
313 if( nc->HasDiffPairWidth() )
314 {
315 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
316 netclassRule->m_Name = wxString::Format( _( "netclass '%s' (diff pair)" ),
317 nc->GetDiffPairWidthParent()->GetHumanReadableName() );
318 netclassRule->SetImplicitSource( DRC_IMPLICIT_SOURCE::NET_CLASS );
319
320 expr = wxString::Format( wxT( "A.hasExactNetclass('%s') && A.inDiffPair('*')" ), ncName );
321 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
322 netclassItemSpecificRules.push_back( netclassRule );
323
325 constraint.Value().SetMin( bds.m_TrackMinWidth );
326 constraint.Value().SetOpt( nc->GetDiffPairWidth() );
327 netclassRule->AddConstraint( constraint );
328 }
329
330 if( nc->HasDiffPairGap() )
331 {
332 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
333 netclassRule->m_Name = wxString::Format( _( "netclass '%s' (diff pair)" ),
334 nc->GetDiffPairGapParent()->GetHumanReadableName() );
335 netclassRule->SetImplicitSource( DRC_IMPLICIT_SOURCE::NET_CLASS );
336
337 expr = wxString::Format( wxT( "A.hasExactNetclass('%s')" ), ncName );
338 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
339 netclassItemSpecificRules.push_back( netclassRule );
340
342 constraint.Value().SetMin( bds.m_MinClearance );
343 constraint.Value().SetOpt( nc->GetDiffPairGap() );
344 netclassRule->AddConstraint( constraint );
345
346 // A narrower diffpair gap overrides the netclass min clearance
347 if( nc->GetDiffPairGap() < nc->GetClearance() )
348 {
349 netclassRule = std::make_shared<DRC_RULE>();
350 netclassRule->m_Name = wxString::Format( _( "netclass '%s' (diff pair)" ),
351 nc->GetDiffPairGapParent()->GetHumanReadableName() );
352 netclassRule->SetImplicitSource( DRC_IMPLICIT_SOURCE::NET_CLASS );
353
354 expr = wxString::Format( wxT( "A.hasExactNetclass('%s') && AB.isCoupledDiffPair()" ), ncName );
355 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
356 netclassItemSpecificRules.push_back( netclassRule );
357
358 DRC_CONSTRAINT min_clearanceConstraint( CLEARANCE_CONSTRAINT );
359 min_clearanceConstraint.Value().SetMin( nc->GetDiffPairGap() );
360 netclassRule->AddConstraint( min_clearanceConstraint );
361
363 }
364 }
365
366 if( nc->HasViaDiameter() )
367 {
368 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
369 netclassRule->m_Name = wxString::Format( _( "netclass '%s'" ),
370 nc->GetViaDiameterParent()->GetHumanReadableName() );
371 netclassRule->SetImplicitSource( DRC_IMPLICIT_SOURCE::NET_CLASS );
372
373 expr = wxString::Format( wxT( "A.hasExactNetclass('%s') && A.Via_Type != 'Micro'" ), ncName );
374 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
375 netclassItemSpecificRules.push_back( netclassRule );
376
378 constraint.Value().SetMin( bds.m_ViasMinSize );
379 constraint.Value().SetOpt( nc->GetViaDiameter() );
380 netclassRule->AddConstraint( constraint );
381 }
382
383 if( nc->HasViaDrill() )
384 {
385 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
386 netclassRule->m_Name = wxString::Format( _( "netclass '%s'" ),
387 nc->GetViaDrillParent()->GetHumanReadableName() );
388 netclassRule->SetImplicitSource( DRC_IMPLICIT_SOURCE::NET_CLASS );
389
390 expr = wxString::Format( wxT( "A.hasExactNetclass('%s') && A.Via_Type != 'Micro'" ), ncName );
391 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
392 netclassItemSpecificRules.push_back( netclassRule );
393
395 constraint.Value().SetMin( bds.m_MinThroughDrill );
396 constraint.Value().SetOpt( nc->GetViaDrill() );
397 netclassRule->AddConstraint( constraint );
398 }
399
400 if( nc->HasuViaDiameter() )
401 {
402 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
403 netclassRule->m_Name = wxString::Format( _( "netclass '%s' (uvia)" ),
404 nc->GetuViaDiameterParent()->GetHumanReadableName() );
405 netclassRule->SetImplicitSource( DRC_IMPLICIT_SOURCE::NET_CLASS );
406
407 expr = wxString::Format( wxT( "A.hasExactNetclass('%s') && A.Via_Type == 'Micro'" ), ncName );
408 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
409 netclassItemSpecificRules.push_back( netclassRule );
410
412 constraint.Value().SetMin( bds.m_MicroViasMinSize );
413 constraint.Value().SetMin( nc->GetuViaDiameter() );
414 netclassRule->AddConstraint( constraint );
415 }
416
417 if( nc->HasuViaDrill() )
418 {
419 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
420 netclassRule->m_Name = wxString::Format( _( "netclass '%s' (uvia)" ),
421 nc->GetuViaDrillParent()->GetHumanReadableName() );
422 netclassRule->SetImplicitSource( DRC_IMPLICIT_SOURCE::NET_CLASS );
423
424 expr = wxString::Format( wxT( "A.hasExactNetclass('%s') && A.Via_Type == 'Micro'" ), ncName );
425 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
426 netclassItemSpecificRules.push_back( netclassRule );
427
429 constraint.Value().SetMin( bds.m_MicroViasMinDrill );
430 constraint.Value().SetOpt( nc->GetuViaDrill() );
431 netclassRule->AddConstraint( constraint );
432 }
433 };
434
435 m_board->SynchronizeNetsAndNetClasses( false );
436 makeNetclassRules( bds.m_NetSettings->GetDefaultNetclass(), true );
437
438 for( const auto& [name, netclass] : bds.m_NetSettings->GetNetclasses() )
439 makeNetclassRules( netclass, false );
440
441 for( const auto& [name, netclass] : bds.m_NetSettings->GetCompositeNetclasses() )
442 makeNetclassRules( netclass, false );
443
444 // The netclass clearance rules have to be sorted by min clearance so the right one fires
445 // if 'A' and 'B' belong to two different netclasses.
446 //
447 // The item-specific netclass rules are all unary, so there's no 'A' vs 'B' issue.
448
449 std::sort( netclassClearanceRules.begin(), netclassClearanceRules.end(),
450 []( const std::shared_ptr<DRC_RULE>& lhs, const std::shared_ptr<DRC_RULE>& rhs )
451 {
452 return lhs->m_Constraints[0].m_Value.Min()
453 < rhs->m_Constraints[0].m_Value.Min();
454 } );
455
456 for( std::shared_ptr<DRC_RULE>& ncRule : netclassClearanceRules )
457 addRule( ncRule );
458
459 for( std::shared_ptr<DRC_RULE>& ncRule : netclassItemSpecificRules )
460 addRule( ncRule );
461
462 // 4) tuning profile rules
463 auto addTuningSingleRule =
464 [&]( const DELAY_PROFILE_TRACK_PROPAGATION_ENTRY& aLayerEntry, const wxString& aProfileName,
465 const wxString& aNetclassName )
466 {
467 if( aLayerEntry.GetWidth() <= 0 )
468 return;
469
470 std::shared_ptr<DRC_RULE> tuningRule = std::make_shared<DRC_RULE>();
471 tuningRule->m_Severity = bds.m_DRCSeverities[DRCE_TUNING_PROFILE_IMPLICIT_RULES];
472 tuningRule->m_Name = wxString::Format( _( "tuning profile '%s'" ), aProfileName );
473 tuningRule->SetImplicitSource( DRC_IMPLICIT_SOURCE::TUNING_PROFILE );
474
475 expr = wxString::Format( wxT( "A.hasExactNetclass('%s') && A.Layer == '%s'" ),
476 aNetclassName,
477 LSET::Name( aLayerEntry.GetSignalLayer() ) );
478 tuningRule->m_Condition = new DRC_RULE_CONDITION( expr );
479
481 constraint.Value().SetMin( std::max( bds.m_TrackMinWidth, aLayerEntry.GetWidth() ) );
482 constraint.Value().SetOpt( aLayerEntry.GetWidth() );
483 constraint.Value().SetMax( aLayerEntry.GetWidth() );
484 tuningRule->AddConstraint( constraint );
485
486 addRule( tuningRule );
487 };
488
489 auto addTuningDifferentialRules =
490 [&]( const DELAY_PROFILE_TRACK_PROPAGATION_ENTRY& aLayerEntry, const wxString& aProfileName,
491 const NETCLASS* aNetclass )
492 {
493 if( aLayerEntry.GetWidth() <= 0 || aLayerEntry.GetDiffPairGap() <= 0 )
494 return;
495
496 std::shared_ptr<DRC_RULE> tuningRule = std::make_shared<DRC_RULE>();
497 tuningRule->m_Severity = bds.m_DRCSeverities[DRCE_TUNING_PROFILE_IMPLICIT_RULES];
498 tuningRule->m_Name = wxString::Format( _( "tuning profile '%s'" ), aProfileName );
499 tuningRule->SetImplicitSource( DRC_IMPLICIT_SOURCE::TUNING_PROFILE );
500
501 expr = wxString::Format( wxT( "A.hasExactNetclass('%s') && A.Layer == '%s' && A.inDiffPair('*')" ),
502 aNetclass->GetName(),
503 LSET::Name( aLayerEntry.GetSignalLayer() ) );
504 tuningRule->m_Condition = new DRC_RULE_CONDITION( expr );
505
507 constraint.Value().SetMin( std::max( bds.m_TrackMinWidth, aLayerEntry.GetWidth() ) );
508 constraint.Value().SetOpt( aLayerEntry.GetWidth() );
509 constraint.Value().SetMax( aLayerEntry.GetWidth() );
510 tuningRule->AddConstraint( constraint );
511
512 addRule( tuningRule );
513
514 std::shared_ptr<DRC_RULE> tuningRule2 = std::make_shared<DRC_RULE>();
515 tuningRule2->m_Severity = bds.m_DRCSeverities[DRCE_TUNING_PROFILE_IMPLICIT_RULES];
516 tuningRule2->m_Name = wxString::Format( _( "tuning profile '%s'" ), aProfileName );
517 tuningRule2->SetImplicitSource( DRC_IMPLICIT_SOURCE::TUNING_PROFILE );
518
519 expr2 = wxString::Format( wxT( "A.hasExactNetclass('%s') && A.Layer == '%s' && A.inDiffPair('*')" ),
520 aNetclass->GetName(),
521 LSET::Name( aLayerEntry.GetSignalLayer() ) );
522 tuningRule2->m_Condition = new DRC_RULE_CONDITION( expr2 );
523
525 constraint2.Value().SetMin( std::max( bds.m_MinClearance, aLayerEntry.GetDiffPairGap() ) );
526 constraint2.Value().SetOpt( aLayerEntry.GetDiffPairGap() );
527 constraint2.Value().SetMax( aLayerEntry.GetDiffPairGap() );
528 tuningRule2->AddConstraint( constraint2 );
529
530 addRule( tuningRule2 );
531
532 // A narrower diffpair gap overrides the netclass min clearance
533 if( aLayerEntry.GetDiffPairGap() < aNetclass->GetClearance() )
534 {
535 std::shared_ptr<DRC_RULE> diffPairClearanceRule = std::make_shared<DRC_RULE>();
536 diffPairClearanceRule->m_Severity = bds.m_DRCSeverities[DRCE_TUNING_PROFILE_IMPLICIT_RULES];
537 diffPairClearanceRule->m_Name = wxString::Format( _( "tuning profile '%s'" ), aProfileName );
538 diffPairClearanceRule->SetImplicitSource( DRC_IMPLICIT_SOURCE::TUNING_PROFILE );
539
540 expr = wxString::Format(
541 wxT( "A.hasExactNetclass('%s') && A.Layer == '%s' && AB.isCoupledDiffPair()" ),
542 aNetclass->GetName(), LSET::Name( aLayerEntry.GetSignalLayer() ) );
543 diffPairClearanceRule->m_Condition = new DRC_RULE_CONDITION( expr );
544
545 DRC_CONSTRAINT min_clearanceConstraint( CLEARANCE_CONSTRAINT );
546 min_clearanceConstraint.Value().SetMin( aLayerEntry.GetDiffPairGap() );
547 diffPairClearanceRule->AddConstraint( min_clearanceConstraint );
548
549 addRule( diffPairClearanceRule );
550 }
551 };
552
553 if( PROJECT* project = m_board->GetProject() )
554 {
555 std::shared_ptr<TUNING_PROFILES> tuningParams = project->GetProjectFile().TuningProfileParameters();
556
557 auto addNetclassTuningProfileRules =
558 [&tuningParams, &addTuningSingleRule, &addTuningDifferentialRules]( NETCLASS* aNetclass )
559 {
560 if( aNetclass->HasTuningProfile() )
561 {
562 const wxString delayProfileName = aNetclass->GetTuningProfile();
563 const TUNING_PROFILE& profile = tuningParams->GetTuningProfile( delayProfileName );
564
566 {
567 if( entry.GetWidth() <= 0 )
568 continue;
569
571 addTuningSingleRule( entry, delayProfileName, aNetclass->GetName() );
572 else
573 addTuningDifferentialRules( entry, delayProfileName, aNetclass );
574 }
575 }
576 };
577
578 addNetclassTuningProfileRules( bds.m_NetSettings->GetDefaultNetclass().get() );
579
580 for( const auto& [netclassName, netclass] : bds.m_NetSettings->GetNetclasses() )
581 addNetclassTuningProfileRules( netclass.get() );
582
583 for( const auto& [netclassName, netclass] : bds.m_NetSettings->GetCompositeNetclasses() )
584 addNetclassTuningProfileRules( netclass.get() );
585 }
586
587 // 5) keepout area rules
588 auto addKeepoutZoneRule =
589 [&]( ZONE* zone, FOOTPRINT* parentFP )
590 {
591 const wxString& name = zone->GetZoneName();
592
593 if( name.IsEmpty() )
594 {
595 if( parentFP )
596 {
597 rule = createImplicitRule(
598 wxString::Format( _( "keepout area of %s" ), DescribeRef( parentFP->GetReference() ) ),
600 }
601 else
602 {
603 rule = createImplicitRule( _( "keepout area" ), DRC_IMPLICIT_SOURCE::KEEPOUT );
604 }
605
606 }
607 else
608 {
609 if( parentFP )
610 {
611 rule = createImplicitRule( wxString::Format( _( "keepout area '%s' of %s" ), name,
612 DescribeRef( parentFP->GetReference() ) ),
614 }
615 else
616 {
617 rule = createImplicitRule( wxString::Format( _( "keepout area '%s'" ), name ),
619 }
620 }
621
622 rule->m_ImplicitItemId = zone->m_Uuid;
623 rule->m_ImplicitItem = zone;
624
625 rule->m_Condition = new DRC_RULE_CONDITION( wxString::Format( wxT( "A.intersectsArea('%s')" ),
626 zone->m_Uuid.AsString() ) );
627
628 rule->m_LayerCondition = zone->GetLayerSet();
629
630 int disallowFlags = 0;
631
632 if( zone->GetDoNotAllowTracks() )
633 disallowFlags |= DRC_DISALLOW_TRACKS;
634
635 if( zone->GetDoNotAllowVias() )
636 disallowFlags |= DRC_DISALLOW_VIAS;
637
638 if( zone->GetDoNotAllowPads() )
639 disallowFlags |= DRC_DISALLOW_PADS;
640
641 if( zone->GetDoNotAllowZoneFills() )
642 disallowFlags |= DRC_DISALLOW_ZONES;
643
644 if( zone->GetDoNotAllowFootprints() )
645 disallowFlags |= DRC_DISALLOW_FOOTPRINTS;
646
647 DRC_CONSTRAINT disallowConstraint( DISALLOW_CONSTRAINT );
648 disallowConstraint.m_DisallowFlags = disallowFlags;
649 rule->AddConstraint( disallowConstraint );
650 };
651
652 for( ZONE* zone : m_board->Zones() )
653 {
654 if( isKeepoutZone( zone, true ) )
655 addKeepoutZoneRule( zone, nullptr );
656 }
657
658 for( FOOTPRINT* footprint : m_board->Footprints() )
659 {
660 for( ZONE* zone : footprint->Zones() )
661 {
662 if( isKeepoutZone( zone, true ) )
663 addKeepoutZoneRule( zone, footprint );
664 }
665 }
666}
667
668
669void DRC_ENGINE::loadRules( const wxFileName& aPath )
670{
671 if( m_board && aPath.FileExists() )
672 {
673 std::vector<std::shared_ptr<DRC_RULE>> rules;
674
675 if( FILE* fp = wxFopen( aPath.GetFullPath(), wxT( "rt" ) ) )
676 {
677 FILE_LINE_READER lineReader( fp, aPath.GetFullPath() ); // Will close rules file
678 wxString rulesText;
679
680 std::function<bool( wxString* )> resolver =
681 [&]( wxString* token ) -> bool
682 {
683 return m_board->ResolveTextVar( token, 0 );
684 };
685
686 while( char* line = lineReader.ReadLine() )
687 {
688 wxString str( line );
689 str = m_board->ConvertCrossReferencesToKIIDs( str );
690 str = ExpandTextVars( str, &resolver );
691
692 rulesText << str << '\n';
693 }
694
695 DRC_RULES_PARSER parser( rulesText, aPath.GetFullPath() );
696 parser.Parse( rules, m_logReporter );
697 }
698
699 // Copy the rules into the member variable afterwards so that if Parse() throws then
700 // the possibly malformed rules won't contaminate the current ruleset.
701
702 for( std::shared_ptr<DRC_RULE>& rule : rules )
703 m_rules.push_back( rule );
704 }
705}
706
707
709{
710 if( m_logReporter )
711 m_logReporter->Report( wxT( "Compiling Rules" ) );
712
713 REPORTER error_semaphore;
714
715 for( std::shared_ptr<DRC_RULE>& rule : m_rules )
716 {
717 DRC_RULE_CONDITION* condition = nullptr;
718
719 if( rule->m_Condition && !rule->m_Condition->GetExpression().IsEmpty() )
720 {
721 condition = rule->m_Condition;
722 condition->Compile( &error_semaphore );
723 }
724
725 if( error_semaphore.HasMessageOfSeverity( RPT_SEVERITY_ERROR ) )
726 THROW_PARSE_ERROR( wxT( "Parse error" ), rule->m_Name,
727 TO_UTF8( rule->m_Condition->GetExpression() ), 0, 0 );
728
729 for( const DRC_CONSTRAINT& constraint : rule->m_Constraints )
730 {
731 auto& ruleVec = m_constraintMap[ constraint.m_Type ];
732
733 if( !ruleVec )
734 ruleVec = new std::vector<DRC_ENGINE_CONSTRAINT*>();
735
736 DRC_ENGINE_CONSTRAINT* engineConstraint = new DRC_ENGINE_CONSTRAINT;
737
738 engineConstraint->layerTest = rule->m_LayerCondition;
739 engineConstraint->condition = condition;
740 engineConstraint->constraint = constraint;
741 engineConstraint->parentRule = rule;
742
743 if( rule->IsImplicit() && constraint.m_Type == DISALLOW_CONSTRAINT
744 && m_board && rule->m_ImplicitItemId != niluuid )
745 {
746 const auto& cache = m_board->GetItemByIdCache();
747 auto it = cache.find( rule->m_ImplicitItemId );
748
749 if( it != cache.end() && it->second->Type() == PCB_ZONE_T )
750 engineConstraint->implicitKeepoutZone = static_cast<ZONE*>( it->second );
751 }
752
753 ruleVec->push_back( engineConstraint );
754 }
755 }
756
759 m_explicitConstraints.clear();
760
761 for( auto& [constraintType, ruleList] : m_constraintMap )
762 {
763 for( DRC_ENGINE_CONSTRAINT* c : *ruleList )
764 {
765 if( c->parentRule && !c->parentRule->IsImplicit() )
766 {
767 m_explicitConstraints[constraintType].push_back( c );
768
769 if( constraintType == CLEARANCE_CONSTRAINT )
771
773 && c->condition
774 && c->condition->HasGeometryDependentFunctions() )
775 {
777 }
778 }
779 }
780 }
781}
782
783
784void DRC_ENGINE::InitEngine( const std::shared_ptr<DRC_RULE>& rule )
785{
787
788 for( DRC_TEST_PROVIDER* provider : m_testProviders )
789 {
790 if( m_logReporter )
791 m_logReporter->Report( wxString::Format( wxT( "Create DRC provider: '%s'" ), provider->GetName() ) );
792
793 provider->SetDRCEngine( this );
794 }
795
796 // Existing markers may hold raw pointers to DRC_RULEs we're about to destroy.
797 // Null them out so GetSeverity() falls back to the board design settings.
798 if( m_board )
799 {
800 for( PCB_MARKER* marker : m_board->Markers() )
801 {
802 DRC_ITEM* drcItem = static_cast<DRC_ITEM*>( marker->GetRCItem().get() );
803 drcItem->SetViolatingRule( nullptr );
804 }
805 }
806
807 m_rules.clear();
808 m_rulesValid = false;
809
810 for( std::pair<DRC_CONSTRAINT_T, std::vector<DRC_ENGINE_CONSTRAINT*>*> pair : m_constraintMap )
811 {
812 for( DRC_ENGINE_CONSTRAINT* constraint : *pair.second )
813 delete constraint;
814
815 delete pair.second;
816 }
817
818 m_constraintMap.clear();
819
820 m_board->IncrementTimeStamp(); // Clear board-level caches
821
822 try
823 {
824 m_rules.push_back( rule );
825 compileRules();
826 }
827 catch( PARSE_ERROR& original_parse_error )
828 {
829 throw original_parse_error;
830 }
831
832 for( int ii = DRCE_FIRST; ii < DRCE_LAST; ++ii )
834
835 m_rulesValid = true;
836}
837
838
839void DRC_ENGINE::InitEngine( const wxFileName& aRulePath )
840{
842
843 for( DRC_TEST_PROVIDER* provider : m_testProviders )
844 {
845 if( m_logReporter )
846 m_logReporter->Report( wxString::Format( wxT( "Create DRC provider: '%s'" ), provider->GetName() ) );
847
848 provider->SetDRCEngine( this );
849 }
850
851 // Existing markers may hold raw pointers to DRC_RULEs we're about to destroy.
852 // Null them out so GetSeverity() falls back to the board design settings.
853 if( m_board )
854 {
855 for( PCB_MARKER* marker : m_board->Markers() )
856 {
857 DRC_ITEM* drcItem = static_cast<DRC_ITEM*>( marker->GetRCItem().get() );
858 drcItem->SetViolatingRule( nullptr );
859 }
860 }
861
862 m_rules.clear();
863 m_rulesValid = false;
864
865 for( std::pair<DRC_CONSTRAINT_T, std::vector<DRC_ENGINE_CONSTRAINT*>*> pair : m_constraintMap )
866 {
867 for( DRC_ENGINE_CONSTRAINT* constraint : *pair.second )
868 delete constraint;
869
870 delete pair.second;
871 }
872
873 m_constraintMap.clear();
874
875 {
876 std::unique_lock<std::shared_mutex> writeLock( m_clearanceCacheMutex );
877 m_ownClearanceCache.clear();
878 m_netclassClearances.clear();
879 }
880
883 m_explicitConstraints.clear();
884
885 m_board->IncrementTimeStamp(); // Clear board-level caches
886
887 try // attempt to load full set of rules (implicit + user rules)
888 {
890 loadRules( aRulePath );
891 compileRules();
892 }
893 catch( PARSE_ERROR& original_parse_error )
894 {
895 m_rules.clear();
896
897 try // try again with just our implicit rules
898 {
900 compileRules();
901 }
902 catch( PARSE_ERROR& )
903 {
904 wxFAIL_MSG( wxT( "Compiling implicit rules failed." ) );
905 }
906
907 throw original_parse_error;
908 }
909
910 for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
912
913 m_rulesValid = true;
914}
915
916
917void DRC_ENGINE::RunTests( EDA_UNITS aUnits, bool aReportAllTrackErrors, bool aTestFootprints,
918 BOARD_COMMIT* aCommit )
919{
920 PROF_TIMER timer;
921
922 SetUserUnits( aUnits );
923
924 m_reportAllTrackErrors = aReportAllTrackErrors;
925 m_testFootprints = aTestFootprints;
926
927 for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
928 {
929 if( m_designSettings->Ignore( ii ) )
930 m_errorLimits[ ii ] = 0;
931 else if( ii == DRCE_CLEARANCE || ii == DRCE_UNCONNECTED_ITEMS )
933 else
935 }
936
938
939 m_board->IncrementTimeStamp(); // Invalidate all caches...
940
941 DRC_CACHE_GENERATOR cacheGenerator;
942 cacheGenerator.SetDRCEngine( this );
943
944 if( !cacheGenerator.Run() ) // ... and regenerate them.
945 return;
946
947 // Recompute component classes
948 m_board->GetComponentClassManager().ForceComponentClassRecalculation();
949
950 int timestamp = m_board->GetTimeStamp();
951
952 for( DRC_TEST_PROVIDER* provider : m_testProviders )
953 {
954 if( m_logReporter )
955 m_logReporter->Report( wxString::Format( wxT( "Run DRC provider: '%s'" ), provider->GetName() ) );
956
957 PROF_TIMER providerTimer;
958
959 if( !provider->RunTests( aUnits ) )
960 break;
961
962 providerTimer.Stop();
963 wxLogTrace( traceDrcProfile, "DRC provider '%s' took %0.3f ms",
964 provider->GetName(), providerTimer.msecs() );
965 }
966
967 timer.Stop();
968 wxLogTrace( traceDrcProfile, "DRC took %0.3f ms", timer.msecs() );
969
970 // DRC tests are multi-threaded; anything that causes us to attempt to re-generate the
971 // caches while DRC is running is problematic.
972 wxASSERT( timestamp == m_board->GetTimeStamp() );
973}
974
975
976#define REPORT( s ) { if( aReporter ) { aReporter->Report( s ); } }
977
979 PCB_LAYER_ID aLayer, REPORTER* aReporter )
980{
981 DRC_CONSTRAINT constraint = EvalRules( ZONE_CONNECTION_CONSTRAINT, a, b, aLayer, aReporter );
982
983 REPORT( "" )
984 REPORT( wxString::Format( _( "Resolved zone connection type: %s." ),
985 PrintZoneConnection( constraint.m_ZoneConnection ) ) )
986
988 {
989 const PAD* pad = nullptr;
990
991 if( a->Type() == PCB_PAD_T )
992 pad = static_cast<const PAD*>( a );
993 else if( b->Type() == PCB_PAD_T )
994 pad = static_cast<const PAD*>( b );
995
996 if( pad && pad->GetAttribute() == PAD_ATTRIB::PTH )
997 {
999 }
1000 else
1001 {
1002 REPORT( wxString::Format( _( "Pad is not a through hole pad; connection will be: %s." ),
1005 }
1006 }
1007
1008 return constraint;
1009}
1010
1011
1013 const BOARD_ITEM* b, PCB_LAYER_ID aLayer,
1014 REPORTER* aReporter )
1015{
1016 /*
1017 * NOTE: all string manipulation MUST BE KEPT INSIDE the REPORT macro. It absolutely
1018 * kills performance when running bulk DRC tests (where aReporter is nullptr).
1019 */
1020
1021 const BOARD_CONNECTED_ITEM* ac = a && a->IsConnected() ? static_cast<const BOARD_CONNECTED_ITEM*>( a ) : nullptr;
1022 const BOARD_CONNECTED_ITEM* bc = b && b->IsConnected() ? static_cast<const BOARD_CONNECTED_ITEM*>( b ) : nullptr;
1023
1024 bool a_is_non_copper = a && ( !a->IsOnCopperLayer() || isKeepoutZone( a, false ) );
1025 bool b_is_non_copper = b && ( !b->IsOnCopperLayer() || isKeepoutZone( b, false ) );
1026
1027 const PAD* pad = nullptr;
1028 const ZONE* zone = nullptr;
1029 const FOOTPRINT* parentFootprint = nullptr;
1030
1031 if( aConstraintType == ZONE_CONNECTION_CONSTRAINT
1032 || aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT
1033 || aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT
1034 || aConstraintType == SOLDER_MASK_EXPANSION_CONSTRAINT
1035 || aConstraintType == SOLDER_PASTE_ABS_MARGIN_CONSTRAINT
1036 || aConstraintType == SOLDER_PASTE_REL_MARGIN_CONSTRAINT )
1037 {
1038 if( a && a->Type() == PCB_PAD_T )
1039 pad = static_cast<const PAD*>( a );
1040 else if( a && a->Type() == PCB_ZONE_T )
1041 zone = static_cast<const ZONE*>( a );
1042
1043 if( b && b->Type() == PCB_PAD_T )
1044 pad = static_cast<const PAD*>( b );
1045 else if( b && b->Type() == PCB_ZONE_T )
1046 zone = static_cast<const ZONE*>( b );
1047
1048 if( pad )
1049 parentFootprint = pad->GetParentFootprint();
1050 }
1051
1052 DRC_CONSTRAINT constraint;
1053 constraint.m_Type = aConstraintType;
1054
1055 auto applyConstraint =
1056 [&]( const DRC_ENGINE_CONSTRAINT* c )
1057 {
1058 if( c->constraint.m_Value.HasMin() )
1059 {
1060 if( c->parentRule && c->parentRule->IsImplicit() )
1061 constraint.m_ImplicitMin = true;
1062 else
1063 constraint.m_ImplicitMin = false;
1064
1065 constraint.m_Value.SetMin( c->constraint.m_Value.Min() );
1066 }
1067
1068 if( c->constraint.m_Value.HasOpt() )
1069 constraint.m_Value.SetOpt( c->constraint.m_Value.Opt() );
1070
1071 if( c->constraint.m_Value.HasMax() )
1072 constraint .m_Value.SetMax( c->constraint.m_Value.Max() );
1073
1074 switch( c->constraint.m_Type )
1075 {
1083 if( constraint.m_Value.Min() > MAXIMUM_CLEARANCE )
1084 constraint.m_Value.SetMin( MAXIMUM_CLEARANCE );
1085
1086 break;
1087
1088 default:
1089 break;
1090 }
1091
1092 // While the expectation would be to OR the disallow flags, we've already
1093 // masked them down to aItem's type -- so we're really only looking for a
1094 // boolean here.
1095 constraint.m_DisallowFlags = c->constraint.m_DisallowFlags;
1096
1097 constraint.m_ZoneConnection = c->constraint.m_ZoneConnection;
1098
1099 constraint.SetParentRule( c->constraint.GetParentRule() );
1100
1101 constraint.SetOptionsFromOther( c->constraint );
1102 };
1103
1104 const FOOTPRINT* footprints[2] = { a ? a->GetParentFootprint() : nullptr,
1105 b ? b->GetParentFootprint() : nullptr };
1106
1107 // Handle Footprint net ties, which will zero out the clearance for footprint objects
1108 if( aConstraintType == CLEARANCE_CONSTRAINT // Only zero clearance, other constraints still apply
1109 && ( ( ( !ac ) ^ ( !bc ) )// Only apply to cases where we are comparing a connected item to a non-connected item
1110 // and not both connected. Connected items of different nets still need to be checked
1111 // for their standard clearance value
1112 || ( ( footprints[0] == footprints[1] ) // Unless both items are in the same footprint
1113 && footprints[0] ) ) // And that footprint exists
1114 && !a_is_non_copper // Also, both elements need to be on copper layers
1115 && !b_is_non_copper )
1116 {
1117 const BOARD_ITEM* child_items[2] = {a, b};
1118
1119 // These are the items being compared against, so the order is reversed
1120 const BOARD_CONNECTED_ITEM* alt_items[2] = {bc, ac};
1121
1122 for( int ii = 0; ii < 2; ++ii )
1123 {
1124 // We need both a footprint item and a connected item to check for a net tie
1125 if( !footprints[ii] || !alt_items[ii] )
1126 continue;
1127
1128 const std::set<int>& netcodes = footprints[ii]->GetNetTieCache( child_items[ii] );
1129
1130 auto it = netcodes.find( alt_items[ii]->GetNetCode() );
1131
1132 if( it != netcodes.end() )
1133 {
1134 REPORT( "" )
1135 REPORT( wxString::Format( _( "Net tie on %s; clearance: 0." ),
1136 EscapeHTML( footprints[ii]->GetItemDescription( this, true ) ) ) )
1137
1138 constraint.SetName( _( "net tie" ) );
1139 constraint.m_Value.SetMin( 0 );
1140 return constraint;
1141 }
1142 }
1143 }
1144
1145 // Local overrides take precedence over everything *except* board min clearance
1146 if( aConstraintType == CLEARANCE_CONSTRAINT || aConstraintType == HOLE_CLEARANCE_CONSTRAINT )
1147 {
1148 int override_val = 0;
1149 std::optional<int> overrideA;
1150 std::optional<int> overrideB;
1151
1152 if( ac && !b_is_non_copper )
1153 overrideA = ac->GetClearanceOverrides( nullptr );
1154
1155 if( bc && !a_is_non_copper )
1156 overrideB = bc->GetClearanceOverrides( nullptr );
1157
1158 if( overrideA.has_value() || overrideB.has_value() )
1159 {
1160 wxString msg;
1161
1162 if( overrideA.has_value() )
1163 {
1164 REPORT( "" )
1165 REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ),
1166 EscapeHTML( a->GetItemDescription( this, true ) ),
1167 MessageTextFromValue( overrideA.value() ) ) )
1168
1169 override_val = ac->GetClearanceOverrides( &msg ).value();
1170 }
1171
1172 if( overrideB.has_value() )
1173 {
1174 REPORT( "" )
1175 REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ),
1176 EscapeHTML( b->GetItemDescription( this, true ) ),
1177 MessageTextFromValue( overrideB.value() ) ) )
1178
1179 if( overrideB > override_val )
1180 override_val = bc->GetClearanceOverrides( &msg ).value();
1181 }
1182
1183 if( override_val )
1184 {
1185 if( aConstraintType == CLEARANCE_CONSTRAINT )
1186 {
1187 if( override_val < m_designSettings->m_MinClearance )
1188 {
1189 override_val = m_designSettings->m_MinClearance;
1190 msg = _( "board minimum" );
1191
1192 REPORT( "" )
1193 REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
1194 MessageTextFromValue( override_val ) ) )
1195 }
1196 }
1197 else
1198 {
1199 if( override_val < m_designSettings->m_HoleClearance )
1200 {
1201 override_val = m_designSettings->m_HoleClearance;
1202 msg = _( "board minimum hole" );
1203
1204 REPORT( "" )
1205 REPORT( wxString::Format( _( "Board minimum hole clearance: %s." ),
1206 MessageTextFromValue( override_val ) ) )
1207 }
1208 }
1209
1210 constraint.SetName( msg );
1211 constraint.m_Value.SetMin( override_val );
1212 return constraint;
1213 }
1214 }
1215 }
1216 else if( aConstraintType == ZONE_CONNECTION_CONSTRAINT )
1217 {
1218 if( pad && pad->GetLocalZoneConnection() != ZONE_CONNECTION::INHERITED )
1219 {
1220 wxString msg;
1221 ZONE_CONNECTION override = pad->GetZoneConnectionOverrides( &msg );
1222
1223 REPORT( "" )
1224 REPORT( wxString::Format( _( "Local override on %s; zone connection: %s." ),
1225 EscapeHTML( pad->GetItemDescription( this, true ) ),
1226 PrintZoneConnection( override ) ) )
1227
1228 constraint.SetName( msg );
1229 constraint.m_ZoneConnection = override;
1230 return constraint;
1231 }
1232 }
1233 else if( aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT )
1234 {
1235 if( pad && pad->GetLocalThermalGapOverride( nullptr ) > 0 )
1236 {
1237 wxString msg;
1238 int gap_override = pad->GetLocalThermalGapOverride( &msg );
1239
1240 REPORT( "" )
1241 REPORT( wxString::Format( _( "Local override on %s; thermal relief gap: %s." ),
1242 EscapeHTML( pad->GetItemDescription( this, true ) ),
1243 MessageTextFromValue( gap_override ) ) )
1244
1245 constraint.SetName( msg );
1246 constraint.m_Value.SetMin( gap_override );
1247 return constraint;
1248 }
1249 }
1250 else if( aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT )
1251 {
1252 if( pad && pad->GetLocalSpokeWidthOverride( nullptr ) > 0 )
1253 {
1254 wxString msg;
1255 int spoke_override = pad->GetLocalSpokeWidthOverride( &msg );
1256
1257 REPORT( "" )
1258 REPORT( wxString::Format( _( "Local override on %s; thermal spoke width: %s." ),
1259 EscapeHTML( pad->GetItemDescription( this, true ) ),
1260 MessageTextFromValue( spoke_override ) ) )
1261
1262 if( zone && zone->GetMinThickness() > spoke_override )
1263 {
1264 spoke_override = zone->GetMinThickness();
1265
1266 REPORT( "" )
1267 REPORT( wxString::Format( _( "%s min thickness: %s." ),
1268 EscapeHTML( zone->GetItemDescription( this, true ) ),
1269 MessageTextFromValue( spoke_override ) ) )
1270 }
1271
1272 constraint.SetName( msg );
1273 constraint.m_Value.SetMin( spoke_override );
1274 return constraint;
1275 }
1276 }
1277 else if( aConstraintType == SOLDER_MASK_EXPANSION_CONSTRAINT )
1278 {
1279 std::optional<int> override;
1280 const BOARD_ITEM* overrideItem = a;
1281
1282 if( pad )
1283 override = pad->GetLocalSolderMaskMargin();
1284 else if( a->Type() == PCB_SHAPE_T )
1285 override = static_cast<const PCB_SHAPE*>( a )->GetLocalSolderMaskMargin();
1286 else if( const PCB_TRACK* track = dynamic_cast<const PCB_TRACK*>( a ) )
1287 override = track->GetLocalSolderMaskMargin();
1288
1289 if( !override.has_value() && pad )
1290 {
1291 if( FOOTPRINT* overrideFootprint = pad->GetParentFootprint() )
1292 {
1293 override = overrideFootprint->GetLocalSolderMaskMargin();
1294 overrideItem = overrideFootprint;
1295 }
1296 }
1297
1298 if( override )
1299 {
1300 REPORT( "" )
1301 REPORT( wxString::Format( _( "Local override on %s; solder mask expansion: %s." ),
1302 EscapeHTML( overrideItem->GetItemDescription( this, true ) ),
1303 MessageTextFromValue( override.value() ) ) )
1304
1305 constraint.m_Value.SetOpt( override.value() );
1306 return constraint;
1307 }
1308 }
1309 else if( aConstraintType == SOLDER_PASTE_ABS_MARGIN_CONSTRAINT )
1310 {
1311 std::optional<int> override;
1312 const BOARD_ITEM* overrideItem = a;
1313
1314 if( pad )
1315 override = pad->GetLocalSolderPasteMargin();
1316
1317 if( !override.has_value() && pad )
1318 {
1319 if( FOOTPRINT* overrideFootprint = pad->GetParentFootprint() )
1320 {
1321 override = overrideFootprint->GetLocalSolderPasteMargin();
1322 overrideItem = overrideFootprint;
1323 }
1324 }
1325
1326 if( override )
1327 {
1328 REPORT( "" )
1329 REPORT( wxString::Format( _( "Local override on %s; solder paste absolute clearance: %s." ),
1330 EscapeHTML( overrideItem->GetItemDescription( this, true ) ),
1331 MessageTextFromValue( override.value() ) ) )
1332
1333 constraint.m_Value.SetOpt( override.value_or( 0 ) );
1334 return constraint;
1335 }
1336 }
1337 else if( aConstraintType == SOLDER_PASTE_REL_MARGIN_CONSTRAINT )
1338 {
1339 std::optional<double> overrideRatio;
1340 const BOARD_ITEM* overrideItem = a;
1341
1342 if( pad )
1343 overrideRatio = pad->GetLocalSolderPasteMarginRatio();
1344
1345 if( !overrideRatio.has_value() && pad )
1346 {
1347 if( FOOTPRINT* overrideFootprint = pad->GetParentFootprint() )
1348 {
1349 overrideRatio = overrideFootprint->GetLocalSolderPasteMarginRatio();
1350 overrideItem = overrideFootprint;
1351 }
1352 }
1353
1354 if( overrideRatio )
1355 {
1356 REPORT( "" )
1357 REPORT( wxString::Format( _( "Local override on %s; solder paste relative clearance: %s." ),
1358 EscapeHTML( overrideItem->GetItemDescription( this, true ) ),
1359 MessageTextFromValue( overrideRatio.value() * 100.0 ) ) )
1360
1361 constraint.m_Value.SetOpt( KiROUND( overrideRatio.value_or( 0 ) * 1000 ) );
1362 return constraint;
1363 }
1364 }
1365
1366 auto testAssertion =
1367 [&]( const DRC_ENGINE_CONSTRAINT* c )
1368 {
1369 REPORT( wxString::Format( _( "Checking assertion '%s'." ),
1370 EscapeHTML( c->constraint.m_Test->GetExpression() ) ) )
1371
1372 if( c->constraint.m_Test->EvaluateFor( a, b, c->constraint.m_Type, aLayer, aReporter ) )
1373 REPORT( _( "Assertion passed." ) )
1374 else
1375 REPORT( EscapeHTML( _( "--> Assertion failed. <--" ) ) )
1376 };
1377
1378 auto processConstraint =
1379 [&]( const DRC_ENGINE_CONSTRAINT* c )
1380 {
1381 bool implicit = c->parentRule && c->parentRule->IsImplicit();
1382
1383 REPORT( "" )
1384
1385 switch( c->constraint.m_Type )
1386 {
1394 REPORT( wxString::Format( _( "Checking %s clearance: %s." ),
1395 EscapeHTML( c->constraint.GetName() ),
1396 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1397 break;
1399 REPORT( wxString::Format( _( "Checking %s creepage: %s." ),
1400 EscapeHTML( c->constraint.GetName() ),
1401 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1402 break;
1404 REPORT( wxString::Format( _( "Checking %s max uncoupled length: %s." ),
1405 EscapeHTML( c->constraint.GetName() ),
1406 MessageTextFromValue( c->constraint.m_Value.Max() ) ) )
1407 break;
1408
1409 case SKEW_CONSTRAINT:
1410 REPORT( wxString::Format( _( "Checking %s max skew: %s." ),
1411 EscapeHTML( c->constraint.GetName() ),
1412 MessageTextFromValue( c->constraint.m_Value.Max() ) ) )
1413 break;
1414
1416 REPORT( wxString::Format( _( "Checking %s gap: %s." ),
1417 EscapeHTML( c->constraint.GetName() ),
1418 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1419 break;
1420
1422 REPORT( wxString::Format( _( "Checking %s thermal spoke width: %s." ),
1423 EscapeHTML( c->constraint.GetName() ),
1424 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1425 break;
1426
1428 REPORT( wxString::Format( _( "Checking %s solder mask expansion: %s." ),
1429 EscapeHTML( c->constraint.GetName() ),
1430 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1431 break;
1432
1434 REPORT( wxString::Format( _( "Checking %s solder paste absolute clearance: %s." ),
1435 EscapeHTML( c->constraint.GetName() ),
1436 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1437 break;
1438
1440 REPORT( wxString::Format( _( "Checking %s solder paste relative clearance: %s." ),
1441 EscapeHTML( c->constraint.GetName() ),
1442 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1443 break;
1444
1446 REPORT( wxString::Format( _( "Checking %s min spoke count: %s." ),
1447 EscapeHTML( c->constraint.GetName() ),
1448 MessageTextFromUnscaledValue( c->constraint.m_Value.Min() ) ) )
1449 break;
1450
1452 REPORT( wxString::Format( _( "Checking %s zone connection: %s." ),
1453 EscapeHTML( c->constraint.GetName() ),
1454 PrintZoneConnection( c->constraint.m_ZoneConnection ) ) )
1455 break;
1456
1464 case LENGTH_CONSTRAINT:
1470 {
1471 if( implicit )
1472 {
1473 switch( c->constraint.m_Type )
1474 {
1476 if( c->constraint.m_Value.HasOpt() )
1477 {
1478 REPORT( wxString::Format( _( "Checking %s track width: opt %s." ),
1479 EscapeHTML( c->constraint.GetName() ),
1480 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1481 }
1482 else if( c->constraint.m_Value.HasMin() )
1483 {
1484 REPORT( wxString::Format( _( "Checking %s track width: min %s." ),
1485 EscapeHTML( c->constraint.GetName() ),
1486 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1487 }
1488
1489 break;
1490
1492 REPORT( wxString::Format( _( "Checking %s annular width: min %s." ),
1493 EscapeHTML( c->constraint.GetName() ),
1494 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1495 break;
1496
1498 if( c->constraint.m_Value.HasOpt() )
1499 {
1500 REPORT( wxString::Format( _( "Checking %s via diameter: opt %s." ),
1501 EscapeHTML( c->constraint.GetName() ),
1502 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1503 }
1504 else if( c->constraint.m_Value.HasMin() )
1505 {
1506 REPORT( wxString::Format( _( "Checking %s via diameter: min %s." ),
1507 EscapeHTML( c->constraint.GetName() ),
1508 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1509 }
1510 break;
1511
1513 if( c->constraint.m_Value.HasOpt() )
1514 {
1515 REPORT( wxString::Format( _( "Checking %s hole size: opt %s." ),
1516 EscapeHTML( c->constraint.GetName() ),
1517 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1518 }
1519 else if( c->constraint.m_Value.HasMin() )
1520 {
1521 REPORT( wxString::Format( _( "Checking %s hole size: min %s." ),
1522 EscapeHTML( c->constraint.GetName() ),
1523 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1524 }
1525
1526 break;
1527
1531 REPORT( wxString::Format( _( "Checking %s: min %s." ),
1532 EscapeHTML( c->constraint.GetName() ),
1533 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1534 break;
1535
1537 if( c->constraint.m_Value.HasOpt() )
1538 {
1539 REPORT( wxString::Format( _( "Checking %s diff pair gap: opt %s." ),
1540 EscapeHTML( c->constraint.GetName() ),
1541 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1542 }
1543 else if( c->constraint.m_Value.HasMin() )
1544 {
1545 REPORT( wxString::Format( _( "Checking %s clearance: min %s." ),
1546 EscapeHTML( c->constraint.GetName() ),
1547 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1548 }
1549
1550 break;
1551
1553 REPORT( wxString::Format( _( "Checking %s hole to hole: min %s." ),
1554 EscapeHTML( c->constraint.GetName() ),
1555 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1556 break;
1557
1558 default:
1559 REPORT( wxString::Format( _( "Checking %s." ),
1560 EscapeHTML( c->constraint.GetName() ) ) )
1561 }
1562 }
1563 else
1564 {
1565 REPORT( wxString::Format( _( "Checking %s: min %s; opt %s; max %s." ),
1566 EscapeHTML( c->constraint.GetName() ),
1567 c->constraint.m_Value.HasMin()
1568 ? MessageTextFromValue( c->constraint.m_Value.Min() )
1569 : wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" ),
1570 c->constraint.m_Value.HasOpt()
1571 ? MessageTextFromValue( c->constraint.m_Value.Opt() )
1572 : wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" ),
1573 c->constraint.m_Value.HasMax()
1574 ? MessageTextFromValue( c->constraint.m_Value.Max() )
1575 : wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" ) ) )
1576 }
1577 break;
1578 }
1579
1580 default:
1581 REPORT( wxString::Format( _( "Checking %s." ),
1582 EscapeHTML( c->constraint.GetName() ) ) )
1583 }
1584
1585 if( c->constraint.m_Type == CLEARANCE_CONSTRAINT )
1586 {
1587 if( a_is_non_copper || b_is_non_copper )
1588 {
1589 if( implicit )
1590 {
1591 REPORT( _( "Netclass clearances apply only between copper items." ) )
1592 }
1593 else if( a_is_non_copper )
1594 {
1595 REPORT( wxString::Format( _( "%s contains no copper. Rule ignored." ),
1596 EscapeHTML( a->GetItemDescription( this, true ) ) ) )
1597 }
1598 else if( b_is_non_copper )
1599 {
1600 REPORT( wxString::Format( _( "%s contains no copper. Rule ignored." ),
1601 EscapeHTML( b->GetItemDescription( this, true ) ) ) )
1602 }
1603
1604 return;
1605 }
1606 }
1607 else if( c->constraint.m_Type == DISALLOW_CONSTRAINT )
1608 {
1609 int mask;
1610
1611 if( a->GetFlags() & HOLE_PROXY )
1612 {
1613 mask = DRC_DISALLOW_HOLES;
1614 }
1615 else if( a->Type() == PCB_VIA_T )
1616 {
1617 const PCB_VIA* via = static_cast<const PCB_VIA*>( a );
1618
1619 if( via->IsMicroVia() )
1621 else if( via->IsBlindVia() )
1623 else if( via->IsBuriedVia() )
1625 else
1627 }
1628 else
1629 {
1630 switch( a->Type() )
1631 {
1632 case PCB_TRACE_T: mask = DRC_DISALLOW_TRACKS; break;
1633 case PCB_ARC_T: mask = DRC_DISALLOW_TRACKS; break;
1634 case PCB_PAD_T: mask = DRC_DISALLOW_PADS; break;
1635 case PCB_FOOTPRINT_T: mask = DRC_DISALLOW_FOOTPRINTS; break;
1636 case PCB_SHAPE_T: mask = DRC_DISALLOW_GRAPHICS; break;
1637 case PCB_BARCODE_T: mask = DRC_DISALLOW_GRAPHICS; break;
1638 case PCB_FIELD_T: mask = DRC_DISALLOW_TEXTS; break;
1639 case PCB_TEXT_T: mask = DRC_DISALLOW_TEXTS; break;
1640 case PCB_TEXTBOX_T: mask = DRC_DISALLOW_TEXTS; break;
1641 case PCB_TABLE_T: mask = DRC_DISALLOW_TEXTS; break;
1642
1643 case PCB_ZONE_T:
1644 // Treat teardrop areas as tracks for DRC purposes
1645 if( static_cast<const ZONE*>( a )->IsTeardropArea() )
1646 mask = DRC_DISALLOW_TRACKS;
1647 else
1648 mask = DRC_DISALLOW_ZONES;
1649
1650 break;
1651
1652 case PCB_LOCATE_HOLE_T: mask = DRC_DISALLOW_HOLES; break;
1653 default: mask = 0; break;
1654 }
1655 }
1656
1657 if( ( c->constraint.m_DisallowFlags & mask ) == 0 )
1658 {
1659 if( implicit )
1660 REPORT( _( "Keepout constraint not met." ) )
1661 else
1662 REPORT( _( "Disallow constraint not met." ) )
1663
1664 return;
1665 }
1666
1667 LSET itemLayers = a->GetLayerSet();
1668
1669 if( a->Type() == PCB_FOOTPRINT_T )
1670 {
1671 const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( a );
1672
1673 if( !footprint->GetCourtyard( F_CrtYd ).IsEmpty() )
1674 itemLayers |= LSET::FrontMask();
1675
1676 if( !footprint->GetCourtyard( B_CrtYd ).IsEmpty() )
1677 itemLayers |= LSET::BackMask();
1678 }
1679
1680 if( !( c->layerTest & itemLayers ).any() )
1681 {
1682 if( implicit )
1683 {
1684 REPORT( _( "Keepout layer(s) not matched." ) )
1685 }
1686 else if( c->parentRule )
1687 {
1688 REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
1689 EscapeHTML( c->parentRule->m_LayerSource ) ) )
1690 }
1691 else
1692 {
1693 REPORT( _( "Rule layer not matched; rule ignored." ) )
1694 }
1695
1696 return;
1697 }
1698 }
1699
1700 if( ( IsPcbLayer( aLayer ) && !c->layerTest.test( aLayer ) )
1701 || ( m_board->GetEnabledLayers() & c->layerTest ).count() == 0 )
1702 {
1703 if( implicit )
1704 {
1705 REPORT( _( "Constraint layer not matched." ) )
1706 }
1707 else if( c->parentRule )
1708 {
1709 REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
1710 EscapeHTML( c->parentRule->m_LayerSource ) ) )
1711 }
1712 else
1713 {
1714 REPORT( _( "Rule layer not matched; rule ignored." ) )
1715 }
1716 }
1717 else if( c->constraint.m_Type == HOLE_TO_HOLE_CONSTRAINT
1718 && ( !a->HasDrilledHole() && !b->HasDrilledHole() ) )
1719 {
1720 // Report non-drilled-holes as an implicit condition
1721 REPORT( wxString::Format( _( "%s is not a drilled hole; rule ignored." ),
1722 a->GetItemDescription( this, true ) ) )
1723 }
1724 else if( !c->condition || c->condition->GetExpression().IsEmpty() )
1725 {
1726 if( aReporter )
1727 {
1728 if( implicit )
1729 {
1730 REPORT( _( "Unconditional constraint applied." ) )
1731 }
1732 else if( constraint.m_Type == ASSERTION_CONSTRAINT )
1733 {
1734 REPORT( _( "Unconditional rule applied." ) )
1735 testAssertion( c );
1736 }
1737 else
1738 {
1739 REPORT( _( "Unconditional rule applied; overrides previous constraints." ) )
1740 }
1741 }
1742
1743 applyConstraint( c );
1744 }
1745 else
1746 {
1747 // For implicit keepout rules with a pre-resolved zone pointer, skip the
1748 // expensive expression evaluation when the item doesn't overlap the
1749 // zone's bounding box. This avoids UUID string parsing and cache lock
1750 // contention for the vast majority of item-keepout pairs.
1751 if( c->implicitKeepoutZone && !aReporter )
1752 {
1753 BOX2I itemBBox = a->GetBoundingBox();
1754 BOX2I zoneBBox = c->implicitKeepoutZone->GetBoundingBox();
1755
1756 if( !itemBBox.Intersects( zoneBBox ) )
1757 return;
1758 }
1759
1760 if( implicit )
1761 {
1762 // Don't report on implicit rule conditions; they're synthetic.
1763 }
1764 else
1765 {
1766 REPORT( wxString::Format( _( "Checking rule condition '%s'." ),
1767 EscapeHTML( c->condition->GetExpression() ) ) )
1768 }
1769
1770 if( c->condition->EvaluateFor( a, b, c->constraint.m_Type, aLayer, aReporter ) )
1771 {
1772 if( aReporter )
1773 {
1774 if( implicit )
1775 {
1776 REPORT( _( "Constraint applied." ) )
1777 }
1778 else if( constraint.m_Type == ASSERTION_CONSTRAINT )
1779 {
1780 REPORT( _( "Rule applied." ) )
1781 testAssertion( c );
1782 }
1783 else
1784 {
1785 REPORT( _( "Rule applied; overrides previous constraints." ) )
1786 }
1787 }
1788
1789 applyConstraint( c );
1790 }
1791 else
1792 {
1793 REPORT( implicit ? _( "Membership not satisfied; constraint ignored." )
1794 : _( "Condition not satisfied; rule ignored." ) )
1795 }
1796 }
1797 };
1798
1799 // Fast-path for netclass clearance when no explicit or diff pair override rules exist
1800 if( aConstraintType == CLEARANCE_CONSTRAINT
1803 && !aReporter
1804 && !a_is_non_copper
1805 && ( !b || !b_is_non_copper ) )
1806 {
1807 int clearance = 0;
1808
1809 // Get netclass names outside of the lock to minimize critical section
1810 wxString ncNameA;
1811 wxString ncNameB;
1812
1813 if( ac )
1814 {
1815 NETCLASS* ncA = ac->GetEffectiveNetClass();
1816
1817 if( ncA )
1818 ncNameA = ncA->GetName();
1819 }
1820
1821 if( bc )
1822 {
1823 NETCLASS* ncB = bc->GetEffectiveNetClass();
1824
1825 if( ncB )
1826 ncNameB = ncB->GetName();
1827 }
1828
1829 // Look up clearances with shared lock protection
1830 if( !ncNameA.empty() || !ncNameB.empty() )
1831 {
1832 std::shared_lock<std::shared_mutex> readLock( m_clearanceCacheMutex );
1833
1834 if( !ncNameA.empty() )
1835 {
1836 auto it = m_netclassClearances.find( ncNameA );
1837
1838 if( it != m_netclassClearances.end() )
1839 clearance = it->second;
1840 }
1841
1842 if( !ncNameB.empty() )
1843 {
1844 auto it = m_netclassClearances.find( ncNameB );
1845
1846 if( it != m_netclassClearances.end() )
1847 clearance = std::max( clearance, it->second );
1848 }
1849 }
1850
1851 if( clearance > 0 )
1852 {
1853 constraint.m_Value.SetMin( clearance );
1854 constraint.m_ImplicitMin = true;
1855 }
1856 }
1857 else
1858 {
1859 auto it = m_constraintMap.find( aConstraintType );
1860
1861 if( it != m_constraintMap.end() )
1862 {
1863 for( DRC_ENGINE_CONSTRAINT* rule : *it->second )
1864 processConstraint( rule );
1865 }
1866 }
1867
1868 if( constraint.GetParentRule() && !constraint.GetParentRule()->IsImplicit() )
1869 return constraint;
1870
1871 // Special case for properties which can be inherited from parent footprints. We've already
1872 // checked for local overrides, and there were no rules targetting the item itself, so we know
1873 // we're inheriting and need to see if there are any rules targetting the parent footprint.
1874 if( pad && parentFootprint && ( aConstraintType == ZONE_CONNECTION_CONSTRAINT
1875 || aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT
1876 || aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT
1877 || aConstraintType == SOLDER_MASK_EXPANSION_CONSTRAINT
1878 || aConstraintType == SOLDER_PASTE_ABS_MARGIN_CONSTRAINT
1879 || aConstraintType == SOLDER_PASTE_REL_MARGIN_CONSTRAINT ) )
1880 {
1881 REPORT( "" )
1882 REPORT( wxString::Format( _( "Inheriting from parent: %s." ),
1883 EscapeHTML( parentFootprint->GetItemDescription( this, true ) ) ) )
1884
1885 if( a == pad )
1886 a = parentFootprint;
1887 else
1888 b = parentFootprint;
1889
1890 auto it = m_constraintMap.find( aConstraintType );
1891
1892 if( it != m_constraintMap.end() )
1893 {
1894 for( DRC_ENGINE_CONSTRAINT* rule : *it->second )
1895 processConstraint( rule );
1896
1897 if( constraint.GetParentRule() && !constraint.GetParentRule()->IsImplicit() )
1898 return constraint;
1899 }
1900
1901 // Found nothing again? Return the defaults.
1902 if( aConstraintType == SOLDER_MASK_EXPANSION_CONSTRAINT )
1903 {
1904 constraint.SetParentRule( nullptr );
1905 constraint.SetName( _( "board setup" ) );
1906 constraint.m_Value.SetOpt( m_designSettings->m_SolderMaskExpansion );
1907 return constraint;
1908 }
1909 else if( aConstraintType == SOLDER_PASTE_ABS_MARGIN_CONSTRAINT )
1910 {
1911 constraint.SetParentRule( nullptr );
1912 constraint.SetName( _( "board setup" ) );
1913 constraint.m_Value.SetOpt( m_designSettings->m_SolderPasteMargin );
1914 return constraint;
1915 }
1916 else if( aConstraintType == SOLDER_PASTE_REL_MARGIN_CONSTRAINT )
1917 {
1918 constraint.SetParentRule( nullptr );
1919 constraint.SetName( _( "board setup" ) );
1920 constraint.m_Value.SetOpt( KiROUND( m_designSettings->m_SolderPasteMarginRatio * 1000 ) );
1921 return constraint;
1922 }
1923 }
1924
1925 // Unfortunately implicit rules don't work for local clearances (such as zones) because
1926 // they have to be max'ed with netclass values (which are already implicit rules), and our
1927 // rule selection paradigm is "winner takes all".
1928 if( aConstraintType == CLEARANCE_CONSTRAINT )
1929 {
1930 int global = constraint.m_Value.Min();
1931 int clearance = global;
1932 bool needBlankLine = true;
1933
1934 if( ac && ac->GetLocalClearance().has_value() )
1935 {
1936 int localA = ac->GetLocalClearance().value();
1937
1938 if( needBlankLine )
1939 {
1940 REPORT( "" )
1941 needBlankLine = false;
1942 }
1943
1944 REPORT( wxString::Format( _( "Local clearance on %s: %s." ),
1945 EscapeHTML( a->GetItemDescription( this, true ) ),
1946 MessageTextFromValue( localA ) ) )
1947
1948 if( localA > clearance )
1949 {
1950 wxString msg;
1951 clearance = ac->GetLocalClearance( &msg ).value();
1952 constraint.SetParentRule( nullptr );
1953 constraint.SetName( msg );
1954 constraint.m_Value.SetMin( clearance );
1955 }
1956 }
1957
1958 if( bc && bc->GetLocalClearance().has_value() )
1959 {
1960 int localB = bc->GetLocalClearance().value();
1961
1962 if( needBlankLine )
1963 {
1964 REPORT( "" )
1965 needBlankLine = false;
1966 }
1967
1968 REPORT( wxString::Format( _( "Local clearance on %s: %s." ),
1969 EscapeHTML( b->GetItemDescription( this, true ) ),
1970 MessageTextFromValue( localB ) ) )
1971
1972 if( localB > clearance )
1973 {
1974 wxString msg;
1975 clearance = bc->GetLocalClearance( &msg ).value();
1976 constraint.SetParentRule( nullptr );
1977 constraint.SetName( msg );
1978 constraint.m_Value.SetMin( clearance );
1979 }
1980 }
1981
1982 if( !a_is_non_copper && !b_is_non_copper )
1983 {
1984 if( needBlankLine )
1985 {
1986 REPORT( "" )
1987 needBlankLine = false;
1988 }
1989
1990 REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
1991 MessageTextFromValue( m_designSettings->m_MinClearance ) ) )
1992
1993 if( clearance < m_designSettings->m_MinClearance )
1994 {
1995 constraint.SetParentRule( nullptr );
1996 constraint.SetName( _( "board minimum" ) );
1997 constraint.m_Value.SetMin( m_designSettings->m_MinClearance );
1998 }
1999 }
2000
2001 return constraint;
2002 }
2003 else if( aConstraintType == DIFF_PAIR_GAP_CONSTRAINT )
2004 {
2005 REPORT( "" )
2006 REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
2007 MessageTextFromValue( m_designSettings->m_MinClearance ) ) )
2008
2009 if( constraint.m_Value.Min() < m_designSettings->m_MinClearance )
2010 {
2011 constraint.SetParentRule( nullptr );
2012 constraint.SetName( _( "board minimum" ) );
2013 constraint.m_Value.SetMin( m_designSettings->m_MinClearance );
2014 }
2015
2016 return constraint;
2017 }
2018 else if( aConstraintType == ZONE_CONNECTION_CONSTRAINT )
2019 {
2020 if( pad && parentFootprint )
2021 {
2022 ZONE_CONNECTION local = parentFootprint->GetLocalZoneConnection();
2023
2024 if( local != ZONE_CONNECTION::INHERITED )
2025 {
2026 REPORT( "" )
2027 REPORT( wxString::Format( _( "%s zone connection: %s." ),
2028 EscapeHTML( parentFootprint->GetItemDescription( this, true ) ),
2029 PrintZoneConnection( local ) ) )
2030
2031 constraint.SetParentRule( nullptr );
2032 constraint.SetName( _( "footprint" ) );
2033 constraint.m_ZoneConnection = local;
2034 return constraint;
2035 }
2036 }
2037
2038 if( zone )
2039 {
2040 ZONE_CONNECTION local = zone->GetPadConnection();
2041
2042 REPORT( "" )
2043 REPORT( wxString::Format( _( "%s pad connection: %s." ),
2044 EscapeHTML( zone->GetItemDescription( this, true ) ),
2045 PrintZoneConnection( local ) ) )
2046
2047 constraint.SetParentRule( nullptr );
2048 constraint.SetName( _( "zone" ) );
2049 constraint.m_ZoneConnection = local;
2050 return constraint;
2051 }
2052 }
2053 else if( aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT )
2054 {
2055 if( zone )
2056 {
2057 int local = zone->GetThermalReliefGap();
2058
2059 REPORT( "" )
2060 REPORT( wxString::Format( _( "%s thermal relief gap: %s." ),
2061 EscapeHTML( zone->GetItemDescription( this, true ) ),
2062 MessageTextFromValue( local ) ) )
2063
2064 constraint.SetParentRule( nullptr );
2065 constraint.SetName( _( "zone" ) );
2066 constraint.m_Value.SetMin( local );
2067 return constraint;
2068 }
2069 }
2070 else if( aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT )
2071 {
2072 if( zone )
2073 {
2074 int local = zone->GetThermalReliefSpokeWidth();
2075
2076 REPORT( "" )
2077 REPORT( wxString::Format( _( "%s thermal spoke width: %s." ),
2078 EscapeHTML( zone->GetItemDescription( this, true ) ),
2079 MessageTextFromValue( local ) ) )
2080
2081 constraint.SetParentRule( nullptr );
2082 constraint.SetName( _( "zone" ) );
2083 constraint.m_Value.SetMin( local );
2084 return constraint;
2085 }
2086 }
2087
2088 if( !constraint.GetParentRule() )
2089 {
2090 constraint.m_Type = NULL_CONSTRAINT;
2091 constraint.m_DisallowFlags = 0;
2092 }
2093
2094 return constraint;
2095}
2096
2097
2099 PCB_LAYER_ID aLayer )
2100{
2103
2104 c = EvalRules( CLEARANCE_CONSTRAINT, a, b, aLayer );
2105
2106 if( c.m_Value.HasMin() )
2107 result.clearance = c.m_Value.Min();
2108
2109 c = EvalRules( HOLE_CLEARANCE_CONSTRAINT, a, b, aLayer );
2110
2111 if( c.m_Value.HasMin() )
2112 result.holeClearance = c.m_Value.Min();
2113
2114 c = EvalRules( HOLE_TO_HOLE_CONSTRAINT, a, b, aLayer );
2115
2116 if( c.m_Value.HasMin() )
2117 result.holeToHole = c.m_Value.Min();
2118
2119 c = EvalRules( EDGE_CLEARANCE_CONSTRAINT, a, b, aLayer );
2120
2121 if( c.m_Value.HasMin() )
2122 result.edgeClearance = c.m_Value.Min();
2123
2124 c = EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, a, b, aLayer );
2125
2126 if( c.m_Value.HasMin() )
2127 result.physicalClearance = c.m_Value.Min();
2128
2129 return result;
2130}
2131
2132
2134 std::function<void( const DRC_CONSTRAINT* )> aFailureHandler,
2135 REPORTER* aReporter )
2136{
2137 /*
2138 * NOTE: all string manipulation MUST BE KEPT INSIDE the REPORT macro. It absolutely
2139 * kills performance when running bulk DRC tests (where aReporter is nullptr).
2140 */
2141
2142 auto testAssertion =
2143 [&]( const DRC_ENGINE_CONSTRAINT* c )
2144 {
2145 REPORT( wxString::Format( _( "Checking rule assertion '%s'." ),
2146 EscapeHTML( c->constraint.m_Test->GetExpression() ) ) )
2147
2148 if( c->constraint.m_Test->EvaluateFor( a, nullptr, c->constraint.m_Type,
2149 a->GetLayer(), aReporter ) )
2150 {
2151 REPORT( _( "Assertion passed." ) )
2152 }
2153 else
2154 {
2155 REPORT( EscapeHTML( _( "--> Assertion failed. <--" ) ) )
2156 aFailureHandler( &c->constraint );
2157 }
2158 };
2159
2160 auto processConstraint =
2161 [&]( const DRC_ENGINE_CONSTRAINT* c )
2162 {
2163 REPORT( "" )
2164 REPORT( wxString::Format( _( "Checking %s." ), c->constraint.GetName() ) )
2165
2166 if( !( a->GetLayerSet() & c->layerTest ).any() )
2167 {
2168 REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
2169 EscapeHTML( c->parentRule->m_LayerSource ) ) )
2170 }
2171
2172 if( !c->condition || c->condition->GetExpression().IsEmpty() )
2173 {
2174 REPORT( _( "Unconditional rule applied." ) )
2175 testAssertion( c );
2176 }
2177 else
2178 {
2179 REPORT( wxString::Format( _( "Checking rule condition '%s'." ),
2180 EscapeHTML( c->condition->GetExpression() ) ) )
2181
2182 if( c->condition->EvaluateFor( a, nullptr, c->constraint.m_Type,
2183 a->GetLayer(), aReporter ) )
2184 {
2185 REPORT( _( "Rule applied." ) )
2186 testAssertion( c );
2187 }
2188 else
2189 {
2190 REPORT( _( "Condition not satisfied; rule ignored." ) )
2191 }
2192 }
2193 };
2194
2195 auto it = m_constraintMap.find( ASSERTION_CONSTRAINT );
2196
2197 if( it != m_constraintMap.end() )
2198 {
2199 for( int ii = 0; ii < (int) it->second->size(); ++ii )
2200 processConstraint( it->second->at( ii ) );
2201 }
2202}
2203
2204
2205#undef REPORT
2206
2207
2209{
2210 assert( error_code >= 0 && error_code <= DRCE_LAST );
2211 std::lock_guard<std::mutex> lock( m_errorLimitsMutex );
2212 return m_errorLimits[ error_code ] <= 0;
2213}
2214
2215
2216void DRC_ENGINE::ReportViolation( const std::shared_ptr<DRC_ITEM>& aItem, const VECTOR2I& aPos,
2217 int aMarkerLayer, const std::function<void( PCB_MARKER* )>& aPathGenerator )
2218{
2219 {
2220 std::lock_guard<std::mutex> lock( m_errorLimitsMutex );
2221 m_errorLimits[ aItem->GetErrorCode() ] -= 1;
2222 }
2223
2224 if( m_violationHandler )
2225 {
2226 static std::mutex handlerLock;
2227 std::lock_guard<std::mutex> guard( handlerLock );
2228 m_violationHandler( aItem, aPos, aMarkerLayer, aPathGenerator );
2229 }
2230
2231 if( m_logReporter )
2232 {
2233 wxString msg = wxString::Format( wxT( "Test '%s': %s (code %d)" ),
2234 aItem->GetViolatingTest()->GetName(),
2235 aItem->GetErrorMessage( false ),
2236 aItem->GetErrorCode() );
2237
2238 DRC_RULE* rule = aItem->GetViolatingRule();
2239
2240 if( rule )
2241 msg += wxString::Format( wxT( ", violating rule: '%s'" ), rule->m_Name );
2242
2243 m_logReporter->Report( msg );
2244
2245 wxString violatingItemsStr = wxT( "Violating items: " );
2246
2247 m_logReporter->Report( wxString::Format( wxT( " |- violating position (%d, %d)" ),
2248 aPos.x,
2249 aPos.y ) );
2250 }
2251}
2252
2253
2255{
2256 if( !m_progressReporter )
2257 return true;
2258
2259 return m_progressReporter->KeepRefreshing( aWait );
2260}
2261
2262
2264{
2265 if( m_progressReporter )
2266 m_progressReporter->AdvanceProgress();
2267}
2268
2269
2271{
2272 if( m_progressReporter )
2273 m_progressReporter->SetMaxProgress( aSize );
2274}
2275
2276
2277bool DRC_ENGINE::ReportProgress( double aProgress )
2278{
2279 if( !m_progressReporter )
2280 return true;
2281
2282 m_progressReporter->SetCurrentProgress( aProgress );
2283 return m_progressReporter->KeepRefreshing( false );
2284}
2285
2286
2287bool DRC_ENGINE::ReportPhase( const wxString& aMessage )
2288{
2289 if( !m_progressReporter )
2290 return true;
2291
2292 m_progressReporter->AdvancePhase( aMessage );
2293 return m_progressReporter->KeepRefreshing( false );
2294}
2295
2296
2298{
2299 return m_progressReporter && m_progressReporter->IsCancelled();
2300}
2301
2302
2304{
2305 auto it = m_constraintMap.find( constraintID );
2306 return it != m_constraintMap.end() && !it->second->empty();
2307}
2308
2309
2311 bool aUnconditionalOnly )
2312{
2313 int worst = 0;
2314 auto it = m_constraintMap.find( aConstraintId );
2315
2316 if( it != m_constraintMap.end() )
2317 {
2318 for( DRC_ENGINE_CONSTRAINT* c : *it->second )
2319 {
2320 if( aUnconditionalOnly && c->condition )
2321 continue;
2322
2323 int current = c->constraint.GetValue().Min();
2324
2325 if( current > worst )
2326 {
2327 worst = current;
2328 aConstraint = c->constraint;
2329 }
2330 }
2331 }
2332
2333 return worst > 0;
2334}
2335
2336
2338{
2340 {
2341 auto it = m_constraintMap.find( type );
2342
2343 if( it != m_constraintMap.end() )
2344 {
2345 for( DRC_ENGINE_CONSTRAINT* c : *it->second )
2346 {
2347 if( c->condition && c->parentRule && !c->parentRule->IsImplicit() )
2348 {
2349 return true;
2350 }
2351 }
2352 }
2353 }
2354
2355 return false;
2356}
2357
2358
2360{
2361 std::set<int> distinctMinimums;
2362 auto it = m_constraintMap.find( aConstraintId );
2363
2364 if( it != m_constraintMap.end() )
2365 {
2366 for( DRC_ENGINE_CONSTRAINT* c : *it->second )
2367 distinctMinimums.emplace( c->constraint.GetValue().Min() );
2368 }
2369
2370 return distinctMinimums;
2371}
2372
2373
2374// fixme: move two functions below to pcbcommon?
2375int DRC_ENGINE::MatchDpSuffix( const wxString& aNetName, wxString& aComplementNet,
2376 wxString& aBaseDpName )
2377{
2378 int rv = 0;
2379 int count = 0;
2380
2381 for( auto it = aNetName.rbegin(); it != aNetName.rend() && rv == 0; ++it, ++count )
2382 {
2383 int ch = *it;
2384
2385 if( ( ch >= '0' && ch <= '9' ) || ch == '_' )
2386 {
2387 continue;
2388 }
2389 else if( ch == '+' )
2390 {
2391 aComplementNet = wxT( "-" );
2392 rv = 1;
2393 }
2394 else if( ch == '-' )
2395 {
2396 aComplementNet = wxT( "+" );
2397 rv = -1;
2398 }
2399 else if( ch == 'N' )
2400 {
2401 aComplementNet = wxT( "P" );
2402 rv = -1;
2403 }
2404 else if ( ch == 'P' )
2405 {
2406 aComplementNet = wxT( "N" );
2407 rv = 1;
2408 }
2409 else
2410 {
2411 break;
2412 }
2413 }
2414
2415 if( rv != 0 && count >= 1 )
2416 {
2417 aBaseDpName = aNetName.Left( aNetName.Length() - count );
2418 aComplementNet = wxString( aBaseDpName ) << aComplementNet << aNetName.Right( count - 1 );
2419 }
2420
2421 return rv;
2422}
2423
2424
2425bool DRC_ENGINE::IsNetADiffPair( BOARD* aBoard, NETINFO_ITEM* aNet, int& aNetP, int& aNetN )
2426{
2427 wxString refName = aNet->GetNetname();
2428 wxString dummy, coupledNetName;
2429
2430 if( int polarity = MatchDpSuffix( refName, coupledNetName, dummy ) )
2431 {
2432 NETINFO_ITEM* net = aBoard->FindNet( coupledNetName );
2433
2434 if( !net )
2435 return false;
2436
2437 if( polarity > 0 )
2438 {
2439 aNetP = aNet->GetNetCode();
2440 aNetN = net->GetNetCode();
2441 }
2442 else
2443 {
2444 aNetP = net->GetNetCode();
2445 aNetN = aNet->GetNetCode();
2446 }
2447
2448 return true;
2449 }
2450
2451 return false;
2452}
2453
2454
2459bool DRC_ENGINE::IsNetTieExclusion( int aTrackNetCode, PCB_LAYER_ID aTrackLayer,
2460 const VECTOR2I& aCollisionPos, BOARD_ITEM* aCollidingItem )
2461{
2462 FOOTPRINT* parentFootprint = aCollidingItem->GetParentFootprint();
2463
2464 if( parentFootprint && parentFootprint->IsNetTie() )
2465 {
2467 std::map<wxString, int> padToNetTieGroupMap = parentFootprint->MapPadNumbersToNetTieGroups();
2468
2469 for( PAD* pad : parentFootprint->Pads() )
2470 {
2471 if( padToNetTieGroupMap[ pad->GetNumber() ] >= 0 && aTrackNetCode == pad->GetNetCode() )
2472 {
2473 if( pad->GetEffectiveShape( aTrackLayer )->Collide( aCollisionPos, epsilon ) )
2474 return true;
2475 }
2476 }
2477 }
2478
2479 return false;
2480}
2481
2482
2483namespace
2484{
2485enum class SHOWMATCH_DOMAIN
2486{
2487 ALL_ITEMS,
2488 COPPER_ITEMS,
2489 EDGE_ITEMS,
2490 FOOTPRINTS,
2491 HOLE_ITEMS,
2492 MASK_EXPANSION_ITEMS,
2493 MASK_ITEMS,
2494 PADS,
2495 PADS_AND_VIAS,
2496 PASTE_ITEMS,
2497 ROUTING_ITEMS,
2498 SILK_ITEMS,
2499 SILK_TARGET_ITEMS,
2500 TEXT_ITEMS,
2501 VIAS
2502};
2503
2504
2505struct SHOWMATCH_DOMAIN_SPEC
2506{
2507 SHOWMATCH_DOMAIN primary;
2508 SHOWMATCH_DOMAIN secondary = SHOWMATCH_DOMAIN::ALL_ITEMS;
2509 bool hasSecondary = false;
2510 bool secondaryUnary = false;
2511};
2512
2513
2514bool isShowMatchSkippable( const BOARD_ITEM* aItem )
2515{
2516 switch( aItem->Type() )
2517 {
2518 case PCB_NETINFO_T:
2519 case PCB_GENERATOR_T:
2520 case PCB_GROUP_T: return true;
2521
2522 default: return false;
2523 }
2524}
2525
2526
2527bool matchesShowMatchDomain( const BOARD_ITEM* aItem, SHOWMATCH_DOMAIN aDomain )
2528{
2529 if( !aItem || isShowMatchSkippable( aItem ) )
2530 return false;
2531
2532 switch( aDomain )
2533 {
2534 case SHOWMATCH_DOMAIN::ALL_ITEMS: return true;
2535
2536 case SHOWMATCH_DOMAIN::COPPER_ITEMS: return aItem->IsOnCopperLayer();
2537
2538 case SHOWMATCH_DOMAIN::EDGE_ITEMS:
2539 if( aItem->IsOnLayer( Edge_Cuts ) || aItem->IsOnLayer( Margin ) )
2540 return true;
2541
2542 if( aItem->Type() == PCB_PAD_T )
2543 {
2544 const PAD* pad = static_cast<const PAD*>( aItem );
2545 return pad->GetAttribute() == PAD_ATTRIB::NPTH && pad->HasHole();
2546 }
2547
2548 return false;
2549
2550 case SHOWMATCH_DOMAIN::FOOTPRINTS: return aItem->Type() == PCB_FOOTPRINT_T;
2551
2552 case SHOWMATCH_DOMAIN::HOLE_ITEMS: return aItem->HasHole();
2553
2554 case SHOWMATCH_DOMAIN::MASK_EXPANSION_ITEMS:
2555 switch( aItem->Type() )
2556 {
2557 case PCB_PAD_T:
2558 case PCB_TRACE_T:
2559 case PCB_ARC_T:
2560 case PCB_VIA_T:
2561 case PCB_SHAPE_T:
2562 case PCB_ZONE_T: return true;
2563
2564 default: return false;
2565 }
2566
2567 case SHOWMATCH_DOMAIN::MASK_ITEMS: return aItem->IsOnLayer( F_Mask ) || aItem->IsOnLayer( B_Mask );
2568
2569 case SHOWMATCH_DOMAIN::PADS: return aItem->Type() == PCB_PAD_T;
2570
2571 case SHOWMATCH_DOMAIN::PADS_AND_VIAS: return aItem->Type() == PCB_PAD_T || aItem->Type() == PCB_VIA_T;
2572
2573 case SHOWMATCH_DOMAIN::PASTE_ITEMS: return aItem->IsOnLayer( F_Paste ) || aItem->IsOnLayer( B_Paste );
2574
2575 case SHOWMATCH_DOMAIN::ROUTING_ITEMS:
2576 switch( aItem->Type() )
2577 {
2578 case PCB_TRACE_T:
2579 case PCB_ARC_T:
2580 case PCB_VIA_T:
2581 case PCB_PAD_T: return true;
2582
2583 default: return false;
2584 }
2585
2586 case SHOWMATCH_DOMAIN::SILK_ITEMS: return aItem->IsOnLayer( F_SilkS ) || aItem->IsOnLayer( B_SilkS );
2587
2588 case SHOWMATCH_DOMAIN::SILK_TARGET_ITEMS:
2589 return aItem->IsOnLayer( F_SilkS ) || aItem->IsOnLayer( B_SilkS ) || aItem->IsOnLayer( F_Mask )
2590 || aItem->IsOnLayer( B_Mask ) || aItem->IsOnLayer( F_Adhes ) || aItem->IsOnLayer( B_Adhes )
2591 || aItem->IsOnLayer( F_Paste ) || aItem->IsOnLayer( B_Paste ) || aItem->IsOnLayer( F_CrtYd )
2592 || aItem->IsOnLayer( B_CrtYd ) || aItem->IsOnLayer( F_Fab ) || aItem->IsOnLayer( B_Fab )
2593 || aItem->IsOnCopperLayer() || aItem->IsOnLayer( Edge_Cuts ) || aItem->IsOnLayer( Margin );
2594
2595 case SHOWMATCH_DOMAIN::TEXT_ITEMS:
2596 return aItem->Type() == PCB_FIELD_T || aItem->Type() == PCB_TEXT_T || aItem->Type() == PCB_TEXTBOX_T
2597 || aItem->Type() == PCB_TABLECELL_T || BaseType( aItem->Type() ) == PCB_DIMENSION_T;
2598
2599 case SHOWMATCH_DOMAIN::VIAS: return aItem->Type() == PCB_VIA_T;
2600 }
2601
2602 return false;
2603}
2604
2605
2606SHOWMATCH_DOMAIN_SPEC getShowMatchDomainSpec( DRC_CONSTRAINT_T aConstraint )
2607{
2608 switch( aConstraint )
2609 {
2610 case CLEARANCE_CONSTRAINT: return { SHOWMATCH_DOMAIN::COPPER_ITEMS };
2611
2612 case EDGE_CLEARANCE_CONSTRAINT: return { SHOWMATCH_DOMAIN::COPPER_ITEMS, SHOWMATCH_DOMAIN::EDGE_ITEMS, true, true };
2613
2614 case HOLE_CLEARANCE_CONSTRAINT: return { SHOWMATCH_DOMAIN::HOLE_ITEMS, SHOWMATCH_DOMAIN::ALL_ITEMS, true, false };
2615
2616 case HOLE_TO_HOLE_CONSTRAINT: return { SHOWMATCH_DOMAIN::HOLE_ITEMS };
2617
2618 case COURTYARD_CLEARANCE_CONSTRAINT: return { SHOWMATCH_DOMAIN::FOOTPRINTS };
2619
2622 case CREEPAGE_CONSTRAINT: return { SHOWMATCH_DOMAIN::ALL_ITEMS };
2623
2625 return { SHOWMATCH_DOMAIN::SILK_ITEMS, SHOWMATCH_DOMAIN::SILK_TARGET_ITEMS, true, false };
2626
2627 case SOLDER_MASK_SLIVER_CONSTRAINT: return { SHOWMATCH_DOMAIN::MASK_ITEMS };
2628
2635 case LENGTH_CONSTRAINT:
2636 case SKEW_CONSTRAINT: return { SHOWMATCH_DOMAIN::ROUTING_ITEMS };
2637
2639 case VIA_COUNT_CONSTRAINT: return { SHOWMATCH_DOMAIN::VIAS };
2640
2641 case HOLE_SIZE_CONSTRAINT: return { SHOWMATCH_DOMAIN::HOLE_ITEMS };
2642
2643 case ANNULAR_WIDTH_CONSTRAINT: return { SHOWMATCH_DOMAIN::PADS_AND_VIAS };
2644
2645 case MIN_RESOLVED_SPOKES_CONSTRAINT: return { SHOWMATCH_DOMAIN::PADS };
2646
2648 case TEXT_THICKNESS_CONSTRAINT: return { SHOWMATCH_DOMAIN::TEXT_ITEMS };
2649
2650 case SOLDER_MASK_EXPANSION_CONSTRAINT: return { SHOWMATCH_DOMAIN::MASK_EXPANSION_ITEMS };
2651
2653 case SOLDER_PASTE_REL_MARGIN_CONSTRAINT: return { SHOWMATCH_DOMAIN::PASTE_ITEMS };
2654
2657 default: return { SHOWMATCH_DOMAIN::ALL_ITEMS };
2658 }
2659}
2660
2661
2662std::vector<BOARD_ITEM*> collectShowMatchCandidates( BOARD* aBoard, SHOWMATCH_DOMAIN aDomain )
2663{
2664 std::vector<BOARD_ITEM*> items;
2665
2666 if( !aBoard )
2667 return items;
2668
2669 for( const auto& [kiid, item] : aBoard->GetItemByIdCache() )
2670 {
2671 if( matchesShowMatchDomain( item, aDomain ) )
2672 items.push_back( item );
2673 }
2674
2675 return items;
2676}
2677
2678
2679std::vector<PCB_LAYER_ID> getShowMatchLayers( const BOARD_ITEM* aItem )
2680{
2681 std::vector<PCB_LAYER_ID> layers;
2682
2683 switch( aItem->Type() )
2684 {
2685 case PCB_PAD_T: layers = static_cast<const PAD*>( aItem )->Padstack().UniqueLayers(); break;
2686
2687 case PCB_VIA_T: layers = static_cast<const PCB_VIA*>( aItem )->Padstack().UniqueLayers(); break;
2688
2689 default:
2690 for( PCB_LAYER_ID layer : aItem->GetLayerSet() )
2691 layers.push_back( layer );
2692
2693 break;
2694 }
2695
2696 if( layers.empty() )
2697 layers.push_back( UNDEFINED_LAYER );
2698
2699 return layers;
2700}
2701
2702
2703bool ruleMatchesUnary( const DRC_RULE& aRule, const BOARD_ITEM* aItem, DRC_CONSTRAINT_T aConstraint,
2704 REPORTER* aReporter )
2705{
2706 bool testedLayer = false;
2707
2708 for( PCB_LAYER_ID layer : getShowMatchLayers( aItem ) )
2709 {
2710 if( layer != UNDEFINED_LAYER && !aRule.m_LayerCondition.test( layer ) )
2711 continue;
2712
2713 testedLayer = true;
2714
2715 if( !aRule.m_Condition
2716 || aRule.m_Condition->EvaluateFor( aItem, nullptr, static_cast<int>( aConstraint ), layer, aReporter ) )
2717 {
2718 return true;
2719 }
2720 }
2721
2722 if( !testedLayer && aItem->GetLayerSet().none() )
2723 {
2724 return !aRule.m_Condition
2725 || aRule.m_Condition->EvaluateFor( aItem, nullptr, static_cast<int>( aConstraint ), UNDEFINED_LAYER,
2726 aReporter );
2727 }
2728
2729 return false;
2730}
2731
2732
2733std::vector<PCB_LAYER_ID> getShowMatchPairLayers( const DRC_RULE& aRule, const BOARD_ITEM* aItemA,
2734 const BOARD_ITEM* aItemB, DRC_CONSTRAINT_T aConstraint )
2735{
2736 std::vector<PCB_LAYER_ID> layers;
2737 std::set<int> seenLayers;
2738
2739 auto addLayer = [&]( PCB_LAYER_ID aLayer )
2740 {
2741 if( aLayer != UNDEFINED_LAYER && !aRule.m_LayerCondition.test( aLayer ) )
2742 return;
2743
2744 if( seenLayers.insert( static_cast<int>( aLayer ) ).second )
2745 layers.push_back( aLayer );
2746 };
2747
2748 switch( aConstraint )
2749 {
2751 for( PCB_LAYER_ID layer : getShowMatchLayers( aItemA ) )
2752 addLayer( layer );
2753
2754 break;
2755
2756 case COURTYARD_CLEARANCE_CONSTRAINT: addLayer( UNDEFINED_LAYER ); break;
2757
2758 default:
2759 for( PCB_LAYER_ID layer : getShowMatchLayers( aItemA ) )
2760 addLayer( layer );
2761
2762 for( PCB_LAYER_ID layer : getShowMatchLayers( aItemB ) )
2763 addLayer( layer );
2764
2765 break;
2766 }
2767
2768 if( layers.empty() )
2769 layers.push_back( UNDEFINED_LAYER );
2770
2771 return layers;
2772}
2773
2774
2775bool ruleMatchesPair( const DRC_RULE& aRule, const BOARD_ITEM* aItemA, const BOARD_ITEM* aItemB,
2776 DRC_CONSTRAINT_T aConstraint, REPORTER* aReporter )
2777{
2778 for( PCB_LAYER_ID layer : getShowMatchPairLayers( aRule, aItemA, aItemB, aConstraint ) )
2779 {
2780 if( !aRule.m_Condition
2781 || aRule.m_Condition->EvaluateFor( aItemA, aItemB, static_cast<int>( aConstraint ), layer, aReporter ) )
2782 {
2783 return true;
2784 }
2785 }
2786
2787 return false;
2788}
2789} // namespace
2790
2791
2793{
2794 for( DRC_TEST_PROVIDER* prov : m_testProviders )
2795 {
2796 if( name == prov->GetName() )
2797 return prov;
2798 }
2799
2800 return nullptr;
2801}
2802
2803
2804std::vector<BOARD_ITEM*> DRC_ENGINE::GetItemsMatchingCondition( const wxString& aExpression,
2805 DRC_CONSTRAINT_T aConstraint,
2806 REPORTER* aReporter )
2807{
2808 wxLogTrace( wxS( "KI_TRACE_DRC_RULE_EDITOR" ),
2809 wxS( "[ShowMatches] engine enter: expr='%s', constraint=%d" ), aExpression, (int) aConstraint );
2810 std::vector<BOARD_ITEM*> matches;
2811
2812 if( !m_board )
2813 return matches;
2814
2815 DRC_RULE_CONDITION condition( aExpression );
2816
2817 if( !condition.Compile( aReporter ? aReporter : m_logReporter ) )
2818 {
2819 wxLogTrace( wxS( "KI_TRACE_DRC_RULE_EDITOR" ), wxS( "[ShowMatches] engine: compile failed" ) );
2820 return matches;
2821 }
2822
2823 // Rebuild the from-to cache so that fromTo() expressions can be evaluated.
2824 // This cache requires explicit rebuilding before use since it depends on the full
2825 // connectivity graph being available.
2826 if( auto connectivity = m_board->GetConnectivity() )
2827 {
2828 if( auto ftCache = connectivity->GetFromToCache() )
2829 ftCache->Rebuild( m_board );
2830 }
2831
2832 BOARD_ITEM_SET items = m_board->GetItemSet();
2833 size_t totalItems = 0;
2834 size_t skippedItems = 0;
2835 size_t noLayerItems = 0;
2836 size_t checkedItems = 0;
2837
2838 for( auto& [kiid, item] : m_board->GetItemByIdCache() )
2839 {
2840 totalItems++;
2841
2842 // Skip items that don't have visible geometry or can't be meaningfully matched
2843 switch( item->Type() )
2844 {
2845 case PCB_NETINFO_T:
2846 case PCB_GENERATOR_T:
2847 case PCB_GROUP_T:
2848 skippedItems++;
2849 continue;
2850
2851 default:
2852 break;
2853 }
2854
2855 LSET itemLayers = item->GetLayerSet();
2856
2857 if( itemLayers.none() )
2858 {
2859 noLayerItems++;
2860 continue;
2861 }
2862
2863 checkedItems++;
2864 bool matched = false;
2865
2866 for( PCB_LAYER_ID layer : itemLayers )
2867 {
2868 if( condition.EvaluateFor( item, nullptr, static_cast<int>( aConstraint ), layer,
2869 aReporter ? aReporter : m_logReporter ) )
2870 {
2871 matches.push_back( item );
2872 wxLogTrace( wxS( "KI_TRACE_DRC_RULE_EDITOR" ),
2873 wxS( "[ShowMatches] engine: match type=%d kiid=%s layer=%d" ),
2874 (int) item->Type(), kiid.AsString(), (int) layer );
2875 matched = true;
2876 break; // No need to check other layers
2877 }
2878 }
2879
2880 // Log a few non-matching items to help debug condition issues
2881 if( !matched && matches.size() == 0 && checkedItems <= 5 )
2882 {
2883 wxLogTrace( wxS( "KI_TRACE_DRC_RULE_EDITOR" ),
2884 wxS( "[ShowMatches] engine: no-match sample type=%d kiid=%s layers=%s" ),
2885 (int) item->Type(), kiid.AsString(), itemLayers.FmtHex() );
2886 }
2887 }
2888
2889 wxLogTrace( wxS( "KI_TRACE_DRC_RULE_EDITOR" ),
2890 wxS( "[ShowMatches] engine stats: total=%zu skipped=%zu noLayer=%zu checked=%zu" ),
2891 totalItems, skippedItems, noLayerItems, checkedItems );
2892
2893 wxLogTrace( wxS( "KI_TRACE_DRC_RULE_EDITOR" ), wxS( "[ShowMatches] engine exit: total=%zu" ), matches.size() );
2894 return matches;
2895}
2896
2897
2898std::vector<BOARD_ITEM*> DRC_ENGINE::GetItemsMatchingRule( const std::shared_ptr<DRC_RULE>& aRule, REPORTER* aReporter )
2899{
2900 std::vector<BOARD_ITEM*> matches;
2901
2902 if( !m_board || !aRule )
2903 return matches;
2904
2905 const wxString condition = aRule->m_Condition ? aRule->m_Condition->GetExpression() : wxString();
2906 const bool requiresPairwise = condition.Contains( wxS( "B." ) );
2907 std::set<BOARD_ITEM*> matchedItems;
2908
2909 if( auto connectivity = m_board->GetConnectivity() )
2910 {
2911 if( auto ftCache = connectivity->GetFromToCache() )
2912 ftCache->Rebuild( m_board );
2913 }
2914
2915 for( const DRC_CONSTRAINT& constraint : aRule->m_Constraints )
2916 {
2917 if( constraint.m_Type == NULL_CONSTRAINT )
2918 continue;
2919
2920 SHOWMATCH_DOMAIN_SPEC domainSpec = getShowMatchDomainSpec( constraint.m_Type );
2921 std::vector<BOARD_ITEM*> primaryItems = collectShowMatchCandidates( m_board, domainSpec.primary );
2922 std::vector<BOARD_ITEM*> secondaryItems;
2923
2924 if( domainSpec.hasSecondary )
2925 secondaryItems = collectShowMatchCandidates( m_board, domainSpec.secondary );
2926
2927 if( requiresPairwise )
2928 {
2929 if( secondaryItems.empty() )
2930 {
2931 for( size_t ii = 0; ii < primaryItems.size(); ++ii )
2932 {
2933 BOARD_ITEM* itemA = primaryItems[ii];
2934
2935 for( size_t jj = ii + 1; jj < primaryItems.size(); ++jj )
2936 {
2937 BOARD_ITEM* itemB = primaryItems[jj];
2938
2939 if( ruleMatchesPair( *aRule, itemA, itemB, constraint.m_Type,
2940 aReporter ? aReporter : m_logReporter ) )
2941 {
2942 matchedItems.insert( itemA );
2943 matchedItems.insert( itemB );
2944 }
2945 }
2946 }
2947 }
2948 else
2949 {
2950 for( BOARD_ITEM* itemA : primaryItems )
2951 {
2952 for( BOARD_ITEM* itemB : secondaryItems )
2953 {
2954 if( itemA == itemB )
2955 continue;
2956
2957 if( ruleMatchesPair( *aRule, itemA, itemB, constraint.m_Type,
2958 aReporter ? aReporter : m_logReporter ) )
2959 {
2960 matchedItems.insert( itemA );
2961 matchedItems.insert( itemB );
2962 }
2963 }
2964 }
2965 }
2966 }
2967 else
2968 {
2969 for( BOARD_ITEM* item : primaryItems )
2970 {
2971 if( ruleMatchesUnary( *aRule, item, constraint.m_Type, aReporter ? aReporter : m_logReporter ) )
2972 {
2973 matchedItems.insert( item );
2974 }
2975 }
2976
2977 if( domainSpec.hasSecondary && domainSpec.secondaryUnary )
2978 {
2979 for( BOARD_ITEM* item : secondaryItems )
2980 {
2981 if( ruleMatchesUnary( *aRule, item, constraint.m_Type, aReporter ? aReporter : m_logReporter ) )
2982 {
2983 matchedItems.insert( item );
2984 }
2985 }
2986 }
2987 }
2988 }
2989
2990 matches.assign( matchedItems.begin(), matchedItems.end() );
2991
2992 return matches;
2993}
2994
2995
2997 wxString* aSource )
2998{
2999 DRC_OWN_CLEARANCE_CACHE_KEY key{ aItem->m_Uuid, aLayer };
3000
3001 // Fast path: check cache with shared (read) lock
3002 {
3003 std::shared_lock<std::shared_mutex> readLock( m_clearanceCacheMutex );
3004
3005 auto it = m_ownClearanceCache.find( key );
3006
3007 if( it != m_ownClearanceCache.end() )
3008 {
3009 // Cache hit. We don't cache the source string since it's rarely requested
3010 // and caching it would add complexity.
3011 return it->second;
3012 }
3013 }
3014
3015 // Cache miss - evaluate the constraint (outside lock to avoid blocking other threads)
3016 DRC_CONSTRAINT_T constraintType = CLEARANCE_CONSTRAINT;
3017
3018 if( aItem->Type() == PCB_PAD_T )
3019 {
3020 const PAD* pad = static_cast<const PAD*>( aItem );
3021
3022 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
3023 constraintType = HOLE_CLEARANCE_CONSTRAINT;
3024 }
3025
3026 DRC_CONSTRAINT constraint = EvalRules( constraintType, aItem, nullptr, aLayer );
3027
3028 int clearance = 0;
3029
3030 if( constraint.Value().HasMin() )
3031 {
3032 clearance = constraint.Value().Min();
3033
3034 if( aSource )
3035 *aSource = constraint.GetName();
3036 }
3037
3038 // Store in cache with exclusive (write) lock using double-checked locking
3039 {
3040 std::unique_lock<std::shared_mutex> writeLock( m_clearanceCacheMutex );
3041
3042 auto it = m_ownClearanceCache.find( key );
3043
3044 if( it == m_ownClearanceCache.end() )
3046 }
3047
3048 return clearance;
3049}
3050
3051
3053{
3054 std::unique_lock<std::shared_mutex> writeLock( m_clearanceCacheMutex );
3055
3056 if( m_board )
3057 {
3058 LSET copperLayers = m_board->GetEnabledLayers() & LSET::AllCuMask();
3059
3060 for( PCB_LAYER_ID layer : copperLayers.Seq() )
3061 m_ownClearanceCache.erase( DRC_OWN_CLEARANCE_CACHE_KEY{ aUuid, layer } );
3062 }
3063 else
3064 {
3065 auto it = m_ownClearanceCache.begin();
3066
3067 while( it != m_ownClearanceCache.end() )
3068 {
3069 if( it->first.m_uuid == aUuid )
3070 it = m_ownClearanceCache.erase( it );
3071 else
3072 ++it;
3073 }
3074 }
3075}
3076
3077
3079{
3080 std::unique_lock<std::shared_mutex> writeLock( m_clearanceCacheMutex );
3081 m_ownClearanceCache.clear();
3082}
3083
3084
3086{
3087 if( !m_board )
3088 return;
3089
3090 // Pre-populate the cache for all connected items to avoid delays during first render.
3091 // We only need to cache copper layers since clearance outlines are only drawn on copper.
3092
3093 LSET copperLayers = m_board->GetEnabledLayers() & LSET::AllCuMask();
3094
3095 using CLEARANCE_MAP = std::unordered_map<DRC_OWN_CLEARANCE_CACHE_KEY, int>;
3096
3097 // Build flat list of (item, layer) pairs to process.
3098 // Estimate size based on tracks + pads * average layers (2 for typical TH pads).
3099 std::vector<std::pair<const BOARD_ITEM*, PCB_LAYER_ID>> itemsToProcess;
3100 size_t estimatedPads = 0;
3101
3102 for( FOOTPRINT* footprint : m_board->Footprints() )
3103 estimatedPads += footprint->Pads().size();
3104
3105 itemsToProcess.reserve( m_board->Tracks().size() + estimatedPads * 2 );
3106
3107 for( PCB_TRACK* track : m_board->Tracks() )
3108 {
3109 if( track->Type() == PCB_VIA_T )
3110 {
3111 for( PCB_LAYER_ID layer : LSET( track->GetLayerSet() & copperLayers ).Seq() )
3112 itemsToProcess.emplace_back( track, layer );
3113 }
3114 else
3115 {
3116 itemsToProcess.emplace_back( track, track->GetLayer() );
3117 }
3118 }
3119
3120 for( FOOTPRINT* footprint : m_board->Footprints() )
3121 {
3122 for( PAD* pad : footprint->Pads() )
3123 {
3124 for( PCB_LAYER_ID layer : LSET( pad->GetLayerSet() & copperLayers ).Seq() )
3125 itemsToProcess.emplace_back( pad, layer );
3126 }
3127 }
3128
3129 if( itemsToProcess.empty() )
3130 return;
3131
3132 {
3133 std::unique_lock<std::shared_mutex> writeLock( m_clearanceCacheMutex );
3134 m_ownClearanceCache.reserve( itemsToProcess.size() );
3135 }
3136
3138
3139 auto processItems = [this]( size_t aStart, size_t aEnd,
3140 const std::vector<std::pair<const BOARD_ITEM*, PCB_LAYER_ID>>& aItems )
3141 -> CLEARANCE_MAP
3142 {
3143 CLEARANCE_MAP localCache;
3144
3145 for( size_t i = aStart; i < aEnd; ++i )
3146 {
3147 const BOARD_ITEM* item = aItems[i].first;
3148 PCB_LAYER_ID layer = aItems[i].second;
3149
3150 DRC_CONSTRAINT_T constraintType = CLEARANCE_CONSTRAINT;
3151
3152 if( item->Type() == PCB_PAD_T )
3153 {
3154 const PAD* pad = static_cast<const PAD*>( item );
3155
3156 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
3157 constraintType = HOLE_CLEARANCE_CONSTRAINT;
3158 }
3159
3160 DRC_CONSTRAINT constraint = EvalRules( constraintType, item, nullptr, layer );
3161
3162 int clearance = 0;
3163
3164 if( constraint.Value().HasMin() )
3165 clearance = constraint.Value().Min();
3166
3167 localCache[{ item->m_Uuid, layer }] = clearance;
3168 }
3169
3170 return localCache;
3171 };
3172
3173 auto results = tp.submit_blocks( 0, itemsToProcess.size(),
3174 [&]( size_t aStart, size_t aEnd ) -> CLEARANCE_MAP
3175 {
3176 return processItems( aStart, aEnd, itemsToProcess );
3177 } );
3178
3179 // Collect all results first WITHOUT holding the lock to avoid deadlock.
3180 // Worker threads call EvalRules() which needs a read lock on m_clearanceCacheMutex.
3181 // If we held a write lock while calling .get(), workers would block on the read lock
3182 // while we block waiting for them to complete.
3183 std::vector<CLEARANCE_MAP> collectedResults;
3184 collectedResults.reserve( results.size() );
3185
3186 for( size_t i = 0; i < results.size(); ++i )
3187 {
3188 if( results[i].valid() )
3189 collectedResults.push_back( results[i].get() );
3190 }
3191
3192 // Now merge with write lock held, but no blocking on futures
3193 {
3194 std::unique_lock<std::shared_mutex> writeLock( m_clearanceCacheMutex );
3195
3196 for( const auto& localCache : collectedResults )
3197 m_ownClearanceCache.insert( localCache.begin(), localCache.end() );
3198 }
3199}
const char * name
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:125
std::set< BOARD_ITEM *, CompareByUuid > BOARD_ITEM_SET
Set of BOARD_ITEMs ordered by UUID.
Definition board.h:307
#define MAXIMUM_CLEARANCE
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
std::string FmtHex() const
Return a hex string showing contents of this set.
Definition base_set.h:302
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
virtual std::optional< int > GetClearanceOverrides(wxString *aSource) const
Return any clearance overrides set in the "classic" (ie: pre-rule) system.
virtual NETCLASS * GetEffectiveNetClass() const
Return the NETCLASS for this item.
virtual std::optional< int > GetLocalClearance() const
Return any local clearances set in the "classic" (ie: pre-rule) system.
Container for design settings for a BOARD object.
std::shared_ptr< NET_SETTINGS > m_NetSettings
std::map< int, SEVERITY > m_DRCSeverities
int GetDRCEpsilon() const
Return an epsilon which accounts for rounding errors, etc.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:268
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition board_item.h:158
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition board_item.h:350
FOOTPRINT * GetParentFootprint() const
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:288
virtual bool HasDrilledHole() const
Definition board_item.h:185
virtual bool IsOnCopperLayer() const
Definition board_item.h:175
virtual bool HasHole() const
Definition board_item.h:180
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:2606
const std::unordered_map< KIID, BOARD_ITEM * > & GetItemByIdCache() const
Definition board.h:1454
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:311
Represents a single line in a time domain profile track propagation setup.
int GetDiffPairGap() const
int GetWidth() const
PCB_LAYER_ID GetSignalLayer() const
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
wxString GetName() const
Definition drc_rule.h:208
int m_DisallowFlags
Definition drc_rule.h:245
void SetParentRule(DRC_RULE *aParentRule)
Definition drc_rule.h:203
MINOPTMAX< int > & Value()
Definition drc_rule.h:201
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:200
ZONE_CONNECTION m_ZoneConnection
Definition drc_rule.h:246
void SetName(const wxString &aName)
Definition drc_rule.h:206
MINOPTMAX< int > m_Value
Definition drc_rule.h:244
DRC_CONSTRAINT_T m_Type
Definition drc_rule.h:243
void SetOptionsFromOther(const DRC_CONSTRAINT &aOther)
Definition drc_rule.h:238
DRC_RULE * GetParentRule() const
Definition drc_rule.h:204
bool m_ImplicitMin
Definition drc_rule.h:248
std::map< DRC_CONSTRAINT_T, std::vector< DRC_ENGINE_CONSTRAINT * > * > m_constraintMap
Definition drc_engine.h:380
DRC_CLEARANCE_BATCH EvalClearanceBatch(const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer)
Evaluate all clearance-related constraints in a single batch call.
void AdvanceProgress()
std::shared_ptr< DRC_RULE > createImplicitRule(const wxString &name, DRC_IMPLICIT_SOURCE aImplicitSource)
void RunTests(EDA_UNITS aUnits, bool aReportAllTrackErrors, bool aTestFootprints, BOARD_COMMIT *aCommit=nullptr)
Run the DRC tests.
std::unordered_map< DRC_OWN_CLEARANCE_CACHE_KEY, int > m_ownClearanceCache
Definition drc_engine.h:391
bool m_hasDiffPairClearanceOverrides
Definition drc_engine.h:403
bool m_testFootprints
Definition drc_engine.h:377
void addRule(std::shared_ptr< DRC_RULE > &rule)
Definition drc_engine.h:334
PROGRESS_REPORTER * m_progressReporter
Definition drc_engine.h:384
void ClearClearanceCache()
Clear the entire clearance cache.
void loadRules(const wxFileName &aPath)
Load and parse a rule set from an sexpr text file.
std::vector< DRC_TEST_PROVIDER * > m_testProviders
Definition drc_engine.h:372
REPORTER * m_logReporter
Definition drc_engine.h:383
std::map< DRC_CONSTRAINT_T, std::vector< DRC_ENGINE_CONSTRAINT * > > m_explicitConstraints
Definition drc_engine.h:404
void compileRules()
std::set< int > QueryDistinctConstraints(DRC_CONSTRAINT_T aConstraintId)
bool m_hasGeometryDependentRules
Definition drc_engine.h:402
DS_PROXY_VIEW_ITEM * m_drawingSheet
Definition drc_engine.h:367
NETLIST * m_schematicNetlist
Definition drc_engine.h:368
std::shared_mutex m_clearanceCacheMutex
Definition drc_engine.h:400
bool KeepRefreshing(bool aWait=false)
std::vector< BOARD_ITEM * > GetItemsMatchingRule(const std::shared_ptr< DRC_RULE > &aRule, REPORTER *aReporter=nullptr)
BOARD * m_board
Definition drc_engine.h:366
int GetCachedOwnClearance(const BOARD_ITEM *aItem, PCB_LAYER_ID aLayer, wxString *aSource=nullptr)
Get the cached own clearance for an item on a specific layer.
bool m_reportAllTrackErrors
Definition drc_engine.h:376
bool ReportProgress(double aProgress)
DRC_TEST_PROVIDER * GetTestProvider(const wxString &name) const
bool HasRulesForConstraintType(DRC_CONSTRAINT_T constraintID)
BOARD_DESIGN_SETTINGS * GetDesignSettings() const
Definition drc_engine.h:150
void SetMaxProgress(int aSize)
DRC_ENGINE(BOARD *aBoard=nullptr, BOARD_DESIGN_SETTINGS *aSettings=nullptr)
bool m_hasExplicitClearanceRules
Definition drc_engine.h:401
std::vector< int > m_errorLimits
Definition drc_engine.h:374
bool IsErrorLimitExceeded(int error_code)
void ProcessAssertions(const BOARD_ITEM *a, std::function< void(const DRC_CONSTRAINT *)> aFailureHandler, REPORTER *aReporter=nullptr)
void loadImplicitRules()
DRC_VIOLATION_HANDLER m_violationHandler
Definition drc_engine.h:382
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
std::vector< std::shared_ptr< DRC_RULE > > m_rules
Definition drc_engine.h:370
bool QueryWorstConstraint(DRC_CONSTRAINT_T aRuleId, DRC_CONSTRAINT &aConstraint, bool aUnconditionalOnly=false)
bool IsCancelled() const
std::unordered_map< wxString, int > m_netclassClearances
Definition drc_engine.h:396
static bool IsNetADiffPair(BOARD *aBoard, NETINFO_ITEM *aNet, int &aNetP, int &aNetN)
bool IsNetTieExclusion(int aTrackNetCode, PCB_LAYER_ID aTrackLayer, const VECTOR2I &aCollisionPos, BOARD_ITEM *aCollidingItem)
Check if the given collision between a track and another item occurs during the track's entry into a ...
virtual ~DRC_ENGINE()
std::vector< BOARD_ITEM * > GetItemsMatchingCondition(const wxString &aExpression, DRC_CONSTRAINT_T aConstraint=ASSERTION_CONSTRAINT, REPORTER *aReporter=nullptr)
Evaluate a DRC condition against all board items and return matches.
void InitEngine(const wxFileName &aRulePath)
Initialize the DRC engine.
DRC_CONSTRAINT EvalZoneConnection(const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
static int MatchDpSuffix(const wxString &aNetName, wxString &aComplementNet, wxString &aBaseDpName)
Check if the given net is a diff pair, returning its polarity and complement if so.
bool ReportPhase(const wxString &aMessage)
void InitializeClearanceCache()
Initialize the clearance cache for all items on the board.
bool m_rulesValid
Definition drc_engine.h:371
void InvalidateClearanceCache(const KIID &aUuid)
Invalidate the clearance cache for a specific item.
BOARD_DESIGN_SETTINGS * m_designSettings
Definition drc_engine.h:365
bool HasUserDefinedPhysicalConstraint()
void ReportViolation(const std::shared_ptr< DRC_ITEM > &aItem, const VECTOR2I &aPos, int aMarkerLayer, const std::function< void(PCB_MARKER *)> &aPathGenerator={})
std::mutex m_errorLimitsMutex
Definition drc_engine.h:375
void SetViolatingRule(DRC_RULE *aRule)
Definition drc_item.h:163
void Parse(std::vector< std::shared_ptr< DRC_RULE > > &aRules, REPORTER *aReporter)
bool Compile(REPORTER *aReporter, int aSourceLine=0, int aSourceOffset=0)
bool EvaluateFor(const BOARD_ITEM *aItemA, const BOARD_ITEM *aItemB, int aConstraint, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
DRC_RULE_CONDITION * m_Condition
Definition drc_rule.h:160
bool IsImplicit() const
Definition drc_rule.h:147
LSET m_LayerCondition
Definition drc_rule.h:159
wxString m_Name
Definition drc_rule.h:157
std::vector< DRC_TEST_PROVIDER * > GetShowMatchesProviders() const
static DRC_SHOWMATCHES_PROVIDER_REGISTRY & Instance()
std::vector< DRC_TEST_PROVIDER * > GetTestProviders() const
static DRC_TEST_PROVIDER_REGISTRY & Instance()
Represent a DRC "provider" which runs some DRC functions over a BOARD and spits out DRC_ITEM and posi...
void SetDRCEngine(DRC_ENGINE *engine)
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:120
virtual wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const
Return a user-visible description string of this item.
Definition eda_item.cpp:154
const KIID m_Uuid
Definition eda_item.h:528
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:112
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:152
A LINE_READER that reads from an open file.
Definition richio.h:158
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
Definition richio.cpp:212
ZONE_CONNECTION GetLocalZoneConnection() const
Definition footprint.h:477
std::map< wxString, int > MapPadNumbersToNetTieGroups() const
std::deque< PAD * > & Pads()
Definition footprint.h:377
const std::set< int > & GetNetTieCache(const BOARD_ITEM *aItem) const
Get the set of net codes that are allowed to connect to a footprint item.
Definition footprint.h:724
bool IsNetTie() const
Definition footprint.h:508
wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const override
Return a user-visible description string of this item.
const SHAPE_POLY_SET & GetCourtyard(PCB_LAYER_ID aLayer) const
Used in DRC to test the courtyard area (a complex polygon).
Definition kiid.h:48
wxString AsString() const
Definition kiid.cpp:244
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 & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:608
static const LSET & BackMask()
Return a mask holding all technical layers and the external CU layer on back side.
Definition lset.cpp:729
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition lset.cpp:313
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition lset.cpp:188
T Min() const
Definition minoptmax.h:33
void SetMin(T v)
Definition minoptmax.h:41
void SetOpt(T v)
Definition minoptmax.h:43
bool HasMin() const
Definition minoptmax.h:37
void SetMax(T v)
Definition minoptmax.h:42
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:42
const wxString GetName() const
Gets the name of this (maybe aggregate) netclass in a format for internal usage or for export to exte...
Definition netclass.cpp:328
Handle the data for a net.
Definition netinfo.h:50
const wxString & GetNetname() const
Definition netinfo.h:103
int GetNetCode() const
Definition netinfo.h:97
const std::map< wxString, std::shared_ptr< NETCLASS > > & GetCompositeNetclasses() const
Gets all composite (multiple assignment / missing defaults) netclasses.
const std::map< wxString, std::shared_ptr< NETCLASS > > & GetNetclasses() const
Gets all netclasses.
std::shared_ptr< NETCLASS > GetDefaultNetclass()
Gets the default netclass for the project.
Definition pad.h:55
std::optional< int > GetLocalSolderMaskMargin() const
Definition pcb_shape.h:216
A small class to help profiling.
Definition profile.h:50
void Stop()
Save the time when this function was called, and set the counter stane to stop.
Definition profile.h:90
double msecs(bool aSinceLast=false)
Definition profile.h:151
Container for project specific data.
Definition project.h:66
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:75
virtual bool HasMessageOfSeverity(int aSeverityMask) const
Returns true if the reporter has one or more messages matching the specified severity mask.
Definition reporter.h:145
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
const EDA_IU_SCALE & GetIuScale() const
wxString MessageTextFromUnscaledValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
UNITS_PROVIDER(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits)
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
A lower-precision version of StringFromValue().
void SetUserUnits(EDA_UNITS aUnits)
Handle a list of polygons defining a copper zone.
Definition zone.h:74
wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const override
Return a user-visible description string of this item.
Definition zone.cpp:1300
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:802
bool GetDoNotAllowVias() const
Definition zone.h:813
bool GetDoNotAllowPads() const
Definition zone.h:815
bool GetDoNotAllowTracks() const
Definition zone.h:814
const wxString & GetZoneName() const
Definition zone.h:164
int GetMinThickness() const
Definition zone.h:319
ZONE_CONNECTION GetPadConnection() const
Definition zone.h:316
int GetThermalReliefSpokeWidth() const
Definition zone.h:263
bool GetDoNotAllowFootprints() const
Definition zone.h:816
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:137
bool HasKeepoutParametersSet() const
Accessor to determine if any keepout parameters are set.
Definition zone.h:793
bool GetDoNotAllowZoneFills() const
Definition zone.h:812
int GetThermalReliefGap() const
Definition zone.h:252
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
Definition common.cpp:63
wxString DescribeRef(const wxString &aRef)
Returns a user-visible HTML string describing a footprint reference designator.
Definition common.cpp:468
The common library.
void drcPrintDebugMessage(int level, const wxString &msg, const char *function, int line)
#define EXTENDED_ERROR_LIMIT
static bool isKeepoutZone(const BOARD_ITEM *aItem, bool aCheckFlags)
#define ERROR_LIMIT
@ DRCE_TUNING_PROFILE_IMPLICIT_RULES
Definition drc_item.h:116
@ DRCE_UNCONNECTED_ITEMS
Definition drc_item.h:40
@ DRCE_CLEARANCE
Definition drc_item.h:44
@ DRCE_FIRST
Definition drc_item.h:39
@ DRCE_LAST
Definition drc_item.h:124
DRC_IMPLICIT_SOURCE
Definition drc_rule.h:114
@ DRC_DISALLOW_PADS
Definition drc_rule.h:104
@ DRC_DISALLOW_BURIED_VIAS
Definition drc_rule.h:102
@ DRC_DISALLOW_BLIND_VIAS
Definition drc_rule.h:101
@ DRC_DISALLOW_TEXTS
Definition drc_rule.h:106
@ DRC_DISALLOW_ZONES
Definition drc_rule.h:105
@ DRC_DISALLOW_HOLES
Definition drc_rule.h:108
@ DRC_DISALLOW_GRAPHICS
Definition drc_rule.h:107
@ DRC_DISALLOW_THROUGH_VIAS
Definition drc_rule.h:99
@ DRC_DISALLOW_FOOTPRINTS
Definition drc_rule.h:109
@ DRC_DISALLOW_TRACKS
Definition drc_rule.h:103
@ DRC_DISALLOW_MICRO_VIAS
Definition drc_rule.h:100
DRC_CONSTRAINT_T
Definition drc_rule.h:53
@ ANNULAR_WIDTH_CONSTRAINT
Definition drc_rule.h:67
@ COURTYARD_CLEARANCE_CONSTRAINT
Definition drc_rule.h:61
@ VIA_DIAMETER_CONSTRAINT
Definition drc_rule.h:76
@ ZONE_CONNECTION_CONSTRAINT
Definition drc_rule.h:68
@ DIFF_PAIR_GAP_CONSTRAINT
Definition drc_rule.h:82
@ NET_CHAIN_LENGTH_CONSTRAINT
Definition drc_rule.h:78
@ SOLDER_MASK_SLIVER_CONSTRAINT
Definition drc_rule.h:93
@ NET_CHAIN_STUB_LENGTH_CONSTRAINT
Definition drc_rule.h:79
@ DISALLOW_CONSTRAINT
Definition drc_rule.h:75
@ TRACK_WIDTH_CONSTRAINT
Definition drc_rule.h:65
@ SILK_CLEARANCE_CONSTRAINT
Definition drc_rule.h:62
@ EDGE_CLEARANCE_CONSTRAINT
Definition drc_rule.h:59
@ MIN_RESOLVED_SPOKES_CONSTRAINT
Definition drc_rule.h:71
@ TRACK_SEGMENT_LENGTH_CONSTRAINT
Definition drc_rule.h:66
@ TEXT_THICKNESS_CONSTRAINT
Definition drc_rule.h:64
@ LENGTH_CONSTRAINT
Definition drc_rule.h:77
@ VIA_COUNT_CONSTRAINT
Definition drc_rule.h:85
@ NET_CHAIN_RETURN_PATH_CONSTRAINT
Definition drc_rule.h:80
@ PHYSICAL_HOLE_CLEARANCE_CONSTRAINT
Definition drc_rule.h:87
@ CLEARANCE_CONSTRAINT
Definition drc_rule.h:55
@ NULL_CONSTRAINT
Definition drc_rule.h:54
@ THERMAL_SPOKE_WIDTH_CONSTRAINT
Definition drc_rule.h:70
@ CONNECTION_WIDTH_CONSTRAINT
Definition drc_rule.h:89
@ THERMAL_RELIEF_GAP_CONSTRAINT
Definition drc_rule.h:69
@ MAX_UNCOUPLED_CONSTRAINT
Definition drc_rule.h:83
@ ASSERTION_CONSTRAINT
Definition drc_rule.h:88
@ SKEW_CONSTRAINT
Definition drc_rule.h:81
@ HOLE_CLEARANCE_CONSTRAINT
Definition drc_rule.h:57
@ SOLDER_PASTE_ABS_MARGIN_CONSTRAINT
Definition drc_rule.h:73
@ SOLDER_MASK_EXPANSION_CONSTRAINT
Definition drc_rule.h:72
@ TRACK_ANGLE_CONSTRAINT
Definition drc_rule.h:90
@ HOLE_SIZE_CONSTRAINT
Definition drc_rule.h:60
@ TEXT_HEIGHT_CONSTRAINT
Definition drc_rule.h:63
@ CREEPAGE_CONSTRAINT
Definition drc_rule.h:56
@ PHYSICAL_CLEARANCE_CONSTRAINT
Definition drc_rule.h:86
@ SOLDER_PASTE_REL_MARGIN_CONSTRAINT
Definition drc_rule.h:74
@ HOLE_TO_HOLE_CONSTRAINT
Definition drc_rule.h:58
constexpr int DRC_DISALLOW_VIAS
Definition drc_rule.h:124
#define _(s)
#define HOLE_PROXY
Indicates the BOARD_ITEM is a proxy for its hole.
EDA_UNITS
Definition eda_units.h:48
static FILENAME_RESOLVER * resolver
static const wxChar * traceDrcProfile
Flag to enable DRC profile timing logging.
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
KIID niluuid(0)
bool IsPcbLayer(int aLayer)
Test whether a layer is a valid layer for Pcbnew.
Definition layer_ids.h:668
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
@ 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
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ B_SilkS
Definition layer_ids.h:101
@ B_Fab
Definition layer_ids.h:118
#define REPORT(msg)
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:103
@ PTH
Plated through hole pad.
Definition padstack.h:98
std::deque< PAD * > PADS
std::deque< FOOTPRINT * > FOOTPRINTS
@ RPT_SEVERITY_ERROR
const double epsilon
std::vector< FAB_LAYER_COLOR > dummy
wxString EscapeHTML(const wxString &aString)
Return a new wxString escaped for embedding in HTML.
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Batch result for clearance-related constraints to reduce per-query overhead during PNS routing.
Definition drc_engine.h:113
std::shared_ptr< DRC_RULE > parentRule
Definition drc_engine.h:352
DRC_RULE_CONDITION * condition
Definition drc_engine.h:351
Cache key for own clearance lookups, combining item UUID and layer.
Definition drc_engine.h:43
A filename or source description, a problem input line, a line number, a byte offset,...
Represents a single line in the tuning profile configuration grid.
PROFILE_TYPE m_Type
std::vector< DELAY_PROFILE_TRACK_PROPAGATION_ENTRY > m_TrackPropagationEntries
static const long long MM
int clearance
wxString result
Test unit parsing edge cases and error handling.
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
static thread_pool * tp
BS::priority_thread_pool thread_pool
Definition thread_pool.h:31
constexpr KICAD_T BaseType(const KICAD_T aType)
Return the underlying type of the given type.
Definition typeinfo.h:260
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:85
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition typeinfo.h:88
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:94
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:108
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:90
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:105
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:89
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:87
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:98
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition typeinfo.h:92
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:83
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:84
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:95
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition typeinfo.h:97
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:91
@ PCB_NETINFO_T
class NETINFO_ITEM, a description of a net
Definition typeinfo.h:107
@ PCB_LOCATE_HOLE_T
Definition typeinfo.h:128
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:93
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687
wxString PrintZoneConnection(ZONE_CONNECTION aConnection)
Definition zones.h:56
ZONE_CONNECTION
How pads are covered by copper in zone.
Definition zones.h:47
@ THERMAL
Use thermal relief for pads.
Definition zones.h:50
@ THT_THERMAL
Thermal relief only for THT pads.
Definition zones.h:52
@ FULL
pads are covered by copper
Definition zones.h:51