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