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