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