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