KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcbexpr_functions.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <algorithm>
21#include <cstdio>
22#include <memory>
23#include <mutex>
24#include <set>
25
26#include <wx/log.h>
27
28#include <board.h>
32#include <drc/drc_rtree.h>
33#include <drc/drc_engine.h>
34#include <footprint.h>
36#include <lset.h>
37#include <pad.h>
38#include <pcb_track.h>
39#include <pcb_group.h>
41#include <pcbexpr_evaluator.h>
45#include <properties/property.h>
47
48
49bool fromToFunc( LIBEVAL::CONTEXT* aCtx, void* self )
50{
51 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
52 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
54 LIBEVAL::VALUE* argTo = aCtx->Pop();
55 LIBEVAL::VALUE* argFrom = aCtx->Pop();
56
57 result->Set(0.0);
58 aCtx->Push( result );
59
60 if( !item )
61 return false;
62
63 if( !argFrom || argFrom->AsString().IsEmpty() )
64 {
65 if( aCtx->HasErrorCallback() )
66 {
67 aCtx->ReportError( wxString::Format( _( "Missing 'from' pad argument (footprint reference designator "
68 "followed by hyphen and pad number) to %s." ),
69 wxT( "fromTo()" ) ) );
70 }
71
72 return false;
73 }
74
75 if( !argTo || argTo->AsString().IsEmpty() )
76 {
77 if( aCtx->HasErrorCallback() )
78 {
79 aCtx->ReportError( wxString::Format( _( "Missing 'to' pad argument (footprint reference designator "
80 "followed by hyphen and pad number) to %s." ),
81 wxT( "fromTo()" ) ) );
82 }
83
84 return false;
85 }
86
87 auto ftCache = item->GetBoard()->GetConnectivity()->GetFromToCache();
88
89 if( !ftCache )
90 {
91 wxLogWarning( wxT( "Attempting to call fromTo() with non-existent from-to cache." ) );
92 return true;
93 }
94
95 if( ftCache->IsOnFromToPath( static_cast<BOARD_CONNECTED_ITEM*>( item ), argFrom->AsString(), argTo->AsString() ) )
96 result->Set(1.0);
97
98 return true;
99}
100
101
102static void existsOnLayerFunc( LIBEVAL::CONTEXT* aCtx, void *self )
103{
104 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
105 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
106 LIBEVAL::VALUE* arg = aCtx->Pop();
108
109 result->Set( 0.0 );
110 aCtx->Push( result );
111
112 if( !item )
113 return;
114
115 if( !arg || arg->AsString().IsEmpty() )
116 {
117 if( aCtx->HasErrorCallback() )
118 {
119 aCtx->ReportError( wxString::Format( _( "Missing layer name argument to %s." ),
120 wxT( "existsOnLayer()" ) ) );
121 }
122
123 return;
124 }
125
126 result->SetDeferredEval(
127 [item, arg, aCtx]() -> double
128 {
129 const wxString& layerName = arg->AsString();
130 wxPGChoices& layerMap = ENUM_MAP<PCB_LAYER_ID>::Instance().Choices();
131
132 if( aCtx->HasErrorCallback())
133 {
134 /*
135 * Interpreted version
136 */
137
138 bool anyMatch = false;
139
140 for( unsigned ii = 0; ii < layerMap.GetCount(); ++ii )
141 {
142 wxPGChoiceEntry& entry = layerMap[ ii ];
143
144 if( entry.GetText().Matches( layerName ))
145 {
146 anyMatch = true;
147
148 if( item->IsOnLayer( ToLAYER_ID( entry.GetValue() ) ) )
149 return 1.0;
150 }
151 }
152
153 if( !anyMatch )
154 {
155 aCtx->ReportError( wxString::Format( _( "Unrecognized layer '%s'" ),
156 layerName ) );
157 }
158
159 return 0.0;
160 }
161 else
162 {
163 /*
164 * Compiled version
165 */
166
167 BOARD* board = item->GetBoard();
168
169 {
170 std::shared_lock<std::shared_mutex> readLock( board->m_CachesMutex );
171
172 auto i = board->m_LayerExpressionCache.find( layerName );
173
174 if( i != board->m_LayerExpressionCache.end() )
175 return ( item->GetLayerSet() & i->second ).any() ? 1.0 : 0.0;
176 }
177
178 LSET mask;
179
180 for( unsigned ii = 0; ii < layerMap.GetCount(); ++ii )
181 {
182 wxPGChoiceEntry& entry = layerMap[ ii ];
183
184 if( entry.GetText().Matches( layerName ) )
185 mask.set( ToLAYER_ID( entry.GetValue() ) );
186 }
187
188 {
189 std::unique_lock<std::shared_mutex> writeLock( board->m_CachesMutex );
190 board->m_LayerExpressionCache[ layerName ] = mask;
191 }
192
193 return ( item->GetLayerSet() & mask ).any() ? 1.0 : 0.0;
194 }
195 } );
196}
197
198
199static void isPlatedFunc( LIBEVAL::CONTEXT* aCtx, void* self )
200{
202
203 result->Set( 0.0 );
204 aCtx->Push( result );
205
206 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
207 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
208
209 if( !item )
210 return;
211
212 if( item->Type() == PCB_PAD_T && static_cast<PAD*>( item )->GetAttribute() == PAD_ATTRIB::PTH )
213 result->Set( 1.0 );
214 else if( item->Type() == PCB_VIA_T )
215 result->Set( 1.0 );
216}
217
218
219bool collidesWithCourtyard( BOARD_ITEM* aItem, std::shared_ptr<SHAPE>& aItemShape,
220 PCBEXPR_CONTEXT* aCtx, FOOTPRINT* aFootprint, PCB_LAYER_ID aSide )
221{
222 const SHAPE_POLY_SET& footprintCourtyard = aFootprint->GetCourtyard( aSide );
223
224 if( footprintCourtyard.OutlineCount() == 0 )
225 return false;
226
227 // Broad phase before the polygon-level Collide, which dominates when a rule tests a
228 // courtyard against many items (intersectsCourtyard('*') over a full board). A bbox miss
229 // cannot collide, so the expensive shape build and Collide are skipped.
230 if( !footprintCourtyard.BBox().Intersects( aItem->GetBoundingBox() ) )
231 return false;
232
233 if( aItemShape )
234 {
235 return footprintCourtyard.Collide( aItemShape.get() );
236 }
237 else if( ZONE* zone = dynamic_cast<ZONE*>( aItem ) )
238 {
239 // Since rules are used for zone filling we can't rely on the filled shapes. Use the
240 // zone outline instead.
241 SHAPE_POLY_SET zoneOutlineStorage;
242 SHAPE_POLY_SET* zoneOutline = &zoneOutlineStorage;
243
244 if( zone->GetParentFootprint() )
245 zoneOutlineStorage = zone->GetBoardOutline();
246 else
247 zoneOutline = zone->Outline();
248
249 return footprintCourtyard.Collide( zoneOutline );
250 }
251 else
252 {
253 return footprintCourtyard.Collide( aItem->GetEffectiveShape( aCtx->GetLayer() ).get() );
254 }
255};
256
257
258static bool testFootprintSelector( FOOTPRINT* aFp, const wxString& aSelector )
259{
260 // NOTE: This code may want to be somewhat more generalized, but for now it's implemented
261 // here to support functions like insersectsCourtyard where we want multiple ways to search
262 // for the footprints in question.
263 // If support for text variable replacement is added, it should happen before any other
264 // logic here, so that people can use text variables to contain references or LIBIDs.
265 // (see: https://gitlab.com/kicad/code/kicad/-/issues/11231)
266
267 if( aSelector.IsEmpty() )
268 return false;
269
270 // First check if we have a known directive
271 if( aSelector[0] == '$' && aSelector.Last() == '}' && aSelector.Upper().StartsWith( wxT( "${CLASS:" ) ) )
272 {
273 wxString name = aSelector.Mid( 8, aSelector.Length() - 9 );
274
275 const COMPONENT_CLASS* compClass = aFp->GetComponentClass();
276
277 if( compClass && compClass->ContainsClassName( name ) )
278 return true;
279 }
280 else if( aFp->GetReference().Matches( aSelector ) )
281 {
282 return true;
283 }
284 else if( aSelector.Find( ':' ) != wxNOT_FOUND && aFp->GetFPIDAsString().Matches( aSelector ) )
285 {
286 return true;
287 }
288
289 return false;
290}
291
292
293/*
294 * Find footprints relevant to a courtyard-intersection predicate. "A"/"B" resolve to the items
295 * under test; any other selector is matched against the footprints whose courtyard can actually
296 * reach aItem, found via the spatial index rather than a full-board scan. A footprint the index
297 * skips would fail the same bbox test collidesWithCourtyard() applies, so the result matches a
298 * linear scan.
299 */
300static bool searchFootprintsNearItem( BOARD* aBoard, const wxString& aArg, PCBEXPR_CONTEXT* aCtx,
301 BOARD_ITEM* aItem, const std::function<bool( FOOTPRINT* )>& aFunc )
302{
303 if( aArg == wxT( "A" ) )
304 {
305 FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( aCtx->GetItem( 0 ) );
306 return fp && aFunc( fp );
307 }
308 else if( aArg == wxT( "B" ) )
309 {
310 FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( aCtx->GetItem( 1 ) );
311 return fp && aFunc( fp );
312 }
313
314 bool found = false;
315
316 // Hold the index alive for the whole query; a concurrent IncrementTimeStamp() may detach the
317 // board's copy while we iterate.
318 std::shared_ptr<const FOOTPRINT_COURTYARD_INDEX> index = aBoard->GetFootprintCourtyardIndex();
319
320 index->QueryOverlapping( aItem->GetBoundingBox(),
321 [&]( FOOTPRINT* fp ) -> bool
322 {
323 if( testFootprintSelector( fp, aArg ) && aFunc( fp ) )
324 {
325 found = true;
326 return false;
327 }
328
329 return true;
330 } );
331
332 return found;
333}
334
335
336#define MISSING_FP_ARG( f ) \
337 wxString::Format( _( "Missing footprint argument (A, B, or reference designator) to %s." ), f )
338
339static void intersectsCourtyardFunc( LIBEVAL::CONTEXT* aCtx, void* self )
340{
341 PCBEXPR_CONTEXT* context = static_cast<PCBEXPR_CONTEXT*>( aCtx );
342 LIBEVAL::VALUE* arg = context->Pop();
343 LIBEVAL::VALUE* result = context->AllocValue();
344
345 result->Set( 0.0 );
346 context->Push( result );
347
348 if( !arg || arg->AsString().IsEmpty() )
349 {
350 if( context->HasErrorCallback() )
351 context->ReportError( MISSING_FP_ARG( wxT( "intersectsCourtyard()" ) ) );
352
353 return;
354 }
355
356 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
357 BOARD_ITEM* item = vref ? vref->GetObject( context ) : nullptr;
358
359 if( !item )
360 return;
361
362 result->SetDeferredEval(
363 [item, arg, context]() -> double
364 {
365 BOARD* board = item->GetBoard();
366 bool transient = ( item->GetFlags() & ROUTER_TRANSIENT ) != 0;
367 const wxString selector = arg->AsString();
368
369 // Whole-predicate memo: the same condition repeated across many rules resolves
370 // in O(1) here instead of re-scanning every footprint. "A"/"B" select the other
371 // item of the current pair rather than a board-wide set, so they cannot be keyed
372 // by item alone (and touch only one footprint anyway); skip the memo for those.
373 bool memoize = !transient && selector != wxT( "A" ) && selector != wxT( "B" );
374
375 ITEM_SELECTOR_LAYER_CACHE_KEY rkey{ item, selector, context->GetLayer(),
376 context->GetConstraint() };
377 bool whole = false;
378
379 if( memoize && board->m_IntersectsCourtyardResultCache.Get( rkey, whole ) )
380 return whole ? 1.0 : 0.0;
381
382 std::shared_ptr<SHAPE> itemShape;
383
384 bool res = searchFootprintsNearItem( board, selector, context, item,
385 [&]( FOOTPRINT* fp )
386 {
387 PTR_PTR_CACHE_KEY key = { fp, item };
388 bool cached = false;
389
390 if( !transient && board->m_IntersectsCourtyardCache.Get( key, cached ) )
391 return cached;
392
393 bool hit = collidesWithCourtyard( item, itemShape, context, fp, F_Cu )
394 || collidesWithCourtyard( item, itemShape, context, fp, B_Cu );
395
396 if( !transient )
397 board->m_IntersectsCourtyardCache.Set( key, hit );
398
399 return hit;
400 } );
401
402 if( memoize )
404
405 if( res )
406 {
407 return 1.0;
408 }
409
410 return 0.0;
411 } );
412}
413
414
415static void intersectsFrontCourtyardFunc( LIBEVAL::CONTEXT* aCtx, void* self )
416{
417 PCBEXPR_CONTEXT* context = static_cast<PCBEXPR_CONTEXT*>( aCtx );
418 LIBEVAL::VALUE* arg = context->Pop();
419 LIBEVAL::VALUE* result = context->AllocValue();
420
421 result->Set( 0.0 );
422 context->Push( result );
423
424 if( !arg || arg->AsString().IsEmpty() )
425 {
426 if( context->HasErrorCallback() )
427 context->ReportError( MISSING_FP_ARG( wxT( "intersectsFrontCourtyard()" ) ) );
428
429 return;
430 }
431
432 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
433 BOARD_ITEM* item = vref ? vref->GetObject( context ) : nullptr;
434
435 if( !item )
436 return;
437
438 result->SetDeferredEval(
439 [item, arg, context]() -> double
440 {
441 BOARD* board = item->GetBoard();
442 bool transient = ( item->GetFlags() & ROUTER_TRANSIENT ) != 0;
443 const wxString selector = arg->AsString();
444
445 // See intersectsCourtyard: "A"/"B" are pair-relative and not memoizable here.
446 bool memoize = !transient && selector != wxT( "A" ) && selector != wxT( "B" );
447
448 ITEM_SELECTOR_LAYER_CACHE_KEY rkey{ item, selector, context->GetLayer(),
449 context->GetConstraint() };
450 bool whole = false;
451
452 if( memoize && board->m_IntersectsFCourtyardResultCache.Get( rkey, whole ) )
453 return whole ? 1.0 : 0.0;
454
455 std::shared_ptr<SHAPE> itemShape;
456
457 bool res = searchFootprintsNearItem( board, selector, context, item,
458 [&]( FOOTPRINT* fp )
459 {
460 PTR_PTR_CACHE_KEY key = { fp, item };
461 bool cached = false;
462
463 if( !transient && board->m_IntersectsFCourtyardCache.Get( key, cached ) )
464 return cached;
465
466 PCB_LAYER_ID layerId = fp->IsFlipped() ? B_Cu : F_Cu;
467
468 bool hit = collidesWithCourtyard( item, itemShape, context, fp, layerId );
469
470 if( !transient )
471 board->m_IntersectsFCourtyardCache.Set( key, hit );
472
473 return hit;
474 } );
475
476 if( memoize )
478
479 return res ? 1.0 : 0.0;
480 } );
481}
482
483
484static void intersectsBackCourtyardFunc( LIBEVAL::CONTEXT* aCtx, void* self )
485{
486 PCBEXPR_CONTEXT* context = static_cast<PCBEXPR_CONTEXT*>( aCtx );
487 LIBEVAL::VALUE* arg = context->Pop();
488 LIBEVAL::VALUE* result = context->AllocValue();
489
490 result->Set( 0.0 );
491 context->Push( result );
492
493 if( !arg || arg->AsString().IsEmpty() )
494 {
495 if( context->HasErrorCallback() )
496 context->ReportError( MISSING_FP_ARG( wxT( "intersectsBackCourtyard()" ) ) );
497
498 return;
499 }
500
501 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
502 BOARD_ITEM* item = vref ? vref->GetObject( context ) : nullptr;
503
504 if( !item )
505 return;
506
507 result->SetDeferredEval(
508 [item, arg, context]() -> double
509 {
510 BOARD* board = item->GetBoard();
511 bool transient = ( item->GetFlags() & ROUTER_TRANSIENT ) != 0;
512 const wxString selector = arg->AsString();
513
514 // See intersectsCourtyard: "A"/"B" are pair-relative and not memoizable here.
515 bool memoize = !transient && selector != wxT( "A" ) && selector != wxT( "B" );
516
517 ITEM_SELECTOR_LAYER_CACHE_KEY rkey{ item, selector, context->GetLayer(),
518 context->GetConstraint() };
519 bool whole = false;
520
521 if( memoize && board->m_IntersectsBCourtyardResultCache.Get( rkey, whole ) )
522 return whole ? 1.0 : 0.0;
523
524 std::shared_ptr<SHAPE> itemShape;
525
526 bool res = searchFootprintsNearItem( board, selector, context, item,
527 [&]( FOOTPRINT* fp )
528 {
529 PTR_PTR_CACHE_KEY key = { fp, item };
530 bool cached = false;
531
532 if( !transient && board->m_IntersectsBCourtyardCache.Get( key, cached ) )
533 return cached;
534
535 PCB_LAYER_ID layerId = fp->IsFlipped() ? F_Cu : B_Cu;
536
537 bool hit = collidesWithCourtyard( item, itemShape, context, fp, layerId );
538
539 if( !transient )
540 board->m_IntersectsBCourtyardCache.Set( key, hit );
541
542 return hit;
543 } );
544
545 if( memoize )
547
548 return res ? 1.0 : 0.0;
549 } );
550}
551
552
554{
555 // Check cache first with read lock
556 {
557 std::shared_lock<std::shared_mutex> readLock( aBoard->m_CachesMutex );
558 auto it = aBoard->m_DeflatedZoneOutlineCache.find( aArea );
559
560 if( it != aBoard->m_DeflatedZoneOutlineCache.end() )
561 return it->second;
562 }
563
564 // Cache miss - compute deflated outline
565 SHAPE_POLY_SET areaOutline = aArea->GetBoardOutline();
566 areaOutline.ClearArcs();
568 ARC_LOW_DEF );
569
570 // Store in cache
571 {
572 std::unique_lock<std::shared_mutex> writeLock( aBoard->m_CachesMutex );
573 aBoard->m_DeflatedZoneOutlineCache[aArea] = areaOutline;
574 }
575
576 return areaOutline;
577}
578
579
580bool collidesWithArea( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer, PCBEXPR_CONTEXT* aCtx, ZONE* aArea, bool aForKeepout )
581{
582 BOARD* board = aArea->GetBoard();
583 BOX2I areaBBox = aArea->GetBoundingBox();
584
585 // Get cached deflated outline. Collisions include touching, so we need to deflate outline
586 // by enough to exclude it. This is particularly important for detecting copper fills as
587 // they will be exactly touching along the entire exclusion border.
588 SHAPE_POLY_SET areaOutline = getDeflatedZoneOutline( board, aArea );
589
590 if( aItem->GetFlags() & HOLE_PROXY )
591 {
592 if( aItem->Type() == PCB_PAD_T )
593 {
594 return areaOutline.Collide( aItem->GetEffectiveHoleShape().get() );
595 }
596 else if( aItem->Type() == PCB_VIA_T )
597 {
598 LSET overlap = aItem->GetLayerSet() & aArea->GetLayerSet();
599
601 if( overlap.any() )
602 {
603 if( aCtx->GetLayer() == UNDEFINED_LAYER || overlap.Contains( aCtx->GetLayer() ) )
604 return areaOutline.Collide( aItem->GetEffectiveHoleShape().get() );
605 }
606 }
607
608 return false;
609 }
610
611 if( aItem->Type() == PCB_FOOTPRINT_T )
612 {
613 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem );
614
615 if( ( footprint->GetFlags() & MALFORMED_COURTYARDS ) != 0 )
616 {
617 if( aCtx->HasErrorCallback() )
618 aCtx->ReportError( _( "Footprint's courtyard is not a single, closed shape." ) );
619
620 return false;
621 }
622
623 if( ( aArea->GetLayerSet() & LSET::FrontMask() ).any() )
624 {
625 const SHAPE_POLY_SET& courtyard = footprint->GetCourtyard( F_CrtYd );
626
627 if( courtyard.OutlineCount() == 0 )
628 {
629 if( aCtx->HasErrorCallback() )
630 aCtx->ReportError( _( "Footprint has no front courtyard." ) );
631 }
632 else if( areaOutline.Collide( &courtyard.Outline( 0 ) ) )
633 {
634 return true;
635 }
636 }
637
638 if( ( aArea->GetLayerSet() & LSET::BackMask() ).any() )
639 {
640 const SHAPE_POLY_SET& courtyard = footprint->GetCourtyard( B_CrtYd );
641
642 if( courtyard.OutlineCount() == 0 )
643 {
644 if( aCtx->HasErrorCallback() )
645 aCtx->ReportError( _( "Footprint has no back courtyard." ) );
646 }
647 else if( areaOutline.Collide( &courtyard.Outline( 0 ) ) )
648 {
649 return true;
650 }
651 }
652
653 return false;
654 }
655 else if( aItem->Type() == PCB_ZONE_T )
656 {
657 ZONE* zone = static_cast<ZONE*>( aItem );
658
659 if( aForKeepout )
660 {
661 if( !zone->IsFilled() )
662 return false;
663
664 if( DRC_RTREE* zoneRTree = board->m_CopperZoneRTreeCache[ zone ].get() )
665 {
666 if( zoneRTree->QueryColliding( areaBBox, &areaOutline, aLayer ) )
667 return true;
668 }
669 else
670 {
671 std::unique_ptr<DRC_RTREE> rtree = std::make_unique<DRC_RTREE>();
672 rtree->Insert( zone, aLayer, CLEARANCE_CONSTRAINT );
673 rtree->Build();
674
675 if( rtree->QueryColliding( areaBBox, &areaOutline, aLayer ) )
676 return true;
677 }
678
679 return false;
680 }
681 else
682 {
683 SHAPE_POLY_SET zonePolyStorage;
684 SHAPE_POLY_SET* zonePoly = &zonePolyStorage;
685
686 // GetBoardOutline() is expensive. Only use it where we have to.
687 if( zone->GetParentFootprint() )
688 zonePolyStorage = zone->GetBoardOutline();
689 else
690 zonePoly = zone->Outline();
691
692 return areaOutline.Collide( zonePoly );
693 }
694 }
695 else
696 {
697 if( !aArea->GetLayerSet().Contains( aLayer ) )
698 return false;
699
700 return areaOutline.Collide( aItem->GetEffectiveShape( aLayer ).get() );
701 }
702}
703
704
705bool searchAreas( BOARD* aBoard, const wxString& aArg, PCBEXPR_CONTEXT* aCtx,
706 const std::function<bool( ZONE* )>& aFunc )
707{
708 if( aArg == wxT( "A" ) )
709 {
710 return aFunc( dynamic_cast<ZONE*>( aCtx->GetItem( 0 ) ) );
711 }
712 else if( aArg == wxT( "B" ) )
713 {
714 return aFunc( dynamic_cast<ZONE*>( aCtx->GetItem( 1 ) ) );
715 }
716 else if( KIID::SniffTest( aArg ) )
717 {
718 KIID target( aArg );
719
720 // Use the board's item-by-ID cache for O(1) lookup instead of O(n) iteration.
721 // The cache includes both board zones and zones inside footprints.
722 const auto& cache = aBoard->GetItemByIdCache();
723 auto it = cache.find( target );
724
725 if( it != cache.end() && it->second->Type() == PCB_ZONE_T )
726 return aFunc( static_cast<ZONE*>( it->second ) );
727
728 return false;
729 }
730 else // Match on zone name
731 {
732 // Use cached zone name lookup to avoid O(n) iteration through all zones for each call.
733 // This is a significant performance improvement for boards with many area-based DRC rules.
734 std::vector<ZONE*> matchingZones;
735 bool cacheHit = false;
736
737 {
738 std::shared_lock<std::shared_mutex> readLock( aBoard->m_CachesMutex );
739 auto it = aBoard->m_ZonesByNameCache.find( aArg );
740
741 if( it != aBoard->m_ZonesByNameCache.end() )
742 {
743 matchingZones = it->second;
744 cacheHit = true;
745 }
746 }
747
748 if( !cacheHit )
749 {
750 for( ZONE* area : aBoard->Zones() )
751 {
752 if( area->GetZoneName().Matches( aArg ) )
753 matchingZones.push_back( area );
754 }
755
756 for( FOOTPRINT* footprint : aBoard->Footprints() )
757 {
758 for( ZONE* area : footprint->Zones() )
759 {
760 if( area->GetZoneName().Matches( aArg ) )
761 matchingZones.push_back( area );
762 }
763 }
764
765 // Store in cache for future lookups
766 {
767 std::unique_lock<std::shared_mutex> writeLock( aBoard->m_CachesMutex );
768 aBoard->m_ZonesByNameCache[aArg] = matchingZones;
769 }
770 }
771
772 for( ZONE* area : matchingZones )
773 {
774 if( aFunc( area ) )
775 return true;
776 }
777
778 return false;
779 }
780}
781
782
784{
785public:
787 {
788 m_item = aItem;
789 m_layers = aItem->GetLayerSet();
790 }
791
793 {
794 m_item->SetLayerSet( m_layers );
795 }
796
797 void Add( PCB_LAYER_ID aLayer )
798 {
799 m_item->SetLayerSet( m_item->GetLayerSet().set( aLayer ) );
800 }
801
802private:
805};
806
807
808#define MISSING_AREA_ARG( f ) \
809 wxString::Format( _( "Missing rule-area argument (A, B, or rule-area name) to %s." ), f )
810
811static void doIntersectsAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self, bool aForKeepout )
812{
813 PCBEXPR_CONTEXT* context = static_cast<PCBEXPR_CONTEXT*>( aCtx );
814 LIBEVAL::VALUE* arg = aCtx->Pop();
816
817 result->Set( 0.0 );
818 aCtx->Push( result );
819
820 if( !arg || arg->AsString().IsEmpty() )
821 {
822 if( aCtx->HasErrorCallback() )
823 {
824 if( aForKeepout )
825 aCtx->ReportError( MISSING_AREA_ARG( wxT( "intersectsKeepout()" ) ) );
826 else
827 aCtx->ReportError( MISSING_AREA_ARG( wxT( "intersectsArea()" ) ) );
828 }
829
830 return;
831 }
832
833 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
834 BOARD_ITEM* item = vref ? vref->GetObject( context ) : nullptr;
835
836 if( !item )
837 return;
838
839 result->SetDeferredEval(
840 [item, arg, context, aForKeepout]() -> double
841 {
842 BOARD* board = item->GetBoard();
843 PCB_LAYER_ID aLayer = context->GetLayer();
844 bool transient = ( item->GetFlags() & ROUTER_TRANSIENT ) != 0;
845 const wxString selector = arg->AsString();
846
847 auto& resultsCache = aForKeepout ? board->m_IntersectsKeepoutResultCache
849
850 auto& intersectsCache = aForKeepout ? board->m_IntersectsKeepoutCache
851 : board->m_IntersectsAreaCache;
852
853 // See intersectsCourtyard: "A"/"B" are pair-relative and not memoizable here.
854 bool memoize = !transient && selector != wxT( "A" ) && selector != wxT( "B" );
855 bool whole = false;
856
857 if( memoize && resultsCache.Get( { item, selector, aLayer, context->GetConstraint() }, whole ) )
858 return whole ? 1.0 : 0.0;
859
860 BOX2I itemBBox = item->GetBoundingBox();
861
862 bool res = searchAreas( board, selector, context,
863 [&]( ZONE* aArea )
864 {
865 if( !aArea || aArea == item || aArea->GetParent() == item )
866 return false;
867
868 SCOPED_LAYERSET scopedLayerSet( aArea );
869
870 if( context->GetConstraint() == SILK_CLEARANCE_CONSTRAINT )
871 {
872 // Silk clearance tests are run across layer pairs
873 if( ( aArea->IsOnLayer( F_SilkS ) && IsFrontLayer( aLayer ) )
874 || ( aArea->IsOnLayer( B_SilkS ) && IsBackLayer( aLayer ) ) )
875 {
876 scopedLayerSet.Add( aLayer );
877 }
878 }
879
880 LSET commonLayers = aArea->GetLayerSet() & item->GetLayerSet();
881
882 if( !commonLayers.any() )
883 return false;
884
885 if( !aArea->GetBoundingBox().Intersects( itemBBox ) )
886 return false;
887
888 LSET testLayers;
889
890 if( aLayer != UNDEFINED_LAYER )
891 testLayers.set( aLayer );
892 else
893 testLayers = commonLayers;
894
895 bool isTransient = ( item->GetFlags() & ROUTER_TRANSIENT ) != 0;
896 std::vector<PCB_LAYER_ID> layersToCompute;
897
898 if( !isTransient )
899 {
900 for( PCB_LAYER_ID layer : testLayers.UIOrder() )
901 {
902 bool cached = false;
903
904 if( intersectsCache.Get( { aArea, item, layer }, cached ) )
905 {
906 if( cached )
907 return true;
908 }
909 else
910 {
911 layersToCompute.push_back( layer );
912 }
913 }
914 }
915 else
916 {
917 for( PCB_LAYER_ID layer : testLayers.UIOrder() )
918 layersToCompute.push_back( layer );
919 }
920
921 bool anyCollision = false;
922
923 for( PCB_LAYER_ID layer : layersToCompute )
924 {
925 bool collides = collidesWithArea( item, layer, context, aArea, aForKeepout );
926
927 if( !isTransient )
928 intersectsCache.Set( { aArea, item, layer }, collides );
929
930 if( collides )
931 anyCollision = true;
932 }
933
934 return anyCollision;
935 } );
936
937 if( memoize )
938 resultsCache.Set( { item, selector, aLayer, context->GetConstraint() }, res );
939
940 return res ? 1.0 : 0.0;
941 } );
942}
943
944
945static void intersectsKeepoutFunc( LIBEVAL::CONTEXT* aCtx, void* self )
946{
947 doIntersectsAreaFunc( aCtx, self, true );
948}
949
950
951static void intersectsAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
952{
953 doIntersectsAreaFunc( aCtx, self, false );
954}
955
956
957static void enclosedByAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
958{
959 PCBEXPR_CONTEXT* context = static_cast<PCBEXPR_CONTEXT*>( aCtx );
960 LIBEVAL::VALUE* arg = aCtx->Pop();
962
963 result->Set( 0.0 );
964 aCtx->Push( result );
965
966 if( !arg || arg->AsString().IsEmpty() )
967 {
968 if( aCtx->HasErrorCallback() )
969 aCtx->ReportError( MISSING_AREA_ARG( wxT( "enclosedByArea()" ) ) );
970
971 return;
972 }
973
974 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
975 BOARD_ITEM* item = vref ? vref->GetObject( context ) : nullptr;
976
977 if( !item )
978 return;
979
980 result->SetDeferredEval(
981 [item, arg, context]() -> double
982 {
983 BOARD* board = item->GetBoard();
984 int maxError = board->GetDesignSettings().m_MaxError;
985 PCB_LAYER_ID layer = context->GetLayer();
986 bool transient = ( item->GetFlags() & ROUTER_TRANSIENT ) != 0;
987 const wxString selector = arg->AsString();
988
989 // See intersectsCourtyard: "A"/"B" are pair-relative and not memoizable here.
990 bool memoize = !transient && selector != wxT( "A" ) && selector != wxT( "B" );
991
992 ITEM_SELECTOR_LAYER_CACHE_KEY rkey{ item, selector, layer, context->GetConstraint() };
993 bool whole = false;
994
995 if( memoize && board->m_EnclosedByAreaResultCache.Get( rkey, whole ) )
996 return whole ? 1.0 : 0.0;
997
998 BOX2I itemBBox = item->GetBoundingBox();
999
1000 bool res = searchAreas( board, selector, context,
1001 [&]( ZONE* aArea )
1002 {
1003 if( !aArea || aArea == item || aArea->GetParent() == item )
1004 return false;
1005
1006 if( item->Type() != PCB_FOOTPRINT_T )
1007 {
1008 if( !( aArea->GetLayerSet() & item->GetLayerSet() ).any() )
1009 return false;
1010 }
1011
1012 if( !aArea->GetBoundingBox().Intersects( itemBBox ) )
1013 return false;
1014
1015 PTR_PTR_LAYER_CACHE_KEY key = { aArea, item, layer };
1016 bool cached = false;
1017
1018 if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0
1019 && board->m_EnclosedByAreaCache.Get( key, cached ) )
1020 {
1021 return cached;
1022 }
1023
1024 SHAPE_POLY_SET itemShape;
1025 bool enclosedByArea = false;
1026
1027 if( item->Type() == PCB_ZONE_T )
1028 {
1029 itemShape = static_cast<ZONE*>( item )->GetBoardOutline();
1030 }
1031 else if( item->Type() == PCB_FOOTPRINT_T )
1032 {
1033 FOOTPRINT* fp = static_cast<FOOTPRINT*>( item );
1034
1035 for( PCB_LAYER_ID testLayer : aArea->GetLayerSet() )
1036 {
1037 fp->TransformPadsToPolySet( itemShape, testLayer, 0, maxError, ERROR_OUTSIDE );
1038 fp->TransformFPShapesToPolySet( itemShape, testLayer, 0, maxError, ERROR_OUTSIDE );
1039 }
1040 }
1041 else
1042 {
1043 item->TransformShapeToPolygon( itemShape, layer, 0, maxError, ERROR_OUTSIDE );
1044 }
1045
1046 if( itemShape.IsEmpty() )
1047 {
1048 // If it's already empty then our test will have no meaning.
1049 enclosedByArea = false;
1050 }
1051 else
1052 {
1053 SHAPE_POLY_SET areaOutlineStorage;
1054 SHAPE_POLY_SET* areaOutline = &areaOutlineStorage;
1055
1056 // GetBoardOutline() is expensive. Only use it where we have to.
1057 if( aArea->GetParentFootprint() )
1058 areaOutlineStorage = aArea->GetBoardOutline();
1059 else
1060 areaOutline = aArea->Outline();
1061
1062 itemShape.ClearArcs();
1063 itemShape.BooleanSubtract( *areaOutline );
1064
1065 enclosedByArea = itemShape.IsEmpty();
1066 }
1067
1068 if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
1069 board->m_EnclosedByAreaCache.Set( key, enclosedByArea );
1070
1071 return enclosedByArea;
1072 } );
1073
1074 if( memoize )
1075 board->m_EnclosedByAreaResultCache.Set( rkey, res );
1076
1077 return res ? 1.0 : 0.0;
1078 } );
1079}
1080
1081
1082static void memberOfGroupFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1083{
1084 LIBEVAL::VALUE* arg = aCtx->Pop();
1085 LIBEVAL::VALUE* result = aCtx->AllocValue();
1086
1087 result->Set( 0.0 );
1088 aCtx->Push( result );
1089
1090 if( !arg || arg->AsString().IsEmpty() )
1091 {
1092 if( aCtx->HasErrorCallback() )
1093 {
1094 aCtx->ReportError( wxString::Format( _( "Missing group name argument to %s." ),
1095 wxT( "memberOfGroup()" ) ) );
1096 }
1097
1098 return;
1099 }
1100
1101 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1102 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1103
1104 if( !item )
1105 return;
1106
1107 result->SetDeferredEval(
1108 [item, arg]() -> double
1109 {
1110 EDA_GROUP* group = item->GetParentGroup();
1111
1112 if( !group && item->GetParent() && item->GetParent()->Type() == PCB_FOOTPRINT_T )
1113 group = item->GetParent()->GetParentGroup();
1114
1115 while( group )
1116 {
1117 if( group->GetName().Matches( arg->AsString() ) )
1118 return 1.0;
1119
1120 group = group->AsEdaItem()->GetParentGroup();
1121 }
1122
1123 return 0.0;
1124 } );
1125}
1126
1127
1128#define MISSING_SHEET_ARG( f ) \
1129 wxString::Format( _( "Missing sheet name argument to %s." ), f )
1130
1131static void memberOfSheetFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1132{
1133 LIBEVAL::VALUE* arg = aCtx->Pop();
1134 LIBEVAL::VALUE* result = aCtx->AllocValue();
1135
1136 result->Set( 0.0 );
1137 aCtx->Push( result );
1138
1139 if( !arg || arg->AsString().IsEmpty() )
1140 {
1141 if( aCtx->HasErrorCallback() )
1142 aCtx->ReportError( MISSING_SHEET_ARG( wxT( "memberOfSheet()" ) ) );
1143
1144 return;
1145 }
1146
1147 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1148 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1149
1150 if( !item )
1151 return;
1152
1153 result->SetDeferredEval(
1154 [item, arg]() -> double
1155 {
1156 FOOTPRINT* fp = item->GetParentFootprint();
1157
1158 if( !fp && item->Type() == PCB_FOOTPRINT_T )
1159 fp = static_cast<FOOTPRINT*>( item );
1160
1161 if( !fp )
1162 return 0.0;
1163
1164 wxString sheetName = fp->GetSheetname();
1165 wxString refName = arg->AsString();
1166
1167 if( sheetName.EndsWith( wxT( "/" ) ) )
1168 sheetName.RemoveLast();
1169 if( refName.EndsWith( wxT( "/" ) ) )
1170 refName.RemoveLast();
1171
1172 if( sheetName.Matches( refName ) )
1173 return 1.0;
1174
1175 if( ( refName.Matches( wxT( "/" ) ) || refName.IsEmpty() )
1176 && sheetName.IsEmpty() )
1177 {
1178 return 1.0;
1179 }
1180
1181 return 0.0;
1182 } );
1183}
1184
1185
1186static void memberOfSheetOrChildrenFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1187{
1188 LIBEVAL::VALUE* arg = aCtx->Pop();
1189 LIBEVAL::VALUE* result = aCtx->AllocValue();
1190
1191 result->Set( 0.0 );
1192 aCtx->Push( result );
1193
1194 if( !arg || arg->AsString().IsEmpty() )
1195 {
1196 if( aCtx->HasErrorCallback() )
1197 aCtx->ReportError( MISSING_SHEET_ARG( wxT( "memberOfSheetOrChildren()" ) ) );
1198
1199 return;
1200 }
1201
1202 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1203 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1204
1205 if( !item )
1206 return;
1207
1208 result->SetDeferredEval(
1209 [item, arg]() -> double
1210 {
1211 FOOTPRINT* fp = item->GetParentFootprint();
1212
1213 if( !fp && item->Type() == PCB_FOOTPRINT_T )
1214 fp = static_cast<FOOTPRINT*>( item );
1215
1216 if( !fp )
1217 return 0.0;
1218
1219 wxString sheetName = fp->GetSheetname();
1220 wxString refName = arg->AsString();
1221
1222 if( sheetName.EndsWith( wxT( "/" ) ) )
1223 sheetName.RemoveLast();
1224 if( refName.EndsWith( wxT( "/" ) ) )
1225 refName.RemoveLast();
1226
1227 wxArrayString sheetPath = wxSplit( sheetName, '/' );
1228 wxArrayString refPath = wxSplit( refName, '/' );
1229
1230 if( refPath.size() > sheetPath.size() )
1231 return 0.0;
1232
1233 if( ( refName.Matches( wxT( "/" ) ) || refName.IsEmpty() )
1234 && sheetName.IsEmpty() )
1235 {
1236 return 1.0;
1237 }
1238
1239 for( size_t i = 0; i < refPath.size(); i++ )
1240 {
1241 if( !sheetPath[i].Matches( refPath[i] ) )
1242 return 0.0;
1243 }
1244
1245 return 1.0;
1246 } );
1247}
1248
1249
1250static void memberOfFootprintFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1251{
1252 LIBEVAL::VALUE* arg = aCtx->Pop();
1253 LIBEVAL::VALUE* result = aCtx->AllocValue();
1254
1255 result->Set( 0.0 );
1256 aCtx->Push( result );
1257
1258 if( !arg || arg->AsString().IsEmpty() )
1259 {
1260 if( aCtx->HasErrorCallback() )
1261 {
1262 aCtx->ReportError( wxString::Format( _( "Missing footprint argument (reference designator) to %s." ),
1263 wxT( "memberOfFootprint()" ) ) );
1264 }
1265
1266 return;
1267 }
1268
1269 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1270 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1271
1272 if( !item )
1273 return;
1274
1275 result->SetDeferredEval(
1276 [item, arg]() -> double
1277 {
1278 if( FOOTPRINT* parentFP = item->GetParentFootprint() )
1279 {
1280 if( testFootprintSelector( parentFP, arg->AsString() ) )
1281 return 1.0;
1282 }
1283
1284 return 0.0;
1285 } );
1286}
1287
1288
1289static void isMicroVia( LIBEVAL::CONTEXT* aCtx, void* self )
1290{
1291 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1292 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1293 LIBEVAL::VALUE* result = aCtx->AllocValue();
1294
1295 result->Set( 0.0 );
1296 aCtx->Push( result );
1297
1298 if( item && item->Type() == PCB_VIA_T && static_cast<PCB_VIA*>( item )->IsMicroVia() )
1299 result->Set( 1.0 );
1300}
1301
1302static void isBlindVia( LIBEVAL::CONTEXT* aCtx, void* self )
1303{
1304 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1305 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1306 LIBEVAL::VALUE* result = aCtx->AllocValue();
1307
1308 result->Set( 0.0 );
1309 aCtx->Push( result );
1310
1311 if( item && item->Type() == PCB_VIA_T && static_cast<PCB_VIA*>( item )->IsBlindVia() )
1312 result->Set( 1.0 );
1313}
1314
1315static void isBuriedVia( LIBEVAL::CONTEXT* aCtx, void* self )
1316{
1317 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1318 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1319 LIBEVAL::VALUE* result = aCtx->AllocValue();
1320
1321 result->Set( 0.0 );
1322 aCtx->Push( result );
1323
1324 if( item && item->Type() == PCB_VIA_T && static_cast<PCB_VIA*>( item )->IsBuriedVia() )
1325 result->Set( 1.0 );
1326}
1327
1328static void isStackedViaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1329{
1330 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1331 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1332 LIBEVAL::VALUE* result = aCtx->AllocValue();
1333
1334 result->Set( 0.0 );
1335 aCtx->Push( result );
1336
1337 if( !item || item->Type() != PCB_VIA_T )
1338 return;
1339
1340 PCB_VIA* via = static_cast<PCB_VIA*>( item );
1341 BOARD* board = via->GetBoard();
1342
1343 if( !board || via->GetViaType() != VIATYPE::MICROVIA )
1344 return;
1345
1346 {
1347 std::shared_lock<std::shared_mutex> readLock( board->m_CachesMutex );
1348
1349 if( board->m_StackedMicroviaCache.has_value() )
1350 {
1351 if( board->m_StackedMicroviaCache->count( via ) )
1352 result->Set( 1.0 );
1353
1354 return;
1355 }
1356 }
1357
1358 std::set<const PCB_VIA*> stacked;
1359
1360 for( const std::vector<PCB_VIA*>& column : PCB_VIA::CollectMicroviaColumns( board ) )
1361 stacked.insert( column.begin(), column.end() );
1362
1363 {
1364 std::unique_lock<std::shared_mutex> writeLock( board->m_CachesMutex );
1365 board->m_StackedMicroviaCache = stacked;
1366 }
1367
1368 if( stacked.count( via ) )
1369 result->Set( 1.0 );
1370}
1371
1372static void isBlindBuriedViaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1373{
1374 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1375 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1376 LIBEVAL::VALUE* result = aCtx->AllocValue();
1377
1378 result->Set( 0.0 );
1379 aCtx->Push( result );
1380
1381 if( item && item->Type() == PCB_VIA_T )
1382 {
1383 PCB_VIA* via = static_cast<PCB_VIA*>( item );
1384
1385 if( via->IsBlindVia() || via->IsBuriedVia() )
1386 result->Set( 1.0 );
1387 }
1388}
1389
1390
1391static void isCoupledDiffPairFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1392{
1393 PCBEXPR_CONTEXT* context = static_cast<PCBEXPR_CONTEXT*>( aCtx );
1394 BOARD_CONNECTED_ITEM* a = dynamic_cast<BOARD_CONNECTED_ITEM*>( context->GetItem( 0 ) );
1395 BOARD_CONNECTED_ITEM* b = dynamic_cast<BOARD_CONNECTED_ITEM*>( context->GetItem( 1 ) );
1396 LIBEVAL::VALUE* result = aCtx->AllocValue();
1397
1398 result->Set( 0.0 );
1399 aCtx->Push( result );
1400
1401 result->SetDeferredEval(
1402 [a, b, context]() -> double
1403 {
1404 NETINFO_ITEM* netinfo = a ? a->GetNet() : nullptr;
1405
1406 if( !netinfo )
1407 return 0.0;
1408
1409 wxString coupledNet;
1410 wxString dummy;
1411
1412 if( !DRC_ENGINE::MatchDpSuffix( netinfo->GetNetname(), coupledNet, dummy ) )
1413 return 0.0;
1414
1419 {
1420 // DRC engine evaluates these only in the context of a diffpair, but doesn't
1421 // always supply the second (B) item.
1422 if( BOARD* board = a->GetBoard() )
1423 {
1424 if( board->FindNet( coupledNet ) )
1425 return 1.0;
1426 }
1427 }
1428
1429 if( b && b->GetNetname() == coupledNet )
1430 return 1.0;
1431
1432 return 0.0;
1433 } );
1434}
1435
1436
1437static void inDiffPairFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1438{
1439 LIBEVAL::VALUE* argv = aCtx->Pop();
1440 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1441 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1442 LIBEVAL::VALUE* result = aCtx->AllocValue();
1443
1444 result->Set( 0.0 );
1445 aCtx->Push( result );
1446
1447 if( !argv || argv->AsString().IsEmpty() )
1448 {
1449 if( aCtx->HasErrorCallback() )
1450 {
1451 aCtx->ReportError( wxString::Format( _( "Missing diff-pair name argument to %s." ),
1452 wxT( "inDiffPair()" ) ) );
1453 }
1454
1455 return;
1456 }
1457
1458 if( !item || !item->GetBoard() )
1459 return;
1460
1461 result->SetDeferredEval(
1462 [item, argv]() -> double
1463 {
1464 if( item && item->IsConnected() )
1465 {
1466 NETINFO_ITEM* netinfo = static_cast<BOARD_CONNECTED_ITEM*>( item )->GetNet();
1467
1468 if( !netinfo )
1469 return 0.0;
1470
1471 wxString refName = netinfo->GetNetname();
1472 wxString arg = argv->AsString();
1473 wxString baseName, coupledNet;
1474 int polarity = DRC_ENGINE::MatchDpSuffix( refName, coupledNet, baseName );
1475
1476 if( polarity != 0 && item->GetBoard()->FindNet( coupledNet ) )
1477 {
1478 if( baseName.Matches( arg ) )
1479 return 1.0;
1480
1481 if( baseName.EndsWith( "_" ) && baseName.BeforeLast( '_' ).Matches( arg ) )
1482 return 1.0;
1483 }
1484 }
1485
1486 return 0.0;
1487 } );
1488}
1489
1490
1491static void inNetChainFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1492{
1493 LIBEVAL::VALUE* argv = aCtx->Pop();
1494 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1495 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1496 LIBEVAL::VALUE* result = aCtx->AllocValue();
1497
1498 result->Set( 0.0 );
1499 aCtx->Push( result );
1500
1501 if( !argv || argv->AsString().IsEmpty() )
1502 {
1503 if( aCtx->HasErrorCallback() )
1504 {
1505 aCtx->ReportError( wxString::Format( _( "Missing net-chain name argument to %s" ),
1506 wxT( "inNetChain()" ) ) );
1507 }
1508
1509 return;
1510 }
1511
1512 if( !item || !item->GetBoard() )
1513 return;
1514
1515 result->SetDeferredEval(
1516 [item, argv]() -> double
1517 {
1518 if( !item || !item->IsConnected() )
1519 return 0.0;
1520
1521 NETINFO_ITEM* netinfo = static_cast<BOARD_CONNECTED_ITEM*>( item )->GetNet();
1522
1523 if( !netinfo )
1524 return 0.0;
1525
1526 const wxString& chainName = netinfo->GetNetChain();
1527
1528 if( chainName.IsEmpty() )
1529 return 0.0;
1530
1531 wxString arg = argv->AsString();
1532
1533 return chainName.Matches( arg ) ? 1.0 : 0.0;
1534 } );
1535}
1536
1537
1538static void hasNetChainFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1539{
1540 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1541 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1542 LIBEVAL::VALUE* result = aCtx->AllocValue();
1543
1544 result->Set( 0.0 );
1545 aCtx->Push( result );
1546
1547 if( !item || !item->GetBoard() )
1548 return;
1549
1550 result->SetDeferredEval(
1551 [item]() -> double
1552 {
1553 if( !item || !item->IsConnected() )
1554 return 0.0;
1555
1556 NETINFO_ITEM* netinfo = static_cast<BOARD_CONNECTED_ITEM*>( item )->GetNet();
1557
1558 return ( netinfo && !netinfo->GetNetChain().IsEmpty() ) ? 1.0 : 0.0;
1559 } );
1560}
1561
1562
1563static void inNetChainClassFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1564{
1565 LIBEVAL::VALUE* argv = aCtx->Pop();
1566 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1567 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1568 LIBEVAL::VALUE* result = aCtx->AllocValue();
1569
1570 result->Set( 0.0 );
1571 aCtx->Push( result );
1572
1573 if( !argv || argv->AsString().IsEmpty() )
1574 {
1575 if( aCtx->HasErrorCallback() )
1576 {
1577 aCtx->ReportError( wxString::Format( _( "Missing netclass name argument to %s" ),
1578 wxT( "inNetChainClass()" ) ) );
1579 }
1580
1581 return;
1582 }
1583
1584 if( !item || !item->GetBoard() )
1585 return;
1586
1587 result->SetDeferredEval(
1588 [item, argv]() -> double
1589 {
1590 if( !item || !item->IsConnected() )
1591 return 0.0;
1592
1593 NETINFO_ITEM* netinfo = static_cast<BOARD_CONNECTED_ITEM*>( item )->GetNet();
1594
1595 if( !netinfo )
1596 return 0.0;
1597
1598 const wxString& chainName = netinfo->GetNetChain();
1599
1600 if( chainName.IsEmpty() )
1601 return 0.0;
1602
1603 if( BOARD* board = item->GetBoard() )
1604 {
1605 std::shared_ptr<NET_SETTINGS> ns = board->GetDesignSettings().m_NetSettings;
1606
1607 if( ns )
1608 {
1609 const wxString& className = ns->GetNetChainClass( chainName );
1610
1611 if( className.IsEmpty() )
1612 return 0.0;
1613
1614 wxString arg = argv->AsString();
1615
1616 return className.Matches( arg ) ? 1.0 : 0.0;
1617 }
1618 }
1619
1620 return 0.0;
1621 } );
1622}
1623
1624
1625static void getFieldFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1626{
1627 LIBEVAL::VALUE* arg = aCtx->Pop();
1628 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1629 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1630 LIBEVAL::VALUE* result = aCtx->AllocValue();
1631
1632 result->Set( "" );
1633 aCtx->Push( result );
1634
1635 if( !arg || arg->AsString().IsEmpty() )
1636 {
1637 if( aCtx->HasErrorCallback() )
1638 {
1639 aCtx->ReportError( wxString::Format( _( "Missing field name argument to %s." ),
1640 wxT( "getField()" ) ) );
1641 }
1642
1643 return;
1644 }
1645
1646 if( !item || !item->GetBoard() )
1647 return;
1648
1649 result->SetDeferredEval(
1650 [item, arg]() -> wxString
1651 {
1652 if( item && item->Type() == PCB_FOOTPRINT_T )
1653 {
1654 FOOTPRINT* fp = static_cast<FOOTPRINT*>( item );
1655 BOARD* board = fp->GetBoard();
1656 const wxString& fieldName = arg->AsString();
1657
1658 // getField only depends on the item, so memoize the resolved text per
1659 // (item, field) to avoid the linear field-name search on every repeat.
1660 ITEM_FIELD_CACHE_KEY key{ item, std::hash<wxString>{}( fieldName ) };
1661 wxString cached;
1662
1663 if( board && board->m_ItemFieldCache.Get( key, cached ) )
1664 return cached;
1665
1666 PCB_FIELD* field = fp->GetField( fieldName );
1667 wxString text = field ? field->GetText() : wxString();
1668
1669 if( board )
1670 board->m_ItemFieldCache.Set( key, text );
1671
1672 return text;
1673 }
1674
1675 return "";
1676 } );
1677}
1678
1679
1680static void hasNetclassFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1681{
1682 LIBEVAL::VALUE* arg = aCtx->Pop();
1683 LIBEVAL::VALUE* result = aCtx->AllocValue();
1684
1685 result->Set( 0.0 );
1686 aCtx->Push( result );
1687
1688 if( !arg || arg->AsString().IsEmpty() )
1689 {
1690 if( aCtx->HasErrorCallback() )
1691 {
1692 aCtx->ReportError( wxString::Format( _( "Missing netclass name argument to %s." ),
1693 wxT( "hasNetclass()" ) ) );
1694 }
1695
1696 return;
1697 }
1698
1699 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1700 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1701
1702 if( !item )
1703 return;
1704
1705 result->SetDeferredEval(
1706 [item, arg]() -> double
1707 {
1708 if( !item->IsConnected() )
1709 return 0.0;
1710
1711 BOARD_CONNECTED_ITEM* bcItem = static_cast<BOARD_CONNECTED_ITEM*>( item );
1712 NETCLASS* netclass = bcItem->GetEffectiveNetClass();
1713
1714 if( netclass && netclass->ContainsNetclassWithName( arg->AsString() ) )
1715 return 1.0;
1716
1717 return 0.0;
1718 } );
1719}
1720
1721
1722static void hasExactNetclassFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1723{
1724 LIBEVAL::VALUE* arg = aCtx->Pop();
1725 LIBEVAL::VALUE* result = aCtx->AllocValue();
1726
1727 result->Set( 0.0 );
1728 aCtx->Push( result );
1729
1730 if( !arg || arg->AsString().IsEmpty() )
1731 {
1732 if( aCtx->HasErrorCallback() )
1733 {
1734 aCtx->ReportError( wxString::Format( _( "Missing netclass name argument to %s." ),
1735 wxT( "hasExactNetclass()" ) ) );
1736 }
1737
1738 return;
1739 }
1740
1741 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1742 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1743
1744 if( !item )
1745 return;
1746
1747 result->SetDeferredEval(
1748 [item, arg]() -> double
1749 {
1750 if( !item->IsConnected() )
1751 return 0.0;
1752
1753 BOARD_CONNECTED_ITEM* bcItem = static_cast<BOARD_CONNECTED_ITEM*>( item );
1754 BOARD* board = bcItem->GetBoard();
1755 wxString netclassName;
1756
1757 if( board && ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
1758 {
1759 std::shared_lock<std::shared_mutex> readLock( board->m_CachesMutex );
1760
1761 auto it = board->m_ItemNetclassCache.find( item );
1762
1763 if( it != board->m_ItemNetclassCache.end() )
1764 netclassName = it->second;
1765 }
1766
1767 if( netclassName.empty() )
1768 {
1769 NETCLASS* netclass = bcItem->GetEffectiveNetClass();
1770
1771 if( netclass )
1772 netclassName = netclass->GetName();
1773
1774 if( board && !netclassName.empty() && ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
1775 {
1776 std::unique_lock<std::shared_mutex> writeLock( board->m_CachesMutex );
1777 board->m_ItemNetclassCache[item] = netclassName;
1778 }
1779 }
1780
1781 return ( netclassName == arg->AsString() ) ? 1.0 : 0.0;
1782 } );
1783}
1784
1785
1786static void hasComponentClassFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1787{
1788 LIBEVAL::VALUE* arg = aCtx->Pop();
1789 LIBEVAL::VALUE* result = aCtx->AllocValue();
1790
1791 result->Set( 0.0 );
1792 aCtx->Push( result );
1793
1794 if( !arg || arg->AsString().IsEmpty() )
1795 {
1796 if( aCtx->HasErrorCallback() )
1797 {
1798 aCtx->ReportError( wxString::Format( _( "Missing component class name argument to %s." ),
1799 wxT( "hasComponentClass()" ) ) );
1800 }
1801
1802 return;
1803 }
1804
1805 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1806 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1807
1808 if( !item )
1809 return;
1810
1811 result->SetDeferredEval(
1812 [item, arg]() -> double
1813 {
1814 FOOTPRINT* footprint = nullptr;
1815
1816 if( item->Type() == PCB_FOOTPRINT_T )
1817 footprint = static_cast<FOOTPRINT*>( item );
1818 else
1819 footprint = item->GetParentFootprint();
1820
1821 if( !footprint )
1822 return 0.0;
1823
1824 const COMPONENT_CLASS* compClass = footprint->GetComponentClass();
1825
1826 if( compClass && compClass->ContainsClassName( arg->AsString() ) )
1827 return 1.0;
1828
1829 return 0.0;
1830 } );
1831}
1832
1833
1834static void customPropertyFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1835{
1836 LIBEVAL::VALUE* arg = aCtx->Pop();
1837 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1838 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1839 LIBEVAL::VALUE* result = aCtx->AllocValue();
1840
1841 result->Set( "" );
1842 aCtx->Push( result );
1843
1844 if( !arg )
1845 {
1846 if( aCtx->HasErrorCallback() )
1847 {
1848 aCtx->ReportError( wxString::Format( _( "Missing property name argument to %s." ),
1849 wxT( "customProperty()" ) ) );
1850 }
1851
1852 return;
1853 }
1854
1855 if( !item )
1856 return;
1857
1858 result->SetDeferredEval(
1859 [item, arg]() -> wxString
1860 {
1861 const wxString key = arg->AsString();
1862 wxString value;
1863
1864 if( item && item->GetCustomProperty( key, value ) )
1865 return value;
1866
1867 if( FOOTPRINT* parentFp = item ? item->GetParentFootprint() : nullptr )
1868 {
1869 if( parentFp->GetCustomProperty( key, value ) )
1870 return value;
1871 }
1872
1873 return wxString();
1874 } );
1875}
1876
1877
1878static void hasCustomPropertyFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1879{
1880 LIBEVAL::VALUE* arg = aCtx->Pop();
1881 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1882 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1883 LIBEVAL::VALUE* result = aCtx->AllocValue();
1884
1885 result->Set( 0.0 );
1886 aCtx->Push( result );
1887
1888 if( !arg )
1889 {
1890 if( aCtx->HasErrorCallback() )
1891 {
1892 aCtx->ReportError( wxString::Format( _( "Missing property name argument to %s." ),
1893 wxT( "hasCustomProperty()" ) ) );
1894 }
1895
1896 return;
1897 }
1898
1899 if( !item )
1900 return;
1901
1902 result->SetDeferredEval(
1903 [item, arg]() -> double
1904 {
1905 const wxString key = arg->AsString();
1906 wxString value;
1907
1908 if( item && item->GetCustomProperty( key, value ) )
1909 return 1.0;
1910
1911 if( FOOTPRINT* parentFp = item ? item->GetParentFootprint() : nullptr )
1912 {
1913 if( parentFp->GetCustomProperty( key, value ) )
1914 return 1.0;
1915 }
1916
1917 return 0.0;
1918 } );
1919}
1920
1921
1926
1927
1929{
1930 m_funcs.clear();
1931
1932 RegisterFunc( wxT( "existsOnLayer('x')" ), existsOnLayerFunc );
1933
1934 RegisterFunc( wxT( "isPlated()" ), isPlatedFunc );
1935
1936 // Geometry-dependent functions depend on item position/shape rather than item properties.
1937 // The third argument marks them so that CreateFuncCall() can detect them automatically.
1938 RegisterFunc( wxT( "insideCourtyard('x') DEPRECATED" ), intersectsCourtyardFunc, true );
1939 RegisterFunc( wxT( "insideFrontCourtyard('x') DEPRECATED" ), intersectsFrontCourtyardFunc, true );
1940 RegisterFunc( wxT( "insideBackCourtyard('x') DEPRECATED" ), intersectsBackCourtyardFunc, true );
1941 RegisterFunc( wxT( "intersectsCourtyard('x')" ), intersectsCourtyardFunc, true );
1942 RegisterFunc( wxT( "intersectsFrontCourtyard('x')" ), intersectsFrontCourtyardFunc, true );
1943 RegisterFunc( wxT( "intersectsBackCourtyard('x')" ), intersectsBackCourtyardFunc, true );
1944
1945 RegisterFunc( wxT( "insideArea('x') DEPRECATED" ), intersectsKeepoutFunc, true );
1946 RegisterFunc( wxT( "intersectsArea('x')" ), intersectsAreaFunc, true );
1947 RegisterFunc( wxT( "intersectsKeepout('x')" ), intersectsKeepoutFunc, true );
1948 RegisterFunc( wxT( "enclosedByArea('x')" ), enclosedByAreaFunc, true );
1949
1950 RegisterFunc( wxT( "isMicroVia()" ), isMicroVia );
1951 RegisterFunc( wxT( "isBlindVia()" ), isBlindVia );
1952 RegisterFunc( wxT( "isBuriedVia()" ), isBuriedVia );
1953 RegisterFunc( wxT( "isBlindBuriedVia()" ), isBlindBuriedViaFunc );
1954 RegisterFunc( wxT( "isStackedVia()" ), isStackedViaFunc );
1955
1956 RegisterFunc( wxT( "memberOf('x') DEPRECATED" ), memberOfGroupFunc );
1957 RegisterFunc( wxT( "memberOfGroup('x')" ), memberOfGroupFunc );
1958 RegisterFunc( wxT( "memberOfFootprint('x')" ), memberOfFootprintFunc );
1959 RegisterFunc( wxT( "memberOfSheet('x')" ), memberOfSheetFunc );
1960 RegisterFunc( wxT( "memberOfSheetOrChildren('x')" ), memberOfSheetOrChildrenFunc );
1961
1962 RegisterFunc( wxT( "fromTo('x','y')" ), fromToFunc );
1963 RegisterFunc( wxT( "isCoupledDiffPair()" ), isCoupledDiffPairFunc );
1964 RegisterFunc( wxT( "inDiffPair('x')" ), inDiffPairFunc );
1965 RegisterFunc( wxT( "inNetChain('x')" ), inNetChainFunc );
1966 RegisterFunc( wxT( "hasNetChain()" ), hasNetChainFunc );
1967 RegisterFunc( wxT( "inNetChainClass('x')" ), inNetChainClassFunc );
1968
1969 RegisterFunc( wxT( "getField('x')" ), getFieldFunc );
1970
1971 RegisterFunc( wxT( "hasNetclass('x')" ), hasNetclassFunc );
1972 RegisterFunc( wxT( "hasExactNetclass('x')" ), hasExactNetclassFunc );
1973 RegisterFunc( wxT( "hasComponentClass('x')" ), hasComponentClassFunc );
1974
1975 RegisterFunc( wxT( "customProperty('x')" ), customPropertyFunc );
1976 RegisterFunc( wxT( "hasCustomProperty('x')" ), hasCustomPropertyFunc );
1977}
int index
const char * name
@ ERROR_OUTSIDE
constexpr int ARC_LOW_DEF
Definition base_units.h:136
BOX2< VECTOR2I > BOX2I
Definition box2.h:927
BASE_SET & set(size_t pos)
Definition base_set.h:126
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
virtual NETCLASS * GetEffectiveNetClass() const
Return the NETCLASS for this item.
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
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 bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition board_item.h:172
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, DRC_CONSTRAINT_T aUsage=NULL_CONSTRAINT) const
virtual void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const
Convert the item shape to a closed polygon.
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition board_item.h:408
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
FOOTPRINT * GetParentFootprint() const
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:346
BOARD_ITEM_CONTAINER * GetParent() const
Definition board_item.h:266
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT, DRC_CONSTRAINT_T aUsage=NULL_CONSTRAINT) const
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:409
SHARDED_CACHE< PTR_PTR_LAYER_CACHE_KEY, bool > m_IntersectsAreaCache
Definition board.h:1821
SHARDED_CACHE< ITEM_SELECTOR_LAYER_CACHE_KEY, bool > m_IntersectsCourtyardResultCache
Definition board.h:1824
std::unordered_map< const BOARD_ITEM *, wxString > m_ItemNetclassCache
Definition board.h:1837
SHARDED_CACHE< PTR_PTR_LAYER_CACHE_KEY, bool > m_EnclosedByAreaCache
Definition board.h:1823
SHARDED_CACHE< ITEM_SELECTOR_LAYER_CACHE_KEY, bool > m_EnclosedByAreaResultCache
Definition board.h:1829
SHARDED_CACHE< PTR_PTR_CACHE_KEY, bool > m_IntersectsBCourtyardCache
Definition board.h:1820
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:2980
const ZONES & Zones() const
Definition board.h:467
SHARDED_CACHE< PTR_PTR_CACHE_KEY, bool > m_IntersectsCourtyardCache
Definition board.h:1818
SHARDED_CACHE< PTR_PTR_LAYER_CACHE_KEY, bool > m_IntersectsKeepoutCache
Definition board.h:1822
const FOOTPRINTS & Footprints() const
Definition board.h:463
std::unordered_map< wxString, LSET > m_LayerExpressionCache
Definition board.h:1831
SHARDED_CACHE< PTR_PTR_CACHE_KEY, bool > m_IntersectsFCourtyardCache
Definition board.h:1819
std::unordered_map< const ZONE *, SHAPE_POLY_SET > m_DeflatedZoneOutlineCache
Definition board.h:1849
SHARDED_CACHE< ITEM_SELECTOR_LAYER_CACHE_KEY, bool > m_IntersectsFCourtyardResultCache
Definition board.h:1825
SHARDED_CACHE< ITEM_SELECTOR_LAYER_CACHE_KEY, bool > m_IntersectsKeepoutResultCache
Definition board.h:1828
SHARDED_CACHE< ITEM_FIELD_CACHE_KEY, wxString > m_ItemFieldCache
Definition board.h:1830
std::optional< std::set< const PCB_VIA * > > m_StackedMicroviaCache
Definition board.h:1841
SHARDED_CACHE< ITEM_SELECTOR_LAYER_CACHE_KEY, bool > m_IntersectsAreaResultCache
Definition board.h:1827
std::unordered_map< ZONE *, std::unique_ptr< DRC_RTREE > > m_CopperZoneRTreeCache
Definition board.h:1832
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1299
std::shared_mutex m_CachesMutex
Definition board.h:1815
std::shared_ptr< const FOOTPRINT_COURTYARD_INDEX > GetFootprintCourtyardIndex()
Return a spatial index of footprint courtyards, building it on first use.
Definition board.cpp:514
const std::unordered_map< KIID, BOARD_ITEM * > & GetItemByIdCache() const
Definition board.h:1689
std::unordered_map< wxString, std::vector< ZONE * > > m_ZonesByNameCache
Definition board.h:1845
SHARDED_CACHE< ITEM_SELECTOR_LAYER_CACHE_KEY, bool > m_IntersectsBCourtyardResultCache
Definition board.h:1826
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition board.h:751
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:308
A lightweight representation of a component class.
bool ContainsClassName(const wxString &className) const
Determines if this (effective) component class contains a specific constituent class.
std::shared_ptr< FROM_TO_CACHE > GetFromToCache()
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.
Implement an R-tree for fast spatial and layer indexing of connectable items.
Definition drc_rtree.h:45
A set of EDA_ITEMs (i.e., without duplicates).
Definition eda_group.h:43
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:270
bool GetCustomProperty(const wxString &aKey, wxString &aValue) const
Definition eda_item.cpp:188
virtual EDA_GROUP * GetParentGroup() const
Definition eda_item.h:116
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:167
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:118
static ENUM_MAP< T > & Instance()
Definition property.h:770
wxString GetSheetname() const
Definition footprint.h:510
PCB_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this footprint.
void TransformPadsToPolySet(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc) const
Generate pads shapes on layer aLayer as polygons and adds these polygons to aBuffer.
const COMPONENT_CLASS * GetComponentClass() const
Returns the component class for this footprint.
wxString GetFPIDAsString() const
Definition footprint.h:479
bool IsFlipped() const
Definition footprint.h:660
void TransformFPShapesToPolySet(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool aIncludeText=true, bool aIncludeShapes=true, bool aIncludePrivateItems=false) const
Generate shapes of graphic items (outlines) on layer aLayer as polygons and adds these polygons to aB...
const wxString & GetReference() const
Definition footprint.h:901
const SHAPE_POLY_SET & GetCourtyard(PCB_LAYER_ID aLayer) const
Used in DRC to test the courtyard area (a complex polygon).
Definition kiid.h:46
static bool SniffTest(const wxString &aCandidate)
Returns true if a string has the correct formatting to be a KIID.
Definition kiid.cpp:175
void ReportError(const wxString &aErrorMsg)
void Push(VALUE *v)
virtual const wxString & AsString() const
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & FrontMask()
Return a mask holding all technical layers and the external CU layer on front side.
Definition lset.cpp:718
LSEQ UIOrder() const
Return the copper, technical and user layers in the order shown in layer widget.
Definition lset.cpp:739
static const LSET & BackMask()
Return a mask holding all technical layers and the external CU layer on back side.
Definition lset.cpp:725
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition lset.h:63
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:43
bool ContainsNetclassWithName(const wxString &netclass) const
Determines if the given netclass name is a constituent of this (maybe aggregate) netclass.
Definition netclass.cpp:324
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:368
Handle the data for a net.
Definition netinfo.h:50
const wxString & GetNetChain() const
Definition netinfo.h:122
const wxString & GetNetname() const
Definition netinfo.h:110
Definition pad.h:61
PAD_ATTRIB GetAttribute() const
Definition pad.h:558
std::map< wxString, LIBEVAL::FUNC_CALL_REF > m_funcs
void RegisterFunc(const wxString &funcSignature, LIBEVAL::FUNC_CALL_REF funcPtr, bool aIsGeometryDependent=false)
int GetConstraint() const
PCB_LAYER_ID GetLayer() const
BOARD_ITEM * GetItem(int index) const
BOARD_ITEM * GetObject(const LIBEVAL::CONTEXT *aCtx) const
bool IsBlindVia() const
static std::vector< std::vector< PCB_VIA * > > CollectMicroviaColumns(BOARD *aBoard)
Runs of microvias that land on one another, each ordered from its outermost hop down.
bool IsBuriedVia() const
bool IsMicroVia() const
void Add(PCB_LAYER_ID aLayer)
SCOPED_LAYERSET(BOARD_ITEM *aItem)
Represent a set of closed polygons.
void ClearArcs()
Removes all arc references from all the outlines and holes in the polyset.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
bool Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
void Deflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError)
int OutlineCount() const
Return the number of outlines in the set.
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
bool Get(const KEY &aKey, VALUE &aValue) const
Look up a key. Returns false on a miss and leaves aValue untouched.
void Set(const KEY &aKey, const VALUE &aValue)
Handle a list of polygons defining a copper zone.
Definition zone.h:70
const BOX2I GetBoundingBox() const override
Definition zone.cpp:788
bool IsFilled() const
Definition zone.h:306
SHAPE_POLY_SET * Outline()
Definition zone.h:418
SHAPE_POLY_SET GetBoardOutline() const
Definition zone.cpp:896
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition zone.cpp:772
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:133
@ ALLOW_ACUTE_CORNERS
just inflate the polygon. Acute angles create spikes
@ DIFF_PAIR_GAP_CONSTRAINT
Definition drc_rule.h:78
@ NET_CHAIN_LENGTH_CONSTRAINT
Definition drc_rule.h:74
@ SILK_CLEARANCE_CONSTRAINT
Definition drc_rule.h:58
@ LENGTH_CONSTRAINT
Definition drc_rule.h:73
@ CLEARANCE_CONSTRAINT
Definition drc_rule.h:51
@ SKEW_CONSTRAINT
Definition drc_rule.h:77
#define _(s)
#define ROUTER_TRANSIENT
transient items that should NOT be cached
#define HOLE_PROXY
Indicates the BOARD_ITEM is a proxy for its hole.
#define MALFORMED_COURTYARDS
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition layer_ids.h:806
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition layer_ids.h:829
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_CrtYd
Definition layer_ids.h:112
@ B_Cu
Definition layer_ids.h:61
@ F_SilkS
Definition layer_ids.h:96
@ B_CrtYd
Definition layer_ids.h:111
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ B_SilkS
Definition layer_ids.h:97
@ F_Cu
Definition layer_ids.h:60
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition lset.cpp:750
@ PTH
Plated through hole pad.
Definition padstack.h:97
Class to handle a set of BOARD_ITEMs.
static void intersectsFrontCourtyardFunc(LIBEVAL::CONTEXT *aCtx, void *self)
#define MISSING_SHEET_ARG(f)
bool collidesWithCourtyard(BOARD_ITEM *aItem, std::shared_ptr< SHAPE > &aItemShape, PCBEXPR_CONTEXT *aCtx, FOOTPRINT *aFootprint, PCB_LAYER_ID aSide)
static SHAPE_POLY_SET getDeflatedZoneOutline(BOARD *aBoard, ZONE *aArea)
static void intersectsBackCourtyardFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void memberOfGroupFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static bool searchFootprintsNearItem(BOARD *aBoard, const wxString &aArg, PCBEXPR_CONTEXT *aCtx, BOARD_ITEM *aItem, const std::function< bool(FOOTPRINT *)> &aFunc)
bool collidesWithArea(BOARD_ITEM *aItem, PCB_LAYER_ID aLayer, PCBEXPR_CONTEXT *aCtx, ZONE *aArea, bool aForKeepout)
#define MISSING_AREA_ARG(f)
static void isCoupledDiffPairFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void doIntersectsAreaFunc(LIBEVAL::CONTEXT *aCtx, void *self, bool aForKeepout)
static void isPlatedFunc(LIBEVAL::CONTEXT *aCtx, void *self)
bool searchAreas(BOARD *aBoard, const wxString &aArg, PCBEXPR_CONTEXT *aCtx, const std::function< bool(ZONE *)> &aFunc)
static void isBuriedVia(LIBEVAL::CONTEXT *aCtx, void *self)
static void existsOnLayerFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void hasCustomPropertyFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static bool testFootprintSelector(FOOTPRINT *aFp, const wxString &aSelector)
static void isBlindBuriedViaFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void memberOfSheetFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void hasComponentClassFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void isBlindVia(LIBEVAL::CONTEXT *aCtx, void *self)
static void hasExactNetclassFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void intersectsKeepoutFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void inNetChainFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void enclosedByAreaFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void customPropertyFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void memberOfSheetOrChildrenFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void memberOfFootprintFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void hasNetChainFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void isMicroVia(LIBEVAL::CONTEXT *aCtx, void *self)
static void getFieldFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void hasNetclassFunc(LIBEVAL::CONTEXT *aCtx, void *self)
bool fromToFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void isStackedViaFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void intersectsCourtyardFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void inNetChainClassFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void inDiffPairFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void intersectsAreaFunc(LIBEVAL::CONTEXT *aCtx, void *self)
#define MISSING_FP_ARG(f)
std::vector< FAB_LAYER_COLOR > dummy
VECTOR3I res
wxString result
Test unit parsing edge cases and error handling.
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:100
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:78
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79