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->GetCopperZoneRTree( zone ) )
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 // Resolve through the board's item index: it re-binds stale entries and includes
721 // both board zones and zones inside footprints. In the normal path, it gives
722 // an O(1) lookup for the item by its UUID.
723 BOARD_ITEM* item = aBoard->ResolveItem( target, true );
724
725 if( item && item->Type() == PCB_ZONE_T )
726 return aFunc( static_cast<ZONE*>( item ) );
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 // No copying or it can lead to double-restoring of the layer set.
800
801 void Add( PCB_LAYER_ID aLayer )
802 {
803 m_item->SetLayerSet( m_item->GetLayerSet().set( aLayer ) );
804 }
805
806private:
809};
810
811
812#define MISSING_AREA_ARG( f ) \
813 wxString::Format( _( "Missing rule-area argument (A, B, or rule-area name) to %s." ), f )
814
815static void doIntersectsAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self, bool aForKeepout )
816{
817 PCBEXPR_CONTEXT* context = static_cast<PCBEXPR_CONTEXT*>( aCtx );
818 LIBEVAL::VALUE* arg = aCtx->Pop();
820
821 result->Set( 0.0 );
822 aCtx->Push( result );
823
824 if( !arg || arg->AsString().IsEmpty() )
825 {
826 if( aCtx->HasErrorCallback() )
827 {
828 if( aForKeepout )
829 aCtx->ReportError( MISSING_AREA_ARG( wxT( "intersectsKeepout()" ) ) );
830 else
831 aCtx->ReportError( MISSING_AREA_ARG( wxT( "intersectsArea()" ) ) );
832 }
833
834 return;
835 }
836
837 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
838 BOARD_ITEM* item = vref ? vref->GetObject( context ) : nullptr;
839
840 if( !item )
841 return;
842
843 result->SetDeferredEval(
844 [item, arg, context, aForKeepout]() -> double
845 {
846 BOARD* board = item->GetBoard();
847 PCB_LAYER_ID aLayer = context->GetLayer();
848 bool transient = ( item->GetFlags() & ROUTER_TRANSIENT ) != 0;
849 const wxString selector = arg->AsString();
850
851 auto& resultsCache = aForKeepout ? board->m_IntersectsKeepoutResultCache
853
854 auto& intersectsCache = aForKeepout ? board->m_IntersectsKeepoutCache
855 : board->m_IntersectsAreaCache;
856
857 // See intersectsCourtyard: "A"/"B" are pair-relative and not memoizable here.
858 bool memoize = !transient && selector != wxT( "A" ) && selector != wxT( "B" );
859 bool whole = false;
860
861 if( memoize && resultsCache.Get( { item, selector, aLayer, context->GetConstraint() }, whole ) )
862 return whole ? 1.0 : 0.0;
863
864 BOX2I itemBBox = item->GetBoundingBox();
865
866 bool res = searchAreas( board, selector, context,
867 [&]( ZONE* aArea )
868 {
869 if( !aArea || aArea == item || aArea->GetParent() == item )
870 return false;
871
872 SCOPED_LAYERSET scopedLayerSet( aArea );
873
874 if( context->GetConstraint() == SILK_CLEARANCE_CONSTRAINT )
875 {
876 // Silk clearance tests are run across layer pairs
877 if( ( aArea->IsOnLayer( F_SilkS ) && IsFrontLayer( aLayer ) )
878 || ( aArea->IsOnLayer( B_SilkS ) && IsBackLayer( aLayer ) ) )
879 {
880 scopedLayerSet.Add( aLayer );
881 }
882 }
883
884 LSET commonLayers = aArea->GetLayerSet() & item->GetLayerSet();
885
886 if( !commonLayers.any() )
887 return false;
888
889 if( !aArea->GetBoundingBox().Intersects( itemBBox ) )
890 return false;
891
892 LSET testLayers;
893
894 if( aLayer != UNDEFINED_LAYER )
895 testLayers.set( aLayer );
896 else
897 testLayers = commonLayers;
898
899 bool isTransient = ( item->GetFlags() & ROUTER_TRANSIENT ) != 0;
900 std::vector<PCB_LAYER_ID> layersToCompute;
901
902 if( !isTransient )
903 {
904 for( PCB_LAYER_ID layer : testLayers.UIOrder() )
905 {
906 bool cached = false;
907
908 if( intersectsCache.Get( { aArea, item, layer }, cached ) )
909 {
910 if( cached )
911 return true;
912 }
913 else
914 {
915 layersToCompute.push_back( layer );
916 }
917 }
918 }
919 else
920 {
921 for( PCB_LAYER_ID layer : testLayers.UIOrder() )
922 layersToCompute.push_back( layer );
923 }
924
925 bool anyCollision = false;
926
927 for( PCB_LAYER_ID layer : layersToCompute )
928 {
929 bool collides = collidesWithArea( item, layer, context, aArea, aForKeepout );
930
931 if( !isTransient )
932 intersectsCache.Set( { aArea, item, layer }, collides );
933
934 if( collides )
935 anyCollision = true;
936 }
937
938 return anyCollision;
939 } );
940
941 if( memoize )
942 resultsCache.Set( { item, selector, aLayer, context->GetConstraint() }, res );
943
944 return res ? 1.0 : 0.0;
945 } );
946}
947
948
949static void intersectsKeepoutFunc( LIBEVAL::CONTEXT* aCtx, void* self )
950{
951 doIntersectsAreaFunc( aCtx, self, true );
952}
953
954
955static void intersectsAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
956{
957 doIntersectsAreaFunc( aCtx, self, false );
958}
959
960
961static void enclosedByAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
962{
963 PCBEXPR_CONTEXT* context = static_cast<PCBEXPR_CONTEXT*>( aCtx );
964 LIBEVAL::VALUE* arg = aCtx->Pop();
966
967 result->Set( 0.0 );
968 aCtx->Push( result );
969
970 if( !arg || arg->AsString().IsEmpty() )
971 {
972 if( aCtx->HasErrorCallback() )
973 aCtx->ReportError( MISSING_AREA_ARG( wxT( "enclosedByArea()" ) ) );
974
975 return;
976 }
977
978 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
979 BOARD_ITEM* item = vref ? vref->GetObject( context ) : nullptr;
980
981 if( !item )
982 return;
983
984 result->SetDeferredEval(
985 [item, arg, context]() -> double
986 {
987 BOARD* board = item->GetBoard();
988 int maxError = board->GetDesignSettings().m_MaxError;
989 PCB_LAYER_ID layer = context->GetLayer();
990 bool transient = ( item->GetFlags() & ROUTER_TRANSIENT ) != 0;
991 const wxString selector = arg->AsString();
992
993 // See intersectsCourtyard: "A"/"B" are pair-relative and not memoizable here.
994 bool memoize = !transient && selector != wxT( "A" ) && selector != wxT( "B" );
995
996 ITEM_SELECTOR_LAYER_CACHE_KEY rkey{ item, selector, layer, context->GetConstraint() };
997 bool whole = false;
998
999 if( memoize && board->m_EnclosedByAreaResultCache.Get( rkey, whole ) )
1000 return whole ? 1.0 : 0.0;
1001
1002 BOX2I itemBBox = item->GetBoundingBox();
1003
1004 bool res = searchAreas( board, selector, context,
1005 [&]( ZONE* aArea )
1006 {
1007 if( !aArea || aArea == item || aArea->GetParent() == item )
1008 return false;
1009
1010 if( item->Type() != PCB_FOOTPRINT_T )
1011 {
1012 if( !( aArea->GetLayerSet() & item->GetLayerSet() ).any() )
1013 return false;
1014 }
1015
1016 if( !aArea->GetBoundingBox().Intersects( itemBBox ) )
1017 return false;
1018
1019 PTR_PTR_LAYER_CACHE_KEY key = { aArea, item, layer };
1020 bool cached = false;
1021
1022 if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0
1023 && board->m_EnclosedByAreaCache.Get( key, cached ) )
1024 {
1025 return cached;
1026 }
1027
1028 SHAPE_POLY_SET itemShape;
1029 bool enclosedByArea = false;
1030
1031 if( item->Type() == PCB_ZONE_T )
1032 {
1033 itemShape = static_cast<ZONE*>( item )->GetBoardOutline();
1034 }
1035 else if( item->Type() == PCB_FOOTPRINT_T )
1036 {
1037 FOOTPRINT* fp = static_cast<FOOTPRINT*>( item );
1038
1039 for( PCB_LAYER_ID testLayer : aArea->GetLayerSet() )
1040 {
1041 fp->TransformPadsToPolySet( itemShape, testLayer, 0, maxError, ERROR_OUTSIDE );
1042 fp->TransformFPShapesToPolySet( itemShape, testLayer, 0, maxError, ERROR_OUTSIDE );
1043 }
1044 }
1045 else
1046 {
1047 item->TransformShapeToPolygon( itemShape, layer, 0, maxError, ERROR_OUTSIDE );
1048 }
1049
1050 if( itemShape.IsEmpty() )
1051 {
1052 // If it's already empty then our test will have no meaning.
1053 enclosedByArea = false;
1054 }
1055 else
1056 {
1057 SHAPE_POLY_SET areaOutlineStorage;
1058 SHAPE_POLY_SET* areaOutline = &areaOutlineStorage;
1059
1060 // GetBoardOutline() is expensive. Only use it where we have to.
1061 if( aArea->GetParentFootprint() )
1062 areaOutlineStorage = aArea->GetBoardOutline();
1063 else
1064 areaOutline = aArea->Outline();
1065
1066 itemShape.ClearArcs();
1067 itemShape.BooleanSubtract( *areaOutline );
1068
1069 enclosedByArea = itemShape.IsEmpty();
1070 }
1071
1072 if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
1073 board->m_EnclosedByAreaCache.Set( key, enclosedByArea );
1074
1075 return enclosedByArea;
1076 } );
1077
1078 if( memoize )
1079 board->m_EnclosedByAreaResultCache.Set( rkey, res );
1080
1081 return res ? 1.0 : 0.0;
1082 } );
1083}
1084
1085
1086static void memberOfGroupFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1087{
1088 LIBEVAL::VALUE* arg = aCtx->Pop();
1089 LIBEVAL::VALUE* result = aCtx->AllocValue();
1090
1091 result->Set( 0.0 );
1092 aCtx->Push( result );
1093
1094 if( !arg || arg->AsString().IsEmpty() )
1095 {
1096 if( aCtx->HasErrorCallback() )
1097 {
1098 aCtx->ReportError( wxString::Format( _( "Missing group name argument to %s." ),
1099 wxT( "memberOfGroup()" ) ) );
1100 }
1101
1102 return;
1103 }
1104
1105 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1106 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1107
1108 if( !item )
1109 return;
1110
1111 result->SetDeferredEval(
1112 [item, arg]() -> double
1113 {
1114 EDA_GROUP* group = item->GetParentGroup();
1115
1116 if( !group && item->GetParent() && item->GetParent()->Type() == PCB_FOOTPRINT_T )
1117 group = item->GetParent()->GetParentGroup();
1118
1119 while( group )
1120 {
1121 if( group->GetName().Matches( arg->AsString() ) )
1122 return 1.0;
1123
1124 group = group->AsEdaItem()->GetParentGroup();
1125 }
1126
1127 return 0.0;
1128 } );
1129}
1130
1131
1132#define MISSING_SHEET_ARG( f ) \
1133 wxString::Format( _( "Missing sheet name argument to %s." ), f )
1134
1135static void memberOfSheetFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1136{
1137 LIBEVAL::VALUE* arg = aCtx->Pop();
1138 LIBEVAL::VALUE* result = aCtx->AllocValue();
1139
1140 result->Set( 0.0 );
1141 aCtx->Push( result );
1142
1143 if( !arg || arg->AsString().IsEmpty() )
1144 {
1145 if( aCtx->HasErrorCallback() )
1146 aCtx->ReportError( MISSING_SHEET_ARG( wxT( "memberOfSheet()" ) ) );
1147
1148 return;
1149 }
1150
1151 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1152 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1153
1154 if( !item )
1155 return;
1156
1157 result->SetDeferredEval(
1158 [item, arg]() -> double
1159 {
1160 FOOTPRINT* fp = item->GetParentFootprint();
1161
1162 if( !fp && item->Type() == PCB_FOOTPRINT_T )
1163 fp = static_cast<FOOTPRINT*>( item );
1164
1165 if( !fp )
1166 return 0.0;
1167
1168 wxString sheetName = fp->GetSheetname();
1169 wxString refName = arg->AsString();
1170
1171 if( sheetName.EndsWith( wxT( "/" ) ) )
1172 sheetName.RemoveLast();
1173 if( refName.EndsWith( wxT( "/" ) ) )
1174 refName.RemoveLast();
1175
1176 if( sheetName.Matches( refName ) )
1177 return 1.0;
1178
1179 if( ( refName.Matches( wxT( "/" ) ) || refName.IsEmpty() )
1180 && sheetName.IsEmpty() )
1181 {
1182 return 1.0;
1183 }
1184
1185 return 0.0;
1186 } );
1187}
1188
1189
1190static void memberOfSheetOrChildrenFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1191{
1192 LIBEVAL::VALUE* arg = aCtx->Pop();
1193 LIBEVAL::VALUE* result = aCtx->AllocValue();
1194
1195 result->Set( 0.0 );
1196 aCtx->Push( result );
1197
1198 if( !arg || arg->AsString().IsEmpty() )
1199 {
1200 if( aCtx->HasErrorCallback() )
1201 aCtx->ReportError( MISSING_SHEET_ARG( wxT( "memberOfSheetOrChildren()" ) ) );
1202
1203 return;
1204 }
1205
1206 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1207 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1208
1209 if( !item )
1210 return;
1211
1212 result->SetDeferredEval(
1213 [item, arg]() -> double
1214 {
1215 FOOTPRINT* fp = item->GetParentFootprint();
1216
1217 if( !fp && item->Type() == PCB_FOOTPRINT_T )
1218 fp = static_cast<FOOTPRINT*>( item );
1219
1220 if( !fp )
1221 return 0.0;
1222
1223 wxString sheetName = fp->GetSheetname();
1224 wxString refName = arg->AsString();
1225
1226 if( sheetName.EndsWith( wxT( "/" ) ) )
1227 sheetName.RemoveLast();
1228 if( refName.EndsWith( wxT( "/" ) ) )
1229 refName.RemoveLast();
1230
1231 wxArrayString sheetPath = wxSplit( sheetName, '/' );
1232 wxArrayString refPath = wxSplit( refName, '/' );
1233
1234 if( refPath.size() > sheetPath.size() )
1235 return 0.0;
1236
1237 if( ( refName.Matches( wxT( "/" ) ) || refName.IsEmpty() )
1238 && sheetName.IsEmpty() )
1239 {
1240 return 1.0;
1241 }
1242
1243 for( size_t i = 0; i < refPath.size(); i++ )
1244 {
1245 if( !sheetPath[i].Matches( refPath[i] ) )
1246 return 0.0;
1247 }
1248
1249 return 1.0;
1250 } );
1251}
1252
1253
1254static void memberOfFootprintFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1255{
1256 LIBEVAL::VALUE* arg = aCtx->Pop();
1257 LIBEVAL::VALUE* result = aCtx->AllocValue();
1258
1259 result->Set( 0.0 );
1260 aCtx->Push( result );
1261
1262 if( !arg || arg->AsString().IsEmpty() )
1263 {
1264 if( aCtx->HasErrorCallback() )
1265 {
1266 aCtx->ReportError( wxString::Format( _( "Missing footprint argument (reference designator) to %s." ),
1267 wxT( "memberOfFootprint()" ) ) );
1268 }
1269
1270 return;
1271 }
1272
1273 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1274 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1275
1276 if( !item )
1277 return;
1278
1279 result->SetDeferredEval(
1280 [item, arg]() -> double
1281 {
1282 if( FOOTPRINT* parentFP = item->GetParentFootprint() )
1283 {
1284 if( testFootprintSelector( parentFP, arg->AsString() ) )
1285 return 1.0;
1286 }
1287
1288 return 0.0;
1289 } );
1290}
1291
1292
1293static void isMicroVia( LIBEVAL::CONTEXT* aCtx, void* self )
1294{
1295 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1296 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1297 LIBEVAL::VALUE* result = aCtx->AllocValue();
1298
1299 result->Set( 0.0 );
1300 aCtx->Push( result );
1301
1302 if( item && item->Type() == PCB_VIA_T && static_cast<PCB_VIA*>( item )->IsMicroVia() )
1303 result->Set( 1.0 );
1304}
1305
1306static void isBlindVia( LIBEVAL::CONTEXT* aCtx, void* self )
1307{
1308 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1309 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1310 LIBEVAL::VALUE* result = aCtx->AllocValue();
1311
1312 result->Set( 0.0 );
1313 aCtx->Push( result );
1314
1315 if( item && item->Type() == PCB_VIA_T && static_cast<PCB_VIA*>( item )->IsBlindVia() )
1316 result->Set( 1.0 );
1317}
1318
1319static void isBuriedVia( LIBEVAL::CONTEXT* aCtx, void* self )
1320{
1321 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1322 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1323 LIBEVAL::VALUE* result = aCtx->AllocValue();
1324
1325 result->Set( 0.0 );
1326 aCtx->Push( result );
1327
1328 if( item && item->Type() == PCB_VIA_T && static_cast<PCB_VIA*>( item )->IsBuriedVia() )
1329 result->Set( 1.0 );
1330}
1331
1332static void isStackedViaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1333{
1334 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1335 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1336 LIBEVAL::VALUE* result = aCtx->AllocValue();
1337
1338 result->Set( 0.0 );
1339 aCtx->Push( result );
1340
1341 if( !item || item->Type() != PCB_VIA_T )
1342 return;
1343
1344 PCB_VIA* via = static_cast<PCB_VIA*>( item );
1345 BOARD* board = via->GetBoard();
1346
1347 if( !board || via->GetViaType() != VIATYPE::MICROVIA )
1348 return;
1349
1350 {
1351 std::shared_lock<std::shared_mutex> readLock( board->m_CachesMutex );
1352
1353 if( board->m_StackedMicroviaCache.has_value() )
1354 {
1355 if( board->m_StackedMicroviaCache->count( via ) )
1356 result->Set( 1.0 );
1357
1358 return;
1359 }
1360 }
1361
1362 std::set<const PCB_VIA*> stacked;
1363
1364 for( const std::vector<PCB_VIA*>& column : PCB_VIA::CollectMicroviaColumns( board ) )
1365 stacked.insert( column.begin(), column.end() );
1366
1367 {
1368 std::unique_lock<std::shared_mutex> writeLock( board->m_CachesMutex );
1369 board->m_StackedMicroviaCache = stacked;
1370 }
1371
1372 if( stacked.count( via ) )
1373 result->Set( 1.0 );
1374}
1375
1376static void isBlindBuriedViaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1377{
1378 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1379 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1380 LIBEVAL::VALUE* result = aCtx->AllocValue();
1381
1382 result->Set( 0.0 );
1383 aCtx->Push( result );
1384
1385 if( item && item->Type() == PCB_VIA_T )
1386 {
1387 PCB_VIA* via = static_cast<PCB_VIA*>( item );
1388
1389 if( via->IsBlindVia() || via->IsBuriedVia() )
1390 result->Set( 1.0 );
1391 }
1392}
1393
1394
1395static void isCoupledDiffPairFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1396{
1397 PCBEXPR_CONTEXT* context = static_cast<PCBEXPR_CONTEXT*>( aCtx );
1398 BOARD_CONNECTED_ITEM* a = dynamic_cast<BOARD_CONNECTED_ITEM*>( context->GetItem( 0 ) );
1399 BOARD_CONNECTED_ITEM* b = dynamic_cast<BOARD_CONNECTED_ITEM*>( context->GetItem( 1 ) );
1400 LIBEVAL::VALUE* result = aCtx->AllocValue();
1401
1402 result->Set( 0.0 );
1403 aCtx->Push( result );
1404
1405 result->SetDeferredEval(
1406 [a, b, context]() -> double
1407 {
1408 NETINFO_ITEM* netinfo = a ? a->GetNet() : nullptr;
1409
1410 if( !netinfo )
1411 return 0.0;
1412
1413 wxString coupledNet;
1414 wxString dummy;
1415
1416 if( !DRC_ENGINE::MatchDpSuffix( netinfo->GetNetname(), coupledNet, dummy ) )
1417 return 0.0;
1418
1423 {
1424 // DRC engine evaluates these only in the context of a diffpair, but doesn't
1425 // always supply the second (B) item.
1426 if( BOARD* board = a->GetBoard() )
1427 {
1428 if( board->FindNet( coupledNet ) )
1429 return 1.0;
1430 }
1431 }
1432
1433 if( b && b->GetNetname() == coupledNet )
1434 return 1.0;
1435
1436 return 0.0;
1437 } );
1438}
1439
1440
1441static void inDiffPairFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1442{
1443 LIBEVAL::VALUE* argv = aCtx->Pop();
1444 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1445 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1446 LIBEVAL::VALUE* result = aCtx->AllocValue();
1447
1448 result->Set( 0.0 );
1449 aCtx->Push( result );
1450
1451 if( !argv || argv->AsString().IsEmpty() )
1452 {
1453 if( aCtx->HasErrorCallback() )
1454 {
1455 aCtx->ReportError( wxString::Format( _( "Missing diff-pair name argument to %s." ),
1456 wxT( "inDiffPair()" ) ) );
1457 }
1458
1459 return;
1460 }
1461
1462 if( !item || !item->GetBoard() )
1463 return;
1464
1465 result->SetDeferredEval(
1466 [item, argv]() -> double
1467 {
1468 if( item && item->IsConnected() )
1469 {
1470 NETINFO_ITEM* netinfo = static_cast<BOARD_CONNECTED_ITEM*>( item )->GetNet();
1471
1472 if( !netinfo )
1473 return 0.0;
1474
1475 wxString refName = netinfo->GetNetname();
1476 wxString arg = argv->AsString();
1477 wxString baseName, coupledNet;
1478 int polarity = DRC_ENGINE::MatchDpSuffix( refName, coupledNet, baseName );
1479
1480 if( polarity != 0 && item->GetBoard()->FindNet( coupledNet ) )
1481 {
1482 if( baseName.Matches( arg ) )
1483 return 1.0;
1484
1485 if( baseName.EndsWith( "_" ) && baseName.BeforeLast( '_' ).Matches( arg ) )
1486 return 1.0;
1487 }
1488 }
1489
1490 return 0.0;
1491 } );
1492}
1493
1494
1495static void inNetChainFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1496{
1497 LIBEVAL::VALUE* argv = aCtx->Pop();
1498 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1499 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1500 LIBEVAL::VALUE* result = aCtx->AllocValue();
1501
1502 result->Set( 0.0 );
1503 aCtx->Push( result );
1504
1505 if( !argv || argv->AsString().IsEmpty() )
1506 {
1507 if( aCtx->HasErrorCallback() )
1508 {
1509 aCtx->ReportError( wxString::Format( _( "Missing net-chain name argument to %s" ),
1510 wxT( "inNetChain()" ) ) );
1511 }
1512
1513 return;
1514 }
1515
1516 if( !item || !item->GetBoard() )
1517 return;
1518
1519 result->SetDeferredEval(
1520 [item, argv]() -> double
1521 {
1522 if( !item || !item->IsConnected() )
1523 return 0.0;
1524
1525 NETINFO_ITEM* netinfo = static_cast<BOARD_CONNECTED_ITEM*>( item )->GetNet();
1526
1527 if( !netinfo )
1528 return 0.0;
1529
1530 const wxString& chainName = netinfo->GetNetChain();
1531
1532 if( chainName.IsEmpty() )
1533 return 0.0;
1534
1535 wxString arg = argv->AsString();
1536
1537 return chainName.Matches( arg ) ? 1.0 : 0.0;
1538 } );
1539}
1540
1541
1542static void hasNetChainFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1543{
1544 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1545 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1546 LIBEVAL::VALUE* result = aCtx->AllocValue();
1547
1548 result->Set( 0.0 );
1549 aCtx->Push( result );
1550
1551 if( !item || !item->GetBoard() )
1552 return;
1553
1554 result->SetDeferredEval(
1555 [item]() -> double
1556 {
1557 if( !item || !item->IsConnected() )
1558 return 0.0;
1559
1560 NETINFO_ITEM* netinfo = static_cast<BOARD_CONNECTED_ITEM*>( item )->GetNet();
1561
1562 return ( netinfo && !netinfo->GetNetChain().IsEmpty() ) ? 1.0 : 0.0;
1563 } );
1564}
1565
1566
1567static void inNetChainClassFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1568{
1569 LIBEVAL::VALUE* argv = aCtx->Pop();
1570 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1571 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1572 LIBEVAL::VALUE* result = aCtx->AllocValue();
1573
1574 result->Set( 0.0 );
1575 aCtx->Push( result );
1576
1577 if( !argv || argv->AsString().IsEmpty() )
1578 {
1579 if( aCtx->HasErrorCallback() )
1580 {
1581 aCtx->ReportError( wxString::Format( _( "Missing netclass name argument to %s" ),
1582 wxT( "inNetChainClass()" ) ) );
1583 }
1584
1585 return;
1586 }
1587
1588 if( !item || !item->GetBoard() )
1589 return;
1590
1591 result->SetDeferredEval(
1592 [item, argv]() -> double
1593 {
1594 if( !item || !item->IsConnected() )
1595 return 0.0;
1596
1597 NETINFO_ITEM* netinfo = static_cast<BOARD_CONNECTED_ITEM*>( item )->GetNet();
1598
1599 if( !netinfo )
1600 return 0.0;
1601
1602 const wxString& chainName = netinfo->GetNetChain();
1603
1604 if( chainName.IsEmpty() )
1605 return 0.0;
1606
1607 if( BOARD* board = item->GetBoard() )
1608 {
1609 std::shared_ptr<NET_SETTINGS> ns = board->GetDesignSettings().m_NetSettings;
1610
1611 if( ns )
1612 {
1613 const wxString& className = ns->GetNetChainClass( chainName );
1614
1615 if( className.IsEmpty() )
1616 return 0.0;
1617
1618 wxString arg = argv->AsString();
1619
1620 return className.Matches( arg ) ? 1.0 : 0.0;
1621 }
1622 }
1623
1624 return 0.0;
1625 } );
1626}
1627
1628
1629static void getFieldFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1630{
1631 LIBEVAL::VALUE* arg = aCtx->Pop();
1632 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1633 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1634 LIBEVAL::VALUE* result = aCtx->AllocValue();
1635
1636 result->Set( "" );
1637 aCtx->Push( result );
1638
1639 if( !arg || arg->AsString().IsEmpty() )
1640 {
1641 if( aCtx->HasErrorCallback() )
1642 {
1643 aCtx->ReportError( wxString::Format( _( "Missing field name argument to %s." ),
1644 wxT( "getField()" ) ) );
1645 }
1646
1647 return;
1648 }
1649
1650 if( !item || !item->GetBoard() )
1651 return;
1652
1653 result->SetDeferredEval(
1654 [item, arg]() -> wxString
1655 {
1656 if( item && item->Type() == PCB_FOOTPRINT_T )
1657 {
1658 FOOTPRINT* fp = static_cast<FOOTPRINT*>( item );
1659 BOARD* board = fp->GetBoard();
1660 const wxString& fieldName = arg->AsString();
1661
1662 if( board )
1663 {
1664 const wxString variantName = board->GetCurrentVariant();
1665
1666 if( const FOOTPRINT_VARIANT* variant = fp->GetVariant( variantName );
1667 variant && variant->HasFieldValue( fieldName ) )
1668 {
1669 return variant->GetFieldValue( fieldName );
1670 }
1671 }
1672
1673 // Only base values go in the cache.
1674 ITEM_FIELD_CACHE_KEY key{ item, std::hash<wxString>{}( fieldName ) };
1675 wxString cached;
1676
1677 if( board && board->m_ItemFieldCache.Get( key, cached ) )
1678 return cached;
1679
1680 PCB_FIELD* field = fp->GetField( fieldName );
1681 wxString text = field ? field->GetText() : wxString();
1682
1683 if( board )
1684 board->m_ItemFieldCache.Set( key, text );
1685
1686 return text;
1687 }
1688
1689 return "";
1690 } );
1691}
1692
1693
1694static void hasNetclassFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1695{
1696 LIBEVAL::VALUE* arg = aCtx->Pop();
1697 LIBEVAL::VALUE* result = aCtx->AllocValue();
1698
1699 result->Set( 0.0 );
1700 aCtx->Push( result );
1701
1702 if( !arg || arg->AsString().IsEmpty() )
1703 {
1704 if( aCtx->HasErrorCallback() )
1705 {
1706 aCtx->ReportError( wxString::Format( _( "Missing netclass name argument to %s." ),
1707 wxT( "hasNetclass()" ) ) );
1708 }
1709
1710 return;
1711 }
1712
1713 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1714 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1715
1716 if( !item )
1717 return;
1718
1719 result->SetDeferredEval(
1720 [item, arg]() -> double
1721 {
1722 if( !item->IsConnected() )
1723 return 0.0;
1724
1725 BOARD_CONNECTED_ITEM* bcItem = static_cast<BOARD_CONNECTED_ITEM*>( item );
1726 NETCLASS* netclass = bcItem->GetEffectiveNetClass();
1727
1728 if( netclass && netclass->ContainsNetclassWithName( arg->AsString() ) )
1729 return 1.0;
1730
1731 return 0.0;
1732 } );
1733}
1734
1735
1736static void hasExactNetclassFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1737{
1738 LIBEVAL::VALUE* arg = aCtx->Pop();
1739 LIBEVAL::VALUE* result = aCtx->AllocValue();
1740
1741 result->Set( 0.0 );
1742 aCtx->Push( result );
1743
1744 if( !arg || arg->AsString().IsEmpty() )
1745 {
1746 if( aCtx->HasErrorCallback() )
1747 {
1748 aCtx->ReportError( wxString::Format( _( "Missing netclass name argument to %s." ),
1749 wxT( "hasExactNetclass()" ) ) );
1750 }
1751
1752 return;
1753 }
1754
1755 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1756 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1757
1758 if( !item )
1759 return;
1760
1761 result->SetDeferredEval(
1762 [item, arg]() -> double
1763 {
1764 if( !item->IsConnected() )
1765 return 0.0;
1766
1767 BOARD_CONNECTED_ITEM* bcItem = static_cast<BOARD_CONNECTED_ITEM*>( item );
1768 NETCLASS* netclass = bcItem->GetEffectiveNetClass();
1769
1770 return netclass && netclass->NameEquals( arg->AsString() ) ? 1.0 : 0.0;
1771 } );
1772}
1773
1774
1775static void hasComponentClassFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1776{
1777 LIBEVAL::VALUE* arg = aCtx->Pop();
1778 LIBEVAL::VALUE* result = aCtx->AllocValue();
1779
1780 result->Set( 0.0 );
1781 aCtx->Push( result );
1782
1783 if( !arg || arg->AsString().IsEmpty() )
1784 {
1785 if( aCtx->HasErrorCallback() )
1786 {
1787 aCtx->ReportError( wxString::Format( _( "Missing component class name argument to %s." ),
1788 wxT( "hasComponentClass()" ) ) );
1789 }
1790
1791 return;
1792 }
1793
1794 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1795 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1796
1797 if( !item )
1798 return;
1799
1800 result->SetDeferredEval(
1801 [item, arg]() -> double
1802 {
1803 FOOTPRINT* footprint = nullptr;
1804
1805 if( item->Type() == PCB_FOOTPRINT_T )
1806 footprint = static_cast<FOOTPRINT*>( item );
1807 else
1808 footprint = item->GetParentFootprint();
1809
1810 if( !footprint )
1811 return 0.0;
1812
1813 const COMPONENT_CLASS* compClass = footprint->GetComponentClass();
1814
1815 if( compClass && compClass->ContainsClassName( arg->AsString() ) )
1816 return 1.0;
1817
1818 return 0.0;
1819 } );
1820}
1821
1822
1823static void customPropertyFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1824{
1825 LIBEVAL::VALUE* arg = aCtx->Pop();
1826 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1827 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1828 LIBEVAL::VALUE* result = aCtx->AllocValue();
1829
1830 result->Set( "" );
1831 aCtx->Push( result );
1832
1833 if( !arg )
1834 {
1835 if( aCtx->HasErrorCallback() )
1836 {
1837 aCtx->ReportError( wxString::Format( _( "Missing property name argument to %s." ),
1838 wxT( "customProperty()" ) ) );
1839 }
1840
1841 return;
1842 }
1843
1844 if( !item )
1845 return;
1846
1847 result->SetDeferredEval(
1848 [item, arg]() -> wxString
1849 {
1850 const wxString key = arg->AsString();
1851 wxString value;
1852
1853 if( item && item->GetCustomProperty( key, value ) )
1854 return value;
1855
1856 if( FOOTPRINT* parentFp = item ? item->GetParentFootprint() : nullptr )
1857 {
1858 if( parentFp->GetCustomProperty( key, value ) )
1859 return value;
1860 }
1861
1862 return wxString();
1863 } );
1864}
1865
1866
1867static void hasCustomPropertyFunc( LIBEVAL::CONTEXT* aCtx, void* self )
1868{
1869 LIBEVAL::VALUE* arg = aCtx->Pop();
1870 PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
1871 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
1872 LIBEVAL::VALUE* result = aCtx->AllocValue();
1873
1874 result->Set( 0.0 );
1875 aCtx->Push( result );
1876
1877 if( !arg )
1878 {
1879 if( aCtx->HasErrorCallback() )
1880 {
1881 aCtx->ReportError( wxString::Format( _( "Missing property name argument to %s." ),
1882 wxT( "hasCustomProperty()" ) ) );
1883 }
1884
1885 return;
1886 }
1887
1888 if( !item )
1889 return;
1890
1891 result->SetDeferredEval(
1892 [item, arg]() -> double
1893 {
1894 const wxString key = arg->AsString();
1895 wxString value;
1896
1897 if( item && item->GetCustomProperty( key, value ) )
1898 return 1.0;
1899
1900 if( FOOTPRINT* parentFp = item ? item->GetParentFootprint() : nullptr )
1901 {
1902 if( parentFp->GetCustomProperty( key, value ) )
1903 return 1.0;
1904 }
1905
1906 return 0.0;
1907 } );
1908}
1909
1910
1915
1916
1918{
1919 m_funcs.clear();
1920
1921 RegisterFunc( wxT( "existsOnLayer('x')" ), existsOnLayerFunc );
1922
1923 RegisterFunc( wxT( "isPlated()" ), isPlatedFunc );
1924
1925 // Geometry-dependent functions depend on item position/shape rather than item properties.
1926 // The third argument marks them so that CreateFuncCall() can detect them automatically.
1927 RegisterFunc( wxT( "insideCourtyard('x') DEPRECATED" ), intersectsCourtyardFunc, true );
1928 RegisterFunc( wxT( "insideFrontCourtyard('x') DEPRECATED" ), intersectsFrontCourtyardFunc, true );
1929 RegisterFunc( wxT( "insideBackCourtyard('x') DEPRECATED" ), intersectsBackCourtyardFunc, true );
1930 RegisterFunc( wxT( "intersectsCourtyard('x')" ), intersectsCourtyardFunc, true );
1931 RegisterFunc( wxT( "intersectsFrontCourtyard('x')" ), intersectsFrontCourtyardFunc, true );
1932 RegisterFunc( wxT( "intersectsBackCourtyard('x')" ), intersectsBackCourtyardFunc, true );
1933
1934 RegisterFunc( wxT( "insideArea('x') DEPRECATED" ), intersectsKeepoutFunc, true );
1935 RegisterFunc( wxT( "intersectsArea('x')" ), intersectsAreaFunc, true );
1936 RegisterFunc( wxT( "intersectsKeepout('x')" ), intersectsKeepoutFunc, true );
1937 RegisterFunc( wxT( "enclosedByArea('x')" ), enclosedByAreaFunc, true );
1938
1939 RegisterFunc( wxT( "isMicroVia()" ), isMicroVia );
1940 RegisterFunc( wxT( "isBlindVia()" ), isBlindVia );
1941 RegisterFunc( wxT( "isBuriedVia()" ), isBuriedVia );
1942 RegisterFunc( wxT( "isBlindBuriedVia()" ), isBlindBuriedViaFunc );
1943 RegisterFunc( wxT( "isStackedVia()" ), isStackedViaFunc );
1944
1945 RegisterFunc( wxT( "memberOf('x') DEPRECATED" ), memberOfGroupFunc );
1946 RegisterFunc( wxT( "memberOfGroup('x')" ), memberOfGroupFunc );
1947 RegisterFunc( wxT( "memberOfFootprint('x')" ), memberOfFootprintFunc );
1948 RegisterFunc( wxT( "memberOfSheet('x')" ), memberOfSheetFunc );
1949 RegisterFunc( wxT( "memberOfSheetOrChildren('x')" ), memberOfSheetOrChildrenFunc );
1950
1951 RegisterFunc( wxT( "fromTo('x','y')" ), fromToFunc );
1952 RegisterFunc( wxT( "isCoupledDiffPair()" ), isCoupledDiffPairFunc );
1953 RegisterFunc( wxT( "inDiffPair('x')" ), inDiffPairFunc );
1954 RegisterFunc( wxT( "inNetChain('x')" ), inNetChainFunc );
1955 RegisterFunc( wxT( "hasNetChain()" ), hasNetChainFunc );
1956 RegisterFunc( wxT( "inNetChainClass('x')" ), inNetChainClassFunc );
1957
1958 RegisterFunc( wxT( "getField('x')" ), getFieldFunc );
1959
1960 RegisterFunc( wxT( "hasNetclass('x')" ), hasNetclassFunc );
1961 RegisterFunc( wxT( "hasExactNetclass('x')" ), hasExactNetclassFunc );
1962 RegisterFunc( wxT( "hasComponentClass('x')" ), hasComponentClassFunc );
1963
1964 RegisterFunc( wxT( "customProperty('x')" ), customPropertyFunc );
1965 RegisterFunc( wxT( "hasCustomProperty('x')" ), hasCustomPropertyFunc );
1966}
int index
const char * name
@ ERROR_OUTSIDE
constexpr int ARC_LOW_DEF
Definition base_units.h:143
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
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 NETINFO_ITEM 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:85
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition board_item.h:173
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:418
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:355
BOARD_ITEM_CONTAINER * GetParent() const
Definition board_item.h:267
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:411
SHARDED_CACHE< PTR_PTR_LAYER_CACHE_KEY, bool > m_IntersectsAreaCache
Definition board.h:1774
SHARDED_CACHE< ITEM_SELECTOR_LAYER_CACHE_KEY, bool > m_IntersectsCourtyardResultCache
Definition board.h:1777
SHARDED_CACHE< PTR_PTR_LAYER_CACHE_KEY, bool > m_EnclosedByAreaCache
Definition board.h:1776
SHARDED_CACHE< ITEM_SELECTOR_LAYER_CACHE_KEY, bool > m_EnclosedByAreaResultCache
Definition board.h:1782
SHARDED_CACHE< PTR_PTR_CACHE_KEY, bool > m_IntersectsBCourtyardCache
Definition board.h:1773
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:2797
const ZONES & Zones() const
Definition board.h:469
SHARDED_CACHE< PTR_PTR_CACHE_KEY, bool > m_IntersectsCourtyardCache
Definition board.h:1771
SHARDED_CACHE< PTR_PTR_LAYER_CACHE_KEY, bool > m_IntersectsKeepoutCache
Definition board.h:1775
const FOOTPRINTS & Footprints() const
Definition board.h:465
std::unordered_map< wxString, LSET > m_LayerExpressionCache
Definition board.h:1784
SHARDED_CACHE< PTR_PTR_CACHE_KEY, bool > m_IntersectsFCourtyardCache
Definition board.h:1772
std::unordered_map< const ZONE *, SHAPE_POLY_SET > m_DeflatedZoneOutlineCache
Definition board.h:1800
SHARDED_CACHE< ITEM_SELECTOR_LAYER_CACHE_KEY, bool > m_IntersectsFCourtyardResultCache
Definition board.h:1778
SHARDED_CACHE< ITEM_SELECTOR_LAYER_CACHE_KEY, bool > m_IntersectsKeepoutResultCache
Definition board.h:1781
SHARDED_CACHE< ITEM_FIELD_CACHE_KEY, wxString > m_ItemFieldCache
Definition board.h:1783
std::optional< std::set< const PCB_VIA * > > m_StackedMicroviaCache
Definition board.h:1792
wxString GetCurrentVariant() const
Definition board.h:530
SHARDED_CACHE< ITEM_SELECTOR_LAYER_CACHE_KEY, bool > m_IntersectsAreaResultCache
Definition board.h:1780
std::shared_mutex m_CachesMutex
Definition board.h:1768
std::shared_ptr< const FOOTPRINT_COURTYARD_INDEX > GetFootprintCourtyardIndex()
Return a spatial index of footprint courtyards, building it on first use.
Definition board.cpp:514
std::unordered_map< wxString, std::vector< ZONE * > > m_ZonesByNameCache
Definition board.h:1796
BOARD_ITEM * ResolveItem(const KIID &aID, bool aAllowNullptrReturn=false) const
Definition board.cpp:2142
BOARD_DESIGN_SETTINGS & GetDesignSettings()
Definition board.cpp:1290
SHARDED_CACHE< ITEM_SELECTOR_LAYER_CACHE_KEY, bool > m_IntersectsBCourtyardResultCache
Definition board.h:1779
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition board.h:758
DRC_RTREE * GetCopperZoneRTree(ZONE *aZone) const
Look up a zone's filled-copper R-tree.
Definition board.h:1818
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:46
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:792
Variant information for a footprint.
Definition footprint.h:178
bool HasFieldValue(const wxString &aFieldName) const
Definition footprint.h:237
wxString GetSheetname() const
Definition footprint.h:469
const FOOTPRINT_VARIANT * GetVariant(const wxString &aVariantName) const
Get a variant by name.
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:419
bool IsFlipped() const
Definition footprint.h:620
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:793
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:44
bool ContainsNetclassWithName(const wxString &netclass) const
Determines if the given netclass name is a constituent of this (maybe aggregate) netclass.
Definition netclass.cpp:336
bool NameEquals(const wxString &aName) const
Compares a name with GetName() without constructing the aggregate name.
Definition netclass.cpp:407
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:560
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)
SCOPED_LAYERSET & operator=(const SCOPED_LAYERSET &)=delete
SCOPED_LAYERSET(const SCOPED_LAYERSET &)=delete
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:822
bool IsFilled() const
Definition zone.h:306
SHAPE_POLY_SET * Outline()
Definition zone.h:421
SHAPE_POLY_SET GetBoardOutline() const
Definition zone.cpp:951
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition zone.cpp:806
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
@ NET_CHAIN_LENGTH_CONSTRAINT
@ SILK_CLEARANCE_CONSTRAINT
@ LENGTH_CONSTRAINT
@ CLEARANCE_CONSTRAINT
@ SKEW_CONSTRAINT
#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:844
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition layer_ids.h:867
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