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
744 m_explicitConstraints.clear();
745
746 for( auto& [constraintType, ruleList] : m_constraintMap )
747 {
748 for( DRC_ENGINE_CONSTRAINT* c : *ruleList )
749 {
750 if( c->parentRule && !c->parentRule->IsImplicit() )
751 {
752 m_explicitConstraints[constraintType].push_back( c );
753
754 if( constraintType == CLEARANCE_CONSTRAINT )
756
758 && c->condition
759 && c->condition->HasGeometryDependentFunctions() )
760 {
762 }
763 }
764 }
765 }
766}
767
768
769void DRC_ENGINE::InitEngine( const std::shared_ptr<DRC_RULE>& rule )
770{
772
773 for( DRC_TEST_PROVIDER* provider : m_testProviders )
774 {
775 if( m_logReporter )
776 m_logReporter->Report( wxString::Format( wxT( "Create DRC provider: '%s'" ), provider->GetName() ) );
777
778 provider->SetDRCEngine( this );
779 }
780
781 m_rules.clear();
782 m_rulesValid = false;
783
784 for( std::pair<DRC_CONSTRAINT_T, std::vector<DRC_ENGINE_CONSTRAINT*>*> pair : m_constraintMap )
785 {
786 for( DRC_ENGINE_CONSTRAINT* constraint : *pair.second )
787 delete constraint;
788
789 delete pair.second;
790 }
791
792 m_constraintMap.clear();
793
794 m_board->IncrementTimeStamp(); // Clear board-level caches
795
796 try
797 {
798 m_rules.push_back( rule );
799 compileRules();
800 }
801 catch( PARSE_ERROR& original_parse_error )
802 {
803 throw original_parse_error;
804 }
805
806 for( int ii = DRCE_FIRST; ii < DRCE_LAST; ++ii )
808
809 m_rulesValid = true;
810}
811
812
813void DRC_ENGINE::InitEngine( const wxFileName& aRulePath )
814{
816
817 for( DRC_TEST_PROVIDER* provider : m_testProviders )
818 {
819 if( m_logReporter )
820 m_logReporter->Report( wxString::Format( wxT( "Create DRC provider: '%s'" ), provider->GetName() ) );
821
822 provider->SetDRCEngine( this );
823 }
824
825 m_rules.clear();
826 m_rulesValid = false;
827
828 for( std::pair<DRC_CONSTRAINT_T, std::vector<DRC_ENGINE_CONSTRAINT*>*> pair : m_constraintMap )
829 {
830 for( DRC_ENGINE_CONSTRAINT* constraint : *pair.second )
831 delete constraint;
832
833 delete pair.second;
834 }
835
836 m_constraintMap.clear();
837
838 {
839 std::unique_lock<std::shared_mutex> writeLock( m_clearanceCacheMutex );
840 m_ownClearanceCache.clear();
841 m_netclassClearances.clear();
842 }
843
846 m_explicitConstraints.clear();
847
848 m_board->IncrementTimeStamp(); // Clear board-level caches
849
850 try // attempt to load full set of rules (implicit + user rules)
851 {
853 loadRules( aRulePath );
854 compileRules();
855 }
856 catch( PARSE_ERROR& original_parse_error )
857 {
858 m_rules.clear();
859
860 try // try again with just our implicit rules
861 {
863 compileRules();
864 }
865 catch( PARSE_ERROR& )
866 {
867 wxFAIL_MSG( wxT( "Compiling implicit rules failed." ) );
868 }
869
870 throw original_parse_error;
871 }
872
873 for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
875
876 m_rulesValid = true;
877}
878
879
880void DRC_ENGINE::RunTests( EDA_UNITS aUnits, bool aReportAllTrackErrors, bool aTestFootprints,
881 BOARD_COMMIT* aCommit )
882{
883 PROF_TIMER timer;
884
885 SetUserUnits( aUnits );
886
887 m_reportAllTrackErrors = aReportAllTrackErrors;
888 m_testFootprints = aTestFootprints;
889
890 for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
891 {
892 if( m_designSettings->Ignore( ii ) )
893 m_errorLimits[ ii ] = 0;
894 else if( ii == DRCE_CLEARANCE || ii == DRCE_UNCONNECTED_ITEMS )
896 else
898 }
899
901
902 m_board->IncrementTimeStamp(); // Invalidate all caches...
903
904 DRC_CACHE_GENERATOR cacheGenerator;
905 cacheGenerator.SetDRCEngine( this );
906
907 if( !cacheGenerator.Run() ) // ... and regenerate them.
908 return;
909
910 // Recompute component classes
911 m_board->GetComponentClassManager().ForceComponentClassRecalculation();
912
913 int timestamp = m_board->GetTimeStamp();
914
915 for( DRC_TEST_PROVIDER* provider : m_testProviders )
916 {
917 if( m_logReporter )
918 m_logReporter->Report( wxString::Format( wxT( "Run DRC provider: '%s'" ), provider->GetName() ) );
919
920 PROF_TIMER providerTimer;
921
922 if( !provider->RunTests( aUnits ) )
923 break;
924
925 providerTimer.Stop();
926 wxLogTrace( traceDrcProfile, "DRC provider '%s' took %0.3f ms",
927 provider->GetName(), providerTimer.msecs() );
928 }
929
930 timer.Stop();
931 wxLogTrace( traceDrcProfile, "DRC took %0.3f ms", timer.msecs() );
932
933 // DRC tests are multi-threaded; anything that causes us to attempt to re-generate the
934 // caches while DRC is running is problematic.
935 wxASSERT( timestamp == m_board->GetTimeStamp() );
936}
937
938
939#define REPORT( s ) { if( aReporter ) { aReporter->Report( s ); } }
940
942 PCB_LAYER_ID aLayer, REPORTER* aReporter )
943{
944 DRC_CONSTRAINT constraint = EvalRules( ZONE_CONNECTION_CONSTRAINT, a, b, aLayer, aReporter );
945
946 REPORT( "" )
947 REPORT( wxString::Format( _( "Resolved zone connection type: %s." ),
948 PrintZoneConnection( constraint.m_ZoneConnection ) ) )
949
951 {
952 const PAD* pad = nullptr;
953
954 if( a->Type() == PCB_PAD_T )
955 pad = static_cast<const PAD*>( a );
956 else if( b->Type() == PCB_PAD_T )
957 pad = static_cast<const PAD*>( b );
958
959 if( pad && pad->GetAttribute() == PAD_ATTRIB::PTH )
960 {
962 }
963 else
964 {
965 REPORT( wxString::Format( _( "Pad is not a through hole pad; connection will be: %s." ),
968 }
969 }
970
971 return constraint;
972}
973
974
976 const BOARD_ITEM* b, PCB_LAYER_ID aLayer,
977 REPORTER* aReporter )
978{
979 /*
980 * NOTE: all string manipulation MUST BE KEPT INSIDE the REPORT macro. It absolutely
981 * kills performance when running bulk DRC tests (where aReporter is nullptr).
982 */
983
984 const BOARD_CONNECTED_ITEM* ac = a && a->IsConnected() ? static_cast<const BOARD_CONNECTED_ITEM*>( a ) : nullptr;
985 const BOARD_CONNECTED_ITEM* bc = b && b->IsConnected() ? static_cast<const BOARD_CONNECTED_ITEM*>( b ) : nullptr;
986
987 bool a_is_non_copper = a && ( !a->IsOnCopperLayer() || isKeepoutZone( a, false ) );
988 bool b_is_non_copper = b && ( !b->IsOnCopperLayer() || isKeepoutZone( b, false ) );
989
990 const PAD* pad = nullptr;
991 const ZONE* zone = nullptr;
992 const FOOTPRINT* parentFootprint = nullptr;
993
994 if( aConstraintType == ZONE_CONNECTION_CONSTRAINT
995 || aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT
996 || aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT
997 || aConstraintType == SOLDER_MASK_EXPANSION_CONSTRAINT
998 || aConstraintType == SOLDER_PASTE_ABS_MARGIN_CONSTRAINT
999 || aConstraintType == SOLDER_PASTE_REL_MARGIN_CONSTRAINT )
1000 {
1001 if( a && a->Type() == PCB_PAD_T )
1002 pad = static_cast<const PAD*>( a );
1003 else if( a && a->Type() == PCB_ZONE_T )
1004 zone = static_cast<const ZONE*>( a );
1005
1006 if( b && b->Type() == PCB_PAD_T )
1007 pad = static_cast<const PAD*>( b );
1008 else if( b && b->Type() == PCB_ZONE_T )
1009 zone = static_cast<const ZONE*>( b );
1010
1011 if( pad )
1012 parentFootprint = pad->GetParentFootprint();
1013 }
1014
1015 DRC_CONSTRAINT constraint;
1016 constraint.m_Type = aConstraintType;
1017
1018 auto applyConstraint =
1019 [&]( const DRC_ENGINE_CONSTRAINT* c )
1020 {
1021 if( c->constraint.m_Value.HasMin() )
1022 {
1023 if( c->parentRule && c->parentRule->IsImplicit() )
1024 constraint.m_ImplicitMin = true;
1025
1026 constraint.m_Value.SetMin( c->constraint.m_Value.Min() );
1027 }
1028
1029 if( c->constraint.m_Value.HasOpt() )
1030 constraint.m_Value.SetOpt( c->constraint.m_Value.Opt() );
1031
1032 if( c->constraint.m_Value.HasMax() )
1033 constraint .m_Value.SetMax( c->constraint.m_Value.Max() );
1034
1035 switch( c->constraint.m_Type )
1036 {
1044 if( constraint.m_Value.Min() > MAXIMUM_CLEARANCE )
1045 constraint.m_Value.SetMin( MAXIMUM_CLEARANCE );
1046
1047 break;
1048
1049 default:
1050 break;
1051 }
1052
1053 // While the expectation would be to OR the disallow flags, we've already
1054 // masked them down to aItem's type -- so we're really only looking for a
1055 // boolean here.
1056 constraint.m_DisallowFlags = c->constraint.m_DisallowFlags;
1057
1058 constraint.m_ZoneConnection = c->constraint.m_ZoneConnection;
1059
1060 constraint.SetParentRule( c->constraint.GetParentRule() );
1061
1062 constraint.SetOptionsFromOther( c->constraint );
1063 };
1064
1065 const FOOTPRINT* footprints[2] = { a ? a->GetParentFootprint() : nullptr,
1066 b ? b->GetParentFootprint() : nullptr };
1067
1068 // Handle Footprint net ties, which will zero out the clearance for footprint objects
1069 if( aConstraintType == CLEARANCE_CONSTRAINT // Only zero clearance, other constraints still apply
1070 && ( ( ( !ac ) ^ ( !bc ) )// Only apply to cases where we are comparing a connected item to a non-connected item
1071 // and not both connected. Connected items of different nets still need to be checked
1072 // for their standard clearance value
1073 || ( ( footprints[0] == footprints[1] ) // Unless both items are in the same footprint
1074 && footprints[0] ) ) // And that footprint exists
1075 && !a_is_non_copper // Also, both elements need to be on copper layers
1076 && !b_is_non_copper )
1077 {
1078 const BOARD_ITEM* child_items[2] = {a, b};
1079
1080 // These are the items being compared against, so the order is reversed
1081 const BOARD_CONNECTED_ITEM* alt_items[2] = {bc, ac};
1082
1083 for( int ii = 0; ii < 2; ++ii )
1084 {
1085 // We need both a footprint item and a connected item to check for a net tie
1086 if( !footprints[ii] || !alt_items[ii] )
1087 continue;
1088
1089 const std::set<int>& netcodes = footprints[ii]->GetNetTieCache( child_items[ii] );
1090
1091 auto it = netcodes.find( alt_items[ii]->GetNetCode() );
1092
1093 if( it != netcodes.end() )
1094 {
1095 REPORT( "" )
1096 REPORT( wxString::Format( _( "Net tie on %s; clearance: 0." ),
1097 EscapeHTML( footprints[ii]->GetItemDescription( this, true ) ) ) )
1098
1099 constraint.SetName( _( "net tie" ) );
1100 constraint.m_Value.SetMin( 0 );
1101 return constraint;
1102 }
1103 }
1104 }
1105
1106 // Local overrides take precedence over everything *except* board min clearance
1107 if( aConstraintType == CLEARANCE_CONSTRAINT || aConstraintType == HOLE_CLEARANCE_CONSTRAINT )
1108 {
1109 int override_val = 0;
1110 std::optional<int> overrideA;
1111 std::optional<int> overrideB;
1112
1113 if( ac && !b_is_non_copper )
1114 overrideA = ac->GetClearanceOverrides( nullptr );
1115
1116 if( bc && !a_is_non_copper )
1117 overrideB = bc->GetClearanceOverrides( nullptr );
1118
1119 if( overrideA.has_value() || overrideB.has_value() )
1120 {
1121 wxString msg;
1122
1123 if( overrideA.has_value() )
1124 {
1125 REPORT( "" )
1126 REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ),
1127 EscapeHTML( a->GetItemDescription( this, true ) ),
1128 MessageTextFromValue( overrideA.value() ) ) )
1129
1130 override_val = ac->GetClearanceOverrides( &msg ).value();
1131 }
1132
1133 if( overrideB.has_value() )
1134 {
1135 REPORT( "" )
1136 REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ),
1137 EscapeHTML( b->GetItemDescription( this, true ) ),
1138 MessageTextFromValue( overrideB.value() ) ) )
1139
1140 if( overrideB > override_val )
1141 override_val = bc->GetClearanceOverrides( &msg ).value();
1142 }
1143
1144 if( override_val )
1145 {
1146 if( aConstraintType == CLEARANCE_CONSTRAINT )
1147 {
1148 if( override_val < m_designSettings->m_MinClearance )
1149 {
1150 override_val = m_designSettings->m_MinClearance;
1151 msg = _( "board minimum" );
1152
1153 REPORT( "" )
1154 REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
1155 MessageTextFromValue( override_val ) ) )
1156 }
1157 }
1158 else
1159 {
1160 if( override_val < m_designSettings->m_HoleClearance )
1161 {
1162 override_val = m_designSettings->m_HoleClearance;
1163 msg = _( "board minimum hole" );
1164
1165 REPORT( "" )
1166 REPORT( wxString::Format( _( "Board minimum hole clearance: %s." ),
1167 MessageTextFromValue( override_val ) ) )
1168 }
1169 }
1170
1171 constraint.SetName( msg );
1172 constraint.m_Value.SetMin( override_val );
1173 return constraint;
1174 }
1175 }
1176 }
1177 else if( aConstraintType == ZONE_CONNECTION_CONSTRAINT )
1178 {
1179 if( pad && pad->GetLocalZoneConnection() != ZONE_CONNECTION::INHERITED )
1180 {
1181 wxString msg;
1182 ZONE_CONNECTION override = pad->GetZoneConnectionOverrides( &msg );
1183
1184 REPORT( "" )
1185 REPORT( wxString::Format( _( "Local override on %s; zone connection: %s." ),
1186 EscapeHTML( pad->GetItemDescription( this, true ) ),
1187 PrintZoneConnection( override ) ) )
1188
1189 constraint.SetName( msg );
1190 constraint.m_ZoneConnection = override;
1191 return constraint;
1192 }
1193 }
1194 else if( aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT )
1195 {
1196 if( pad && pad->GetLocalThermalGapOverride( nullptr ) > 0 )
1197 {
1198 wxString msg;
1199 int gap_override = pad->GetLocalThermalGapOverride( &msg );
1200
1201 REPORT( "" )
1202 REPORT( wxString::Format( _( "Local override on %s; thermal relief gap: %s." ),
1203 EscapeHTML( pad->GetItemDescription( this, true ) ),
1204 MessageTextFromValue( gap_override ) ) )
1205
1206 constraint.SetName( msg );
1207 constraint.m_Value.SetMin( gap_override );
1208 return constraint;
1209 }
1210 }
1211 else if( aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT )
1212 {
1213 if( pad && pad->GetLocalSpokeWidthOverride( nullptr ) > 0 )
1214 {
1215 wxString msg;
1216 int spoke_override = pad->GetLocalSpokeWidthOverride( &msg );
1217
1218 REPORT( "" )
1219 REPORT( wxString::Format( _( "Local override on %s; thermal spoke width: %s." ),
1220 EscapeHTML( pad->GetItemDescription( this, true ) ),
1221 MessageTextFromValue( spoke_override ) ) )
1222
1223 if( zone && zone->GetMinThickness() > spoke_override )
1224 {
1225 spoke_override = zone->GetMinThickness();
1226
1227 REPORT( "" )
1228 REPORT( wxString::Format( _( "%s min thickness: %s." ),
1229 EscapeHTML( zone->GetItemDescription( this, true ) ),
1230 MessageTextFromValue( spoke_override ) ) )
1231 }
1232
1233 constraint.SetName( msg );
1234 constraint.m_Value.SetMin( spoke_override );
1235 return constraint;
1236 }
1237 }
1238 else if( aConstraintType == SOLDER_MASK_EXPANSION_CONSTRAINT )
1239 {
1240 std::optional<int> override;
1241 const BOARD_ITEM* overrideItem = a;
1242
1243 if( pad )
1244 override = pad->GetLocalSolderMaskMargin();
1245 else if( a->Type() == PCB_SHAPE_T )
1246 override = static_cast<const PCB_SHAPE*>( a )->GetLocalSolderMaskMargin();
1247 else if( const PCB_TRACK* track = dynamic_cast<const PCB_TRACK*>( a ) )
1248 override = track->GetLocalSolderMaskMargin();
1249
1250 if( !override.has_value() && pad )
1251 {
1252 if( FOOTPRINT* overrideFootprint = pad->GetParentFootprint() )
1253 {
1254 override = overrideFootprint->GetLocalSolderMaskMargin();
1255 overrideItem = overrideFootprint;
1256 }
1257 }
1258
1259 if( override )
1260 {
1261 REPORT( "" )
1262 REPORT( wxString::Format( _( "Local override on %s; solder mask expansion: %s." ),
1263 EscapeHTML( overrideItem->GetItemDescription( this, true ) ),
1264 MessageTextFromValue( override.value() ) ) )
1265
1266 constraint.m_Value.SetOpt( override.value() );
1267 return constraint;
1268 }
1269 }
1270 else if( aConstraintType == SOLDER_PASTE_ABS_MARGIN_CONSTRAINT )
1271 {
1272 std::optional<int> override;
1273 const BOARD_ITEM* overrideItem = a;
1274
1275 if( pad )
1276 override = pad->GetLocalSolderPasteMargin();
1277
1278 if( !override.has_value() && pad )
1279 {
1280 if( FOOTPRINT* overrideFootprint = pad->GetParentFootprint() )
1281 {
1282 override = overrideFootprint->GetLocalSolderPasteMargin();
1283 overrideItem = overrideFootprint;
1284 }
1285 }
1286
1287 if( override )
1288 {
1289 REPORT( "" )
1290 REPORT( wxString::Format( _( "Local override on %s; solder paste absolute clearance: %s." ),
1291 EscapeHTML( overrideItem->GetItemDescription( this, true ) ),
1292 MessageTextFromValue( override.value() ) ) )
1293
1294 constraint.m_Value.SetOpt( override.value_or( 0 ) );
1295 return constraint;
1296 }
1297 }
1298 else if( aConstraintType == SOLDER_PASTE_REL_MARGIN_CONSTRAINT )
1299 {
1300 std::optional<double> overrideRatio;
1301 const BOARD_ITEM* overrideItem = a;
1302
1303 if( pad )
1304 overrideRatio = pad->GetLocalSolderPasteMarginRatio();
1305
1306 if( !overrideRatio.has_value() && pad )
1307 {
1308 if( FOOTPRINT* overrideFootprint = pad->GetParentFootprint() )
1309 {
1310 overrideRatio = overrideFootprint->GetLocalSolderPasteMarginRatio();
1311 overrideItem = overrideFootprint;
1312 }
1313 }
1314
1315 if( overrideRatio )
1316 {
1317 REPORT( "" )
1318 REPORT( wxString::Format( _( "Local override on %s; solder paste relative clearance: %s." ),
1319 EscapeHTML( overrideItem->GetItemDescription( this, true ) ),
1320 MessageTextFromValue( overrideRatio.value() * 100.0 ) ) )
1321
1322 constraint.m_Value.SetOpt( KiROUND( overrideRatio.value_or( 0 ) * 1000 ) );
1323 return constraint;
1324 }
1325 }
1326
1327 auto testAssertion =
1328 [&]( const DRC_ENGINE_CONSTRAINT* c )
1329 {
1330 REPORT( wxString::Format( _( "Checking assertion '%s'." ),
1331 EscapeHTML( c->constraint.m_Test->GetExpression() ) ) )
1332
1333 if( c->constraint.m_Test->EvaluateFor( a, b, c->constraint.m_Type, aLayer, aReporter ) )
1334 REPORT( _( "Assertion passed." ) )
1335 else
1336 REPORT( EscapeHTML( _( "--> Assertion failed. <--" ) ) )
1337 };
1338
1339 auto processConstraint =
1340 [&]( const DRC_ENGINE_CONSTRAINT* c )
1341 {
1342 bool implicit = c->parentRule && c->parentRule->IsImplicit();
1343
1344 REPORT( "" )
1345
1346 switch( c->constraint.m_Type )
1347 {
1355 REPORT( wxString::Format( _( "Checking %s clearance: %s." ),
1356 EscapeHTML( c->constraint.GetName() ),
1357 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1358 break;
1360 REPORT( wxString::Format( _( "Checking %s creepage: %s." ),
1361 EscapeHTML( c->constraint.GetName() ),
1362 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1363 break;
1365 REPORT( wxString::Format( _( "Checking %s max uncoupled length: %s." ),
1366 EscapeHTML( c->constraint.GetName() ),
1367 MessageTextFromValue( c->constraint.m_Value.Max() ) ) )
1368 break;
1369
1370 case SKEW_CONSTRAINT:
1371 REPORT( wxString::Format( _( "Checking %s max skew: %s." ),
1372 EscapeHTML( c->constraint.GetName() ),
1373 MessageTextFromValue( c->constraint.m_Value.Max() ) ) )
1374 break;
1375
1377 REPORT( wxString::Format( _( "Checking %s gap: %s." ),
1378 EscapeHTML( c->constraint.GetName() ),
1379 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1380 break;
1381
1383 REPORT( wxString::Format( _( "Checking %s thermal spoke width: %s." ),
1384 EscapeHTML( c->constraint.GetName() ),
1385 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1386 break;
1387
1389 REPORT( wxString::Format( _( "Checking %s solder mask expansion: %s." ),
1390 EscapeHTML( c->constraint.GetName() ),
1391 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1392 break;
1393
1395 REPORT( wxString::Format( _( "Checking %s solder paste absolute clearance: %s." ),
1396 EscapeHTML( c->constraint.GetName() ),
1397 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1398 break;
1399
1401 REPORT( wxString::Format( _( "Checking %s solder paste relative clearance: %s." ),
1402 EscapeHTML( c->constraint.GetName() ),
1403 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1404 break;
1405
1407 REPORT( wxString::Format( _( "Checking %s min spoke count: %s." ),
1408 EscapeHTML( c->constraint.GetName() ),
1409 MessageTextFromUnscaledValue( c->constraint.m_Value.Min() ) ) )
1410 break;
1411
1413 REPORT( wxString::Format( _( "Checking %s zone connection: %s." ),
1414 EscapeHTML( c->constraint.GetName() ),
1415 PrintZoneConnection( c->constraint.m_ZoneConnection ) ) )
1416 break;
1417
1425 case LENGTH_CONSTRAINT:
1428 {
1429 if( implicit )
1430 {
1431 switch( c->constraint.m_Type )
1432 {
1434 if( c->constraint.m_Value.HasOpt() )
1435 {
1436 REPORT( wxString::Format( _( "Checking %s track width: opt %s." ),
1437 EscapeHTML( c->constraint.GetName() ),
1438 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1439 }
1440 else if( c->constraint.m_Value.HasMin() )
1441 {
1442 REPORT( wxString::Format( _( "Checking %s track width: min %s." ),
1443 EscapeHTML( c->constraint.GetName() ),
1444 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1445 }
1446
1447 break;
1448
1450 REPORT( wxString::Format( _( "Checking %s annular width: min %s." ),
1451 EscapeHTML( c->constraint.GetName() ),
1452 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1453 break;
1454
1456 if( c->constraint.m_Value.HasOpt() )
1457 {
1458 REPORT( wxString::Format( _( "Checking %s via diameter: opt %s." ),
1459 EscapeHTML( c->constraint.GetName() ),
1460 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1461 }
1462 else if( c->constraint.m_Value.HasMin() )
1463 {
1464 REPORT( wxString::Format( _( "Checking %s via diameter: min %s." ),
1465 EscapeHTML( c->constraint.GetName() ),
1466 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1467 }
1468 break;
1469
1471 if( c->constraint.m_Value.HasOpt() )
1472 {
1473 REPORT( wxString::Format( _( "Checking %s hole size: opt %s." ),
1474 EscapeHTML( c->constraint.GetName() ),
1475 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1476 }
1477 else if( c->constraint.m_Value.HasMin() )
1478 {
1479 REPORT( wxString::Format( _( "Checking %s hole size: min %s." ),
1480 EscapeHTML( c->constraint.GetName() ),
1481 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1482 }
1483
1484 break;
1485
1489 REPORT( wxString::Format( _( "Checking %s: min %s." ),
1490 EscapeHTML( c->constraint.GetName() ),
1491 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1492 break;
1493
1495 if( c->constraint.m_Value.HasOpt() )
1496 {
1497 REPORT( wxString::Format( _( "Checking %s diff pair gap: opt %s." ),
1498 EscapeHTML( c->constraint.GetName() ),
1499 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1500 }
1501 else if( c->constraint.m_Value.HasMin() )
1502 {
1503 REPORT( wxString::Format( _( "Checking %s clearance: min %s." ),
1504 EscapeHTML( c->constraint.GetName() ),
1505 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1506 }
1507
1508 break;
1509
1511 REPORT( wxString::Format( _( "Checking %s hole to hole: min %s." ),
1512 EscapeHTML( c->constraint.GetName() ),
1513 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1514 break;
1515
1516 default:
1517 REPORT( wxString::Format( _( "Checking %s." ),
1518 EscapeHTML( c->constraint.GetName() ) ) )
1519 }
1520 }
1521 else
1522 {
1523 REPORT( wxString::Format( _( "Checking %s: min %s; opt %s; max %s." ),
1524 EscapeHTML( c->constraint.GetName() ),
1525 c->constraint.m_Value.HasMin()
1526 ? MessageTextFromValue( c->constraint.m_Value.Min() )
1527 : wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" ),
1528 c->constraint.m_Value.HasOpt()
1529 ? MessageTextFromValue( c->constraint.m_Value.Opt() )
1530 : wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" ),
1531 c->constraint.m_Value.HasMax()
1532 ? MessageTextFromValue( c->constraint.m_Value.Max() )
1533 : wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" ) ) )
1534 }
1535 break;
1536 }
1537
1538 default:
1539 REPORT( wxString::Format( _( "Checking %s." ),
1540 EscapeHTML( c->constraint.GetName() ) ) )
1541 }
1542
1543 if( c->constraint.m_Type == CLEARANCE_CONSTRAINT )
1544 {
1545 if( a_is_non_copper || b_is_non_copper )
1546 {
1547 if( implicit )
1548 {
1549 REPORT( _( "Netclass clearances apply only between copper items." ) )
1550 }
1551 else if( a_is_non_copper )
1552 {
1553 REPORT( wxString::Format( _( "%s contains no copper. Rule ignored." ),
1554 EscapeHTML( a->GetItemDescription( this, true ) ) ) )
1555 }
1556 else if( b_is_non_copper )
1557 {
1558 REPORT( wxString::Format( _( "%s contains no copper. Rule ignored." ),
1559 EscapeHTML( b->GetItemDescription( this, true ) ) ) )
1560 }
1561
1562 return;
1563 }
1564 }
1565 else if( c->constraint.m_Type == DISALLOW_CONSTRAINT )
1566 {
1567 int mask;
1568
1569 if( a->GetFlags() & HOLE_PROXY )
1570 {
1571 mask = DRC_DISALLOW_HOLES;
1572 }
1573 else if( a->Type() == PCB_VIA_T )
1574 {
1575 const PCB_VIA* via = static_cast<const PCB_VIA*>( a );
1576
1577 if( via->IsMicroVia() )
1579 else if( via->IsBlindVia() )
1581 else if( via->IsBuriedVia() )
1583 else
1585 }
1586 else
1587 {
1588 switch( a->Type() )
1589 {
1590 case PCB_TRACE_T: mask = DRC_DISALLOW_TRACKS; break;
1591 case PCB_ARC_T: mask = DRC_DISALLOW_TRACKS; break;
1592 case PCB_PAD_T: mask = DRC_DISALLOW_PADS; break;
1593 case PCB_FOOTPRINT_T: mask = DRC_DISALLOW_FOOTPRINTS; break;
1594 case PCB_SHAPE_T: mask = DRC_DISALLOW_GRAPHICS; break;
1595 case PCB_BARCODE_T: mask = DRC_DISALLOW_GRAPHICS; break;
1596 case PCB_FIELD_T: mask = DRC_DISALLOW_TEXTS; break;
1597 case PCB_TEXT_T: mask = DRC_DISALLOW_TEXTS; break;
1598 case PCB_TEXTBOX_T: mask = DRC_DISALLOW_TEXTS; break;
1599 case PCB_TABLE_T: mask = DRC_DISALLOW_TEXTS; break;
1600
1601 case PCB_ZONE_T:
1602 // Treat teardrop areas as tracks for DRC purposes
1603 if( static_cast<const ZONE*>( a )->IsTeardropArea() )
1604 mask = DRC_DISALLOW_TRACKS;
1605 else
1606 mask = DRC_DISALLOW_ZONES;
1607
1608 break;
1609
1610 case PCB_LOCATE_HOLE_T: mask = DRC_DISALLOW_HOLES; break;
1611 default: mask = 0; break;
1612 }
1613 }
1614
1615 if( ( c->constraint.m_DisallowFlags & mask ) == 0 )
1616 {
1617 if( implicit )
1618 REPORT( _( "Keepout constraint not met." ) )
1619 else
1620 REPORT( _( "Disallow constraint not met." ) )
1621
1622 return;
1623 }
1624
1625 LSET itemLayers = a->GetLayerSet();
1626
1627 if( a->Type() == PCB_FOOTPRINT_T )
1628 {
1629 const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( a );
1630
1631 if( !footprint->GetCourtyard( F_CrtYd ).IsEmpty() )
1632 itemLayers |= LSET::FrontMask();
1633
1634 if( !footprint->GetCourtyard( B_CrtYd ).IsEmpty() )
1635 itemLayers |= LSET::BackMask();
1636 }
1637
1638 if( !( c->layerTest & itemLayers ).any() )
1639 {
1640 if( implicit )
1641 {
1642 REPORT( _( "Keepout layer(s) not matched." ) )
1643 }
1644 else if( c->parentRule )
1645 {
1646 REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
1647 EscapeHTML( c->parentRule->m_LayerSource ) ) )
1648 }
1649 else
1650 {
1651 REPORT( _( "Rule layer not matched; rule ignored." ) )
1652 }
1653
1654 return;
1655 }
1656 }
1657
1658 if( ( IsPcbLayer( aLayer ) && !c->layerTest.test( aLayer ) )
1659 || ( m_board->GetEnabledLayers() & c->layerTest ).count() == 0 )
1660 {
1661 if( implicit )
1662 {
1663 REPORT( _( "Constraint layer not matched." ) )
1664 }
1665 else if( c->parentRule )
1666 {
1667 REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
1668 EscapeHTML( c->parentRule->m_LayerSource ) ) )
1669 }
1670 else
1671 {
1672 REPORT( _( "Rule layer not matched; rule ignored." ) )
1673 }
1674 }
1675 else if( c->constraint.m_Type == HOLE_TO_HOLE_CONSTRAINT
1676 && ( !a->HasDrilledHole() && !b->HasDrilledHole() ) )
1677 {
1678 // Report non-drilled-holes as an implicit condition
1679 REPORT( wxString::Format( _( "%s is not a drilled hole; rule ignored." ),
1680 a->GetItemDescription( this, true ) ) )
1681 }
1682 else if( !c->condition || c->condition->GetExpression().IsEmpty() )
1683 {
1684 if( aReporter )
1685 {
1686 if( implicit )
1687 {
1688 REPORT( _( "Unconditional constraint applied." ) )
1689 }
1690 else if( constraint.m_Type == ASSERTION_CONSTRAINT )
1691 {
1692 REPORT( _( "Unconditional rule applied." ) )
1693 testAssertion( c );
1694 }
1695 else
1696 {
1697 REPORT( _( "Unconditional rule applied; overrides previous constraints." ) )
1698 }
1699 }
1700
1701 applyConstraint( c );
1702 }
1703 else
1704 {
1705 if( implicit )
1706 {
1707 // Don't report on implicit rule conditions; they're synthetic.
1708 }
1709 else
1710 {
1711 REPORT( wxString::Format( _( "Checking rule condition '%s'." ),
1712 EscapeHTML( c->condition->GetExpression() ) ) )
1713 }
1714
1715 if( c->condition->EvaluateFor( a, b, c->constraint.m_Type, aLayer, aReporter ) )
1716 {
1717 if( aReporter )
1718 {
1719 if( implicit )
1720 {
1721 REPORT( _( "Constraint applied." ) )
1722 }
1723 else if( constraint.m_Type == ASSERTION_CONSTRAINT )
1724 {
1725 REPORT( _( "Rule applied." ) )
1726 testAssertion( c );
1727 }
1728 else
1729 {
1730 REPORT( _( "Rule applied; overrides previous constraints." ) )
1731 }
1732 }
1733
1734 applyConstraint( c );
1735 }
1736 else
1737 {
1738 REPORT( implicit ? _( "Membership not satisfied; constraint ignored." )
1739 : _( "Condition not satisfied; rule ignored." ) )
1740 }
1741 }
1742 };
1743
1744 // Fast-path for netclass clearance when no explicit or diff pair override rules exist
1745 if( aConstraintType == CLEARANCE_CONSTRAINT
1748 && !aReporter
1749 && !a_is_non_copper
1750 && ( !b || !b_is_non_copper ) )
1751 {
1752 int clearance = 0;
1753
1754 // Get netclass names outside of the lock to minimize critical section
1755 wxString ncNameA;
1756 wxString ncNameB;
1757
1758 if( ac )
1759 {
1760 NETCLASS* ncA = ac->GetEffectiveNetClass();
1761
1762 if( ncA )
1763 ncNameA = ncA->GetName();
1764 }
1765
1766 if( bc )
1767 {
1768 NETCLASS* ncB = bc->GetEffectiveNetClass();
1769
1770 if( ncB )
1771 ncNameB = ncB->GetName();
1772 }
1773
1774 // Look up clearances with shared lock protection
1775 if( !ncNameA.empty() || !ncNameB.empty() )
1776 {
1777 std::shared_lock<std::shared_mutex> readLock( m_clearanceCacheMutex );
1778
1779 if( !ncNameA.empty() )
1780 {
1781 auto it = m_netclassClearances.find( ncNameA );
1782
1783 if( it != m_netclassClearances.end() )
1784 clearance = it->second;
1785 }
1786
1787 if( !ncNameB.empty() )
1788 {
1789 auto it = m_netclassClearances.find( ncNameB );
1790
1791 if( it != m_netclassClearances.end() )
1792 clearance = std::max( clearance, it->second );
1793 }
1794 }
1795
1796 if( clearance > 0 )
1797 {
1798 constraint.m_Value.SetMin( clearance );
1799 constraint.m_ImplicitMin = true;
1800 }
1801 }
1802 else
1803 {
1804 auto it = m_constraintMap.find( aConstraintType );
1805
1806 if( it != m_constraintMap.end() )
1807 {
1808 for( DRC_ENGINE_CONSTRAINT* rule : *it->second )
1809 processConstraint( rule );
1810 }
1811 }
1812
1813 if( constraint.GetParentRule() && !constraint.GetParentRule()->IsImplicit() )
1814 return constraint;
1815
1816 // Special case for properties which can be inherited from parent footprints. We've already
1817 // checked for local overrides, and there were no rules targetting the item itself, so we know
1818 // we're inheriting and need to see if there are any rules targetting the parent footprint.
1819 if( pad && parentFootprint && ( aConstraintType == ZONE_CONNECTION_CONSTRAINT
1820 || aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT
1821 || aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT
1822 || aConstraintType == SOLDER_MASK_EXPANSION_CONSTRAINT
1823 || aConstraintType == SOLDER_PASTE_ABS_MARGIN_CONSTRAINT
1824 || aConstraintType == SOLDER_PASTE_REL_MARGIN_CONSTRAINT ) )
1825 {
1826 REPORT( "" )
1827 REPORT( wxString::Format( _( "Inheriting from parent: %s." ),
1828 EscapeHTML( parentFootprint->GetItemDescription( this, true ) ) ) )
1829
1830 if( a == pad )
1831 a = parentFootprint;
1832 else
1833 b = parentFootprint;
1834
1835 auto it = m_constraintMap.find( aConstraintType );
1836
1837 if( it != m_constraintMap.end() )
1838 {
1839 for( DRC_ENGINE_CONSTRAINT* rule : *it->second )
1840 processConstraint( rule );
1841
1842 if( constraint.GetParentRule() && !constraint.GetParentRule()->IsImplicit() )
1843 return constraint;
1844 }
1845
1846 // Found nothing again? Return the defaults.
1847 if( aConstraintType == SOLDER_MASK_EXPANSION_CONSTRAINT )
1848 {
1849 constraint.SetParentRule( nullptr );
1850 constraint.SetName( _( "board setup" ) );
1851 constraint.m_Value.SetOpt( m_designSettings->m_SolderMaskExpansion );
1852 return constraint;
1853 }
1854 else if( aConstraintType == SOLDER_PASTE_ABS_MARGIN_CONSTRAINT )
1855 {
1856 constraint.SetParentRule( nullptr );
1857 constraint.SetName( _( "board setup" ) );
1858 constraint.m_Value.SetOpt( m_designSettings->m_SolderPasteMargin );
1859 return constraint;
1860 }
1861 else if( aConstraintType == SOLDER_PASTE_REL_MARGIN_CONSTRAINT )
1862 {
1863 constraint.SetParentRule( nullptr );
1864 constraint.SetName( _( "board setup" ) );
1865 constraint.m_Value.SetOpt( KiROUND( m_designSettings->m_SolderPasteMarginRatio * 1000 ) );
1866 return constraint;
1867 }
1868 }
1869
1870 // Unfortunately implicit rules don't work for local clearances (such as zones) because
1871 // they have to be max'ed with netclass values (which are already implicit rules), and our
1872 // rule selection paradigm is "winner takes all".
1873 if( aConstraintType == CLEARANCE_CONSTRAINT )
1874 {
1875 int global = constraint.m_Value.Min();
1876 int clearance = global;
1877 bool needBlankLine = true;
1878
1879 if( ac && ac->GetLocalClearance().has_value() )
1880 {
1881 int localA = ac->GetLocalClearance().value();
1882
1883 if( needBlankLine )
1884 {
1885 REPORT( "" )
1886 needBlankLine = false;
1887 }
1888
1889 REPORT( wxString::Format( _( "Local clearance on %s: %s." ),
1890 EscapeHTML( a->GetItemDescription( this, true ) ),
1891 MessageTextFromValue( localA ) ) )
1892
1893 if( localA > clearance )
1894 {
1895 wxString msg;
1896 clearance = ac->GetLocalClearance( &msg ).value();
1897 constraint.SetParentRule( nullptr );
1898 constraint.SetName( msg );
1899 constraint.m_Value.SetMin( clearance );
1900 }
1901 }
1902
1903 if( bc && bc->GetLocalClearance().has_value() )
1904 {
1905 int localB = bc->GetLocalClearance().value();
1906
1907 if( needBlankLine )
1908 {
1909 REPORT( "" )
1910 needBlankLine = false;
1911 }
1912
1913 REPORT( wxString::Format( _( "Local clearance on %s: %s." ),
1914 EscapeHTML( b->GetItemDescription( this, true ) ),
1915 MessageTextFromValue( localB ) ) )
1916
1917 if( localB > clearance )
1918 {
1919 wxString msg;
1920 clearance = bc->GetLocalClearance( &msg ).value();
1921 constraint.SetParentRule( nullptr );
1922 constraint.SetName( msg );
1923 constraint.m_Value.SetMin( clearance );
1924 }
1925 }
1926
1927 if( !a_is_non_copper && !b_is_non_copper )
1928 {
1929 if( needBlankLine )
1930 {
1931 REPORT( "" )
1932 needBlankLine = false;
1933 }
1934
1935 REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
1936 MessageTextFromValue( m_designSettings->m_MinClearance ) ) )
1937
1938 if( clearance < m_designSettings->m_MinClearance )
1939 {
1940 constraint.SetParentRule( nullptr );
1941 constraint.SetName( _( "board minimum" ) );
1942 constraint.m_Value.SetMin( m_designSettings->m_MinClearance );
1943 }
1944 }
1945
1946 return constraint;
1947 }
1948 else if( aConstraintType == DIFF_PAIR_GAP_CONSTRAINT )
1949 {
1950 REPORT( "" )
1951 REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
1952 MessageTextFromValue( m_designSettings->m_MinClearance ) ) )
1953
1954 if( constraint.m_Value.Min() < m_designSettings->m_MinClearance )
1955 {
1956 constraint.SetParentRule( nullptr );
1957 constraint.SetName( _( "board minimum" ) );
1958 constraint.m_Value.SetMin( m_designSettings->m_MinClearance );
1959 }
1960
1961 return constraint;
1962 }
1963 else if( aConstraintType == ZONE_CONNECTION_CONSTRAINT )
1964 {
1965 if( pad && parentFootprint )
1966 {
1967 ZONE_CONNECTION local = parentFootprint->GetLocalZoneConnection();
1968
1969 if( local != ZONE_CONNECTION::INHERITED )
1970 {
1971 REPORT( "" )
1972 REPORT( wxString::Format( _( "%s zone connection: %s." ),
1973 EscapeHTML( parentFootprint->GetItemDescription( this, true ) ),
1974 PrintZoneConnection( local ) ) )
1975
1976 constraint.SetParentRule( nullptr );
1977 constraint.SetName( _( "footprint" ) );
1978 constraint.m_ZoneConnection = local;
1979 return constraint;
1980 }
1981 }
1982
1983 if( zone )
1984 {
1985 ZONE_CONNECTION local = zone->GetPadConnection();
1986
1987 REPORT( "" )
1988 REPORT( wxString::Format( _( "%s pad connection: %s." ),
1989 EscapeHTML( zone->GetItemDescription( this, true ) ),
1990 PrintZoneConnection( local ) ) )
1991
1992 constraint.SetParentRule( nullptr );
1993 constraint.SetName( _( "zone" ) );
1994 constraint.m_ZoneConnection = local;
1995 return constraint;
1996 }
1997 }
1998 else if( aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT )
1999 {
2000 if( zone )
2001 {
2002 int local = zone->GetThermalReliefGap();
2003
2004 REPORT( "" )
2005 REPORT( wxString::Format( _( "%s thermal relief gap: %s." ),
2006 EscapeHTML( zone->GetItemDescription( this, true ) ),
2007 MessageTextFromValue( local ) ) )
2008
2009 constraint.SetParentRule( nullptr );
2010 constraint.SetName( _( "zone" ) );
2011 constraint.m_Value.SetMin( local );
2012 return constraint;
2013 }
2014 }
2015 else if( aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT )
2016 {
2017 if( zone )
2018 {
2019 int local = zone->GetThermalReliefSpokeWidth();
2020
2021 REPORT( "" )
2022 REPORT( wxString::Format( _( "%s thermal spoke width: %s." ),
2023 EscapeHTML( zone->GetItemDescription( this, true ) ),
2024 MessageTextFromValue( local ) ) )
2025
2026 constraint.SetParentRule( nullptr );
2027 constraint.SetName( _( "zone" ) );
2028 constraint.m_Value.SetMin( local );
2029 return constraint;
2030 }
2031 }
2032
2033 if( !constraint.GetParentRule() )
2034 {
2035 constraint.m_Type = NULL_CONSTRAINT;
2036 constraint.m_DisallowFlags = 0;
2037 }
2038
2039 return constraint;
2040}
2041
2042
2044 PCB_LAYER_ID aLayer )
2045{
2048
2049 c = EvalRules( CLEARANCE_CONSTRAINT, a, b, aLayer );
2050
2051 if( c.m_Value.HasMin() )
2052 result.clearance = c.m_Value.Min();
2053
2054 c = EvalRules( HOLE_CLEARANCE_CONSTRAINT, a, b, aLayer );
2055
2056 if( c.m_Value.HasMin() )
2057 result.holeClearance = c.m_Value.Min();
2058
2059 c = EvalRules( HOLE_TO_HOLE_CONSTRAINT, a, b, aLayer );
2060
2061 if( c.m_Value.HasMin() )
2062 result.holeToHole = c.m_Value.Min();
2063
2064 c = EvalRules( EDGE_CLEARANCE_CONSTRAINT, a, b, aLayer );
2065
2066 if( c.m_Value.HasMin() )
2067 result.edgeClearance = c.m_Value.Min();
2068
2069 c = EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, a, b, aLayer );
2070
2071 if( c.m_Value.HasMin() )
2072 result.physicalClearance = c.m_Value.Min();
2073
2074 return result;
2075}
2076
2077
2079 std::function<void( const DRC_CONSTRAINT* )> aFailureHandler,
2080 REPORTER* aReporter )
2081{
2082 /*
2083 * NOTE: all string manipulation MUST BE KEPT INSIDE the REPORT macro. It absolutely
2084 * kills performance when running bulk DRC tests (where aReporter is nullptr).
2085 */
2086
2087 auto testAssertion =
2088 [&]( const DRC_ENGINE_CONSTRAINT* c )
2089 {
2090 REPORT( wxString::Format( _( "Checking rule assertion '%s'." ),
2091 EscapeHTML( c->constraint.m_Test->GetExpression() ) ) )
2092
2093 if( c->constraint.m_Test->EvaluateFor( a, nullptr, c->constraint.m_Type,
2094 a->GetLayer(), aReporter ) )
2095 {
2096 REPORT( _( "Assertion passed." ) )
2097 }
2098 else
2099 {
2100 REPORT( EscapeHTML( _( "--> Assertion failed. <--" ) ) )
2101 aFailureHandler( &c->constraint );
2102 }
2103 };
2104
2105 auto processConstraint =
2106 [&]( const DRC_ENGINE_CONSTRAINT* c )
2107 {
2108 REPORT( "" )
2109 REPORT( wxString::Format( _( "Checking %s." ), c->constraint.GetName() ) )
2110
2111 if( !( a->GetLayerSet() & c->layerTest ).any() )
2112 {
2113 REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
2114 EscapeHTML( c->parentRule->m_LayerSource ) ) )
2115 }
2116
2117 if( !c->condition || c->condition->GetExpression().IsEmpty() )
2118 {
2119 REPORT( _( "Unconditional rule applied." ) )
2120 testAssertion( c );
2121 }
2122 else
2123 {
2124 REPORT( wxString::Format( _( "Checking rule condition '%s'." ),
2125 EscapeHTML( c->condition->GetExpression() ) ) )
2126
2127 if( c->condition->EvaluateFor( a, nullptr, c->constraint.m_Type,
2128 a->GetLayer(), aReporter ) )
2129 {
2130 REPORT( _( "Rule applied." ) )
2131 testAssertion( c );
2132 }
2133 else
2134 {
2135 REPORT( _( "Condition not satisfied; rule ignored." ) )
2136 }
2137 }
2138 };
2139
2140 auto it = m_constraintMap.find( ASSERTION_CONSTRAINT );
2141
2142 if( it != m_constraintMap.end() )
2143 {
2144 for( int ii = 0; ii < (int) it->second->size(); ++ii )
2145 processConstraint( it->second->at( ii ) );
2146 }
2147}
2148
2149
2150#undef REPORT
2151
2152
2154{
2155 assert( error_code >= 0 && error_code <= DRCE_LAST );
2156 std::lock_guard<std::mutex> lock( m_errorLimitsMutex );
2157 return m_errorLimits[ error_code ] <= 0;
2158}
2159
2160
2161void DRC_ENGINE::ReportViolation( const std::shared_ptr<DRC_ITEM>& aItem, const VECTOR2I& aPos,
2162 int aMarkerLayer, const std::function<void( PCB_MARKER* )>& aPathGenerator )
2163{
2164 {
2165 std::lock_guard<std::mutex> lock( m_errorLimitsMutex );
2166 m_errorLimits[ aItem->GetErrorCode() ] -= 1;
2167 }
2168
2169 if( m_violationHandler )
2170 {
2171 static std::mutex handlerLock;
2172 std::lock_guard<std::mutex> guard( handlerLock );
2173 m_violationHandler( aItem, aPos, aMarkerLayer, aPathGenerator );
2174 }
2175
2176 if( m_logReporter )
2177 {
2178 wxString msg = wxString::Format( wxT( "Test '%s': %s (code %d)" ),
2179 aItem->GetViolatingTest()->GetName(),
2180 aItem->GetErrorMessage( false ),
2181 aItem->GetErrorCode() );
2182
2183 DRC_RULE* rule = aItem->GetViolatingRule();
2184
2185 if( rule )
2186 msg += wxString::Format( wxT( ", violating rule: '%s'" ), rule->m_Name );
2187
2188 m_logReporter->Report( msg );
2189
2190 wxString violatingItemsStr = wxT( "Violating items: " );
2191
2192 m_logReporter->Report( wxString::Format( wxT( " |- violating position (%d, %d)" ),
2193 aPos.x,
2194 aPos.y ) );
2195 }
2196}
2197
2198
2200{
2201 if( !m_progressReporter )
2202 return true;
2203
2204 return m_progressReporter->KeepRefreshing( aWait );
2205}
2206
2207
2209{
2210 if( m_progressReporter )
2211 m_progressReporter->AdvanceProgress();
2212}
2213
2214
2216{
2217 if( m_progressReporter )
2218 m_progressReporter->SetMaxProgress( aSize );
2219}
2220
2221
2222bool DRC_ENGINE::ReportProgress( double aProgress )
2223{
2224 if( !m_progressReporter )
2225 return true;
2226
2227 m_progressReporter->SetCurrentProgress( aProgress );
2228 return m_progressReporter->KeepRefreshing( false );
2229}
2230
2231
2232bool DRC_ENGINE::ReportPhase( const wxString& aMessage )
2233{
2234 if( !m_progressReporter )
2235 return true;
2236
2237 m_progressReporter->AdvancePhase( aMessage );
2238 return m_progressReporter->KeepRefreshing( false );
2239}
2240
2241
2243{
2244 return m_progressReporter && m_progressReporter->IsCancelled();
2245}
2246
2247
2249{
2250 auto it = m_constraintMap.find( constraintID );
2251 return it != m_constraintMap.end() && !it->second->empty();
2252}
2253
2254
2256 bool aUnconditionalOnly )
2257{
2258 int worst = 0;
2259 auto it = m_constraintMap.find( aConstraintId );
2260
2261 if( it != m_constraintMap.end() )
2262 {
2263 for( DRC_ENGINE_CONSTRAINT* c : *it->second )
2264 {
2265 if( aUnconditionalOnly && c->condition )
2266 continue;
2267
2268 int current = c->constraint.GetValue().Min();
2269
2270 if( current > worst )
2271 {
2272 worst = current;
2273 aConstraint = c->constraint;
2274 }
2275 }
2276 }
2277
2278 return worst > 0;
2279}
2280
2281
2283{
2284 std::set<int> distinctMinimums;
2285 auto it = m_constraintMap.find( aConstraintId );
2286
2287 if( it != m_constraintMap.end() )
2288 {
2289 for( DRC_ENGINE_CONSTRAINT* c : *it->second )
2290 distinctMinimums.emplace( c->constraint.GetValue().Min() );
2291 }
2292
2293 return distinctMinimums;
2294}
2295
2296
2297// fixme: move two functions below to pcbcommon?
2298int DRC_ENGINE::MatchDpSuffix( const wxString& aNetName, wxString& aComplementNet,
2299 wxString& aBaseDpName )
2300{
2301 int rv = 0;
2302 int count = 0;
2303
2304 for( auto it = aNetName.rbegin(); it != aNetName.rend() && rv == 0; ++it, ++count )
2305 {
2306 int ch = *it;
2307
2308 if( ( ch >= '0' && ch <= '9' ) || ch == '_' )
2309 {
2310 continue;
2311 }
2312 else if( ch == '+' )
2313 {
2314 aComplementNet = wxT( "-" );
2315 rv = 1;
2316 }
2317 else if( ch == '-' )
2318 {
2319 aComplementNet = wxT( "+" );
2320 rv = -1;
2321 }
2322 else if( ch == 'N' )
2323 {
2324 aComplementNet = wxT( "P" );
2325 rv = -1;
2326 }
2327 else if ( ch == 'P' )
2328 {
2329 aComplementNet = wxT( "N" );
2330 rv = 1;
2331 }
2332 else
2333 {
2334 break;
2335 }
2336 }
2337
2338 if( rv != 0 && count >= 1 )
2339 {
2340 aBaseDpName = aNetName.Left( aNetName.Length() - count );
2341 aComplementNet = wxString( aBaseDpName ) << aComplementNet << aNetName.Right( count - 1 );
2342 }
2343
2344 return rv;
2345}
2346
2347
2348bool DRC_ENGINE::IsNetADiffPair( BOARD* aBoard, NETINFO_ITEM* aNet, int& aNetP, int& aNetN )
2349{
2350 wxString refName = aNet->GetNetname();
2351 wxString dummy, coupledNetName;
2352
2353 if( int polarity = MatchDpSuffix( refName, coupledNetName, dummy ) )
2354 {
2355 NETINFO_ITEM* net = aBoard->FindNet( coupledNetName );
2356
2357 if( !net )
2358 return false;
2359
2360 if( polarity > 0 )
2361 {
2362 aNetP = aNet->GetNetCode();
2363 aNetN = net->GetNetCode();
2364 }
2365 else
2366 {
2367 aNetP = net->GetNetCode();
2368 aNetN = aNet->GetNetCode();
2369 }
2370
2371 return true;
2372 }
2373
2374 return false;
2375}
2376
2377
2382bool DRC_ENGINE::IsNetTieExclusion( int aTrackNetCode, PCB_LAYER_ID aTrackLayer,
2383 const VECTOR2I& aCollisionPos, BOARD_ITEM* aCollidingItem )
2384{
2385 FOOTPRINT* parentFootprint = aCollidingItem->GetParentFootprint();
2386
2387 if( parentFootprint && parentFootprint->IsNetTie() )
2388 {
2390 std::map<wxString, int> padToNetTieGroupMap = parentFootprint->MapPadNumbersToNetTieGroups();
2391
2392 for( PAD* pad : parentFootprint->Pads() )
2393 {
2394 if( padToNetTieGroupMap[ pad->GetNumber() ] >= 0 && aTrackNetCode == pad->GetNetCode() )
2395 {
2396 if( pad->GetEffectiveShape( aTrackLayer )->Collide( aCollisionPos, epsilon ) )
2397 return true;
2398 }
2399 }
2400 }
2401
2402 return false;
2403}
2404
2405
2407{
2408 for( DRC_TEST_PROVIDER* prov : m_testProviders )
2409 {
2410 if( name == prov->GetName() )
2411 return prov;
2412 }
2413
2414 return nullptr;
2415}
2416
2417
2418std::vector<BOARD_ITEM*> DRC_ENGINE::GetItemsMatchingCondition( const wxString& aExpression,
2419 DRC_CONSTRAINT_T aConstraint,
2420 REPORTER* aReporter )
2421{
2422 wxLogTrace( wxS( "KI_TRACE_DRC_RULE_EDITOR" ),
2423 wxS( "[ShowMatches] engine enter: expr='%s', constraint=%d" ), aExpression, (int) aConstraint );
2424 std::vector<BOARD_ITEM*> matches;
2425
2426 if( !m_board )
2427 return matches;
2428
2429 DRC_RULE_CONDITION condition( aExpression );
2430
2431 if( !condition.Compile( aReporter ? aReporter : m_logReporter ) )
2432 {
2433 wxLogTrace( wxS( "KI_TRACE_DRC_RULE_EDITOR" ), wxS( "[ShowMatches] engine: compile failed" ) );
2434 return matches;
2435 }
2436
2437 // Rebuild the from-to cache so that fromTo() expressions can be evaluated.
2438 // This cache requires explicit rebuilding before use since it depends on the full
2439 // connectivity graph being available.
2440 if( auto connectivity = m_board->GetConnectivity() )
2441 {
2442 if( auto ftCache = connectivity->GetFromToCache() )
2443 ftCache->Rebuild( m_board );
2444 }
2445
2446 BOARD_ITEM_SET items = m_board->GetItemSet();
2447 size_t totalItems = 0;
2448 size_t skippedItems = 0;
2449 size_t noLayerItems = 0;
2450 size_t checkedItems = 0;
2451
2452 for( auto& [kiid, item] : m_board->GetItemByIdCache() )
2453 {
2454 totalItems++;
2455
2456 // Skip items that don't have visible geometry or can't be meaningfully matched
2457 switch( item->Type() )
2458 {
2459 case PCB_NETINFO_T:
2460 case PCB_GENERATOR_T:
2461 case PCB_GROUP_T:
2462 skippedItems++;
2463 continue;
2464
2465 default:
2466 break;
2467 }
2468
2469 LSET itemLayers = item->GetLayerSet();
2470
2471 if( itemLayers.none() )
2472 {
2473 noLayerItems++;
2474 continue;
2475 }
2476
2477 checkedItems++;
2478 bool matched = false;
2479
2480 for( PCB_LAYER_ID layer : itemLayers )
2481 {
2482 if( condition.EvaluateFor( item, nullptr, static_cast<int>( aConstraint ), layer,
2483 aReporter ? aReporter : m_logReporter ) )
2484 {
2485 matches.push_back( item );
2486 wxLogTrace( wxS( "KI_TRACE_DRC_RULE_EDITOR" ),
2487 wxS( "[ShowMatches] engine: match type=%d kiid=%s layer=%d" ),
2488 (int) item->Type(), kiid.AsString(), (int) layer );
2489 matched = true;
2490 break; // No need to check other layers
2491 }
2492 }
2493
2494 // Log a few non-matching items to help debug condition issues
2495 if( !matched && matches.size() == 0 && checkedItems <= 5 )
2496 {
2497 wxLogTrace( wxS( "KI_TRACE_DRC_RULE_EDITOR" ),
2498 wxS( "[ShowMatches] engine: no-match sample type=%d kiid=%s layers=%s" ),
2499 (int) item->Type(), kiid.AsString(), itemLayers.FmtHex() );
2500 }
2501 }
2502
2503 wxLogTrace( wxS( "KI_TRACE_DRC_RULE_EDITOR" ),
2504 wxS( "[ShowMatches] engine stats: total=%zu skipped=%zu noLayer=%zu checked=%zu" ),
2505 totalItems, skippedItems, noLayerItems, checkedItems );
2506
2507 wxLogTrace( wxS( "KI_TRACE_DRC_RULE_EDITOR" ), wxS( "[ShowMatches] engine exit: total=%zu" ), matches.size() );
2508 return matches;
2509}
2510
2511
2513 wxString* aSource )
2514{
2515 DRC_OWN_CLEARANCE_CACHE_KEY key{ aItem->m_Uuid, aLayer };
2516
2517 // Fast path: check cache with shared (read) lock
2518 {
2519 std::shared_lock<std::shared_mutex> readLock( m_clearanceCacheMutex );
2520
2521 auto it = m_ownClearanceCache.find( key );
2522
2523 if( it != m_ownClearanceCache.end() )
2524 {
2525 // Cache hit. We don't cache the source string since it's rarely requested
2526 // and caching it would add complexity.
2527 return it->second;
2528 }
2529 }
2530
2531 // Cache miss - evaluate the constraint (outside lock to avoid blocking other threads)
2532 DRC_CONSTRAINT_T constraintType = CLEARANCE_CONSTRAINT;
2533
2534 if( aItem->Type() == PCB_PAD_T )
2535 {
2536 const PAD* pad = static_cast<const PAD*>( aItem );
2537
2538 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
2539 constraintType = HOLE_CLEARANCE_CONSTRAINT;
2540 }
2541
2542 DRC_CONSTRAINT constraint = EvalRules( constraintType, aItem, nullptr, aLayer );
2543
2544 int clearance = 0;
2545
2546 if( constraint.Value().HasMin() )
2547 {
2548 clearance = constraint.Value().Min();
2549
2550 if( aSource )
2551 *aSource = constraint.GetName();
2552 }
2553
2554 // Store in cache with exclusive (write) lock using double-checked locking
2555 {
2556 std::unique_lock<std::shared_mutex> writeLock( m_clearanceCacheMutex );
2557
2558 auto it = m_ownClearanceCache.find( key );
2559
2560 if( it == m_ownClearanceCache.end() )
2562 }
2563
2564 return clearance;
2565}
2566
2567
2569{
2570 std::unique_lock<std::shared_mutex> writeLock( m_clearanceCacheMutex );
2571
2572 if( m_board )
2573 {
2574 LSET copperLayers = m_board->GetEnabledLayers() & LSET::AllCuMask();
2575
2576 for( PCB_LAYER_ID layer : copperLayers.Seq() )
2577 m_ownClearanceCache.erase( DRC_OWN_CLEARANCE_CACHE_KEY{ aUuid, layer } );
2578 }
2579 else
2580 {
2581 auto it = m_ownClearanceCache.begin();
2582
2583 while( it != m_ownClearanceCache.end() )
2584 {
2585 if( it->first.m_uuid == aUuid )
2586 it = m_ownClearanceCache.erase( it );
2587 else
2588 ++it;
2589 }
2590 }
2591}
2592
2593
2595{
2596 std::unique_lock<std::shared_mutex> writeLock( m_clearanceCacheMutex );
2597 m_ownClearanceCache.clear();
2598}
2599
2600
2602{
2603 if( !m_board )
2604 return;
2605
2606 // Pre-populate the cache for all connected items to avoid delays during first render.
2607 // We only need to cache copper layers since clearance outlines are only drawn on copper.
2608
2609 LSET copperLayers = m_board->GetEnabledLayers() & LSET::AllCuMask();
2610
2611 using CLEARANCE_MAP = std::unordered_map<DRC_OWN_CLEARANCE_CACHE_KEY, int>;
2612
2613 // Build flat list of (item, layer) pairs to process.
2614 // Estimate size based on tracks + pads * average layers (2 for typical TH pads).
2615 std::vector<std::pair<const BOARD_ITEM*, PCB_LAYER_ID>> itemsToProcess;
2616 size_t estimatedPads = 0;
2617
2618 for( FOOTPRINT* footprint : m_board->Footprints() )
2619 estimatedPads += footprint->Pads().size();
2620
2621 itemsToProcess.reserve( m_board->Tracks().size() + estimatedPads * 2 );
2622
2623 for( PCB_TRACK* track : m_board->Tracks() )
2624 {
2625 if( track->Type() == PCB_VIA_T )
2626 {
2627 for( PCB_LAYER_ID layer : LSET( track->GetLayerSet() & copperLayers ).Seq() )
2628 itemsToProcess.emplace_back( track, layer );
2629 }
2630 else
2631 {
2632 itemsToProcess.emplace_back( track, track->GetLayer() );
2633 }
2634 }
2635
2636 for( FOOTPRINT* footprint : m_board->Footprints() )
2637 {
2638 for( PAD* pad : footprint->Pads() )
2639 {
2640 for( PCB_LAYER_ID layer : LSET( pad->GetLayerSet() & copperLayers ).Seq() )
2641 itemsToProcess.emplace_back( pad, layer );
2642 }
2643 }
2644
2645 if( itemsToProcess.empty() )
2646 return;
2647
2648 {
2649 std::unique_lock<std::shared_mutex> writeLock( m_clearanceCacheMutex );
2650 m_ownClearanceCache.reserve( itemsToProcess.size() );
2651 }
2652
2654
2655 auto processItems = [this]( size_t aStart, size_t aEnd,
2656 const std::vector<std::pair<const BOARD_ITEM*, PCB_LAYER_ID>>& aItems )
2657 -> CLEARANCE_MAP
2658 {
2659 CLEARANCE_MAP localCache;
2660
2661 for( size_t i = aStart; i < aEnd; ++i )
2662 {
2663 const BOARD_ITEM* item = aItems[i].first;
2664 PCB_LAYER_ID layer = aItems[i].second;
2665
2666 DRC_CONSTRAINT_T constraintType = CLEARANCE_CONSTRAINT;
2667
2668 if( item->Type() == PCB_PAD_T )
2669 {
2670 const PAD* pad = static_cast<const PAD*>( item );
2671
2672 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
2673 constraintType = HOLE_CLEARANCE_CONSTRAINT;
2674 }
2675
2676 DRC_CONSTRAINT constraint = EvalRules( constraintType, item, nullptr, layer );
2677
2678 int clearance = 0;
2679
2680 if( constraint.Value().HasMin() )
2681 clearance = constraint.Value().Min();
2682
2683 localCache[{ item->m_Uuid, layer }] = clearance;
2684 }
2685
2686 return localCache;
2687 };
2688
2689 auto results = tp.submit_blocks( 0, itemsToProcess.size(),
2690 [&]( size_t aStart, size_t aEnd ) -> CLEARANCE_MAP
2691 {
2692 return processItems( aStart, aEnd, itemsToProcess );
2693 } );
2694
2695 // Collect all results first WITHOUT holding the lock to avoid deadlock.
2696 // Worker threads call EvalRules() which needs a read lock on m_clearanceCacheMutex.
2697 // If we held a write lock while calling .get(), workers would block on the read lock
2698 // while we block waiting for them to complete.
2699 std::vector<CLEARANCE_MAP> collectedResults;
2700 collectedResults.reserve( results.size() );
2701
2702 for( size_t i = 0; i < results.size(); ++i )
2703 {
2704 if( results[i].valid() )
2705 collectedResults.push_back( results[i].get() );
2706 }
2707
2708 // Now merge with write lock held, but no blocking on futures
2709 {
2710 std::unique_lock<std::shared_mutex> writeLock( m_clearanceCacheMutex );
2711
2712 for( const auto& localCache : collectedResults )
2713 m_ownClearanceCache.insert( localCache.begin(), localCache.end() );
2714 }
2715}
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:2375
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:371
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:382
bool m_hasDiffPairClearanceOverrides
Definition drc_engine.h:394
bool m_testFootprints
Definition drc_engine.h:368
void addRule(std::shared_ptr< DRC_RULE > &rule)
Definition drc_engine.h:330
PROGRESS_REPORTER * m_progressReporter
Definition drc_engine.h:375
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:363
REPORTER * m_logReporter
Definition drc_engine.h:374
std::map< DRC_CONSTRAINT_T, std::vector< DRC_ENGINE_CONSTRAINT * > > m_explicitConstraints
Definition drc_engine.h:395
void compileRules()
std::set< int > QueryDistinctConstraints(DRC_CONSTRAINT_T aConstraintId)
bool m_hasGeometryDependentRules
Definition drc_engine.h:393
DS_PROXY_VIEW_ITEM * m_drawingSheet
Definition drc_engine.h:358
NETLIST * m_schematicNetlist
Definition drc_engine.h:359
std::shared_mutex m_clearanceCacheMutex
Definition drc_engine.h:391
bool KeepRefreshing(bool aWait=false)
BOARD * m_board
Definition drc_engine.h:357
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:367
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:392
std::vector< int > m_errorLimits
Definition drc_engine.h:365
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:373
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:361
bool QueryWorstConstraint(DRC_CONSTRAINT_T aRuleId, DRC_CONSTRAINT &aConstraint, bool aUnconditionalOnly=false)
bool IsCancelled() const
std::unordered_map< wxString, int > m_netclassClearances
Definition drc_engine.h:387
static bool IsNetADiffPair(BOARD *aBoard, NETINFO_ITEM *aNet, int &aNetP, int &aNetN)
bool IsNetTieExclusion(int aTrackNetCode, PCB_LAYER_ID aTrackLayer, const VECTOR2I &aCollisionPos, BOARD_ITEM *aCollidingItem)
Check if the given collision between a track and another item occurs during the track's entry into a ...
virtual ~DRC_ENGINE()
std::vector< BOARD_ITEM * > GetItemsMatchingCondition(const wxString &aExpression, DRC_CONSTRAINT_T aConstraint=ASSERTION_CONSTRAINT, REPORTER *aReporter=nullptr)
Evaluate a DRC condition against all board items and return matches.
void InitEngine(const wxFileName &aRulePath)
Initialize the DRC engine.
DRC_CONSTRAINT EvalZoneConnection(const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
static int MatchDpSuffix(const wxString &aNetName, wxString &aComplementNet, wxString &aBaseDpName)
Check if the given net is a diff pair, returning its polarity and complement if so.
bool ReportPhase(const wxString &aMessage)
void InitializeClearanceCache()
Initialize the clearance cache for all items on the board.
bool m_rulesValid
Definition drc_engine.h:362
void InvalidateClearanceCache(const KIID &aUuid)
Invalidate the clearance cache for a specific item.
BOARD_DESIGN_SETTINGS * m_designSettings
Definition drc_engine.h:356
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:366
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:527
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 & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:608
static const LSET & BackMask()
Return a mask holding all technical layers and the external CU layer on back side.
Definition lset.cpp:729
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition lset.cpp:313
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition lset.cpp:188
T Min() const
Definition minoptmax.h:33
void SetMin(T v)
Definition minoptmax.h:41
void SetOpt(T v)
Definition minoptmax.h:43
bool HasMin() const
Definition minoptmax.h:37
void SetMax(T v)
Definition minoptmax.h:42
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h: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:122
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:348
DRC_RULE_CONDITION * condition
Definition drc_engine.h:347
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