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