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