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