KiCad PCB EDA Suite
pcb_expr_evaluator.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2019-2022 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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24
25#include <cstdio>
26#include <memory>
27#include <board.h>
29#include <drc/drc_rtree.h>
30#include <pcb_track.h>
31#include <pcb_group.h>
33#include <pcb_expr_evaluator.h>
34#include <wx/log.h>
35
39
40#include <drc/drc_engine.h>
41
42bool fromToFunc( LIBEVAL::CONTEXT* aCtx, void* self )
43{
44 PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
45 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
46 LIBEVAL::VALUE* result = aCtx->AllocValue();
47
48 LIBEVAL::VALUE* argTo = aCtx->Pop();
49 LIBEVAL::VALUE* argFrom = aCtx->Pop();
50
51 result->Set(0.0);
52 aCtx->Push( result );
53
54 if(!item)
55 return false;
56
57 auto ftCache = item->GetBoard()->GetConnectivity()->GetFromToCache();
58
59 if( !ftCache )
60 {
61 wxLogWarning( wxT( "Attempting to call fromTo() with non-existent from-to cache." ) );
62 return true;
63 }
64
65 if( ftCache->IsOnFromToPath( static_cast<BOARD_CONNECTED_ITEM*>( item ),
66 argFrom->AsString(), argTo->AsString() ) )
67 {
68 result->Set(1.0);
69 }
70
71 return true;
72}
73
74
75#define MISSING_LAYER_ARG( f ) wxString::Format( _( "Missing layer name argument to %s." ), f )
76
77static void existsOnLayerFunc( LIBEVAL::CONTEXT* aCtx, void *self )
78{
79 PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
80 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
81
82 LIBEVAL::VALUE* arg = aCtx->Pop();
83 LIBEVAL::VALUE* result = aCtx->AllocValue();
84
85 result->Set( 0.0 );
86 aCtx->Push( result );
87
88 if( !item )
89 return;
90
91 if( !arg )
92 {
93 if( aCtx->HasErrorCallback() )
94 aCtx->ReportError( MISSING_LAYER_ARG( wxT( "existsOnLayer()" ) ) );
95
96 return;
97 }
98
99 result->SetDeferredEval(
100 [item, arg, aCtx]() -> double
101 {
102 const wxString& layerName = arg->AsString();
103 wxPGChoices& layerMap = ENUM_MAP<PCB_LAYER_ID>::Instance().Choices();
104
105 if( aCtx->HasErrorCallback())
106 {
107 /*
108 * Interpreted version
109 */
110
111 bool anyMatch = false;
112
113 for( unsigned ii = 0; ii < layerMap.GetCount(); ++ii )
114 {
115 wxPGChoiceEntry& entry = layerMap[ ii ];
116
117 if( entry.GetText().Matches( layerName ))
118 {
119 anyMatch = true;
120
121 if( item->IsOnLayer( ToLAYER_ID( entry.GetValue())))
122 return 1.0;
123 }
124 }
125
126 if( !anyMatch )
127 {
128 aCtx->ReportError( wxString::Format( _( "Unrecognized layer '%s'" ),
129 layerName ) );
130 }
131 }
132 else
133 {
134 /*
135 * Compiled version
136 */
137
138 BOARD* board = item->GetBoard();
139 std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );
140 auto i = board->m_LayerExpressionCache.find( layerName );
141 LSET mask;
142
143 if( i == board->m_LayerExpressionCache.end() )
144 {
145 for( unsigned ii = 0; ii < layerMap.GetCount(); ++ii )
146 {
147 wxPGChoiceEntry& entry = layerMap[ ii ];
148
149 if( entry.GetText().Matches( layerName ) )
150 mask.set( ToLAYER_ID( entry.GetValue() ) );
151 }
152
153 board->m_LayerExpressionCache[ layerName ] = mask;
154 }
155 else
156 {
157 mask = i->second;
158 }
159
160 if( ( item->GetLayerSet() & mask ).any() )
161 return 1.0;
162 }
163
164 return 0.0;
165 } );
166}
167
168
169static void isPlatedFunc( LIBEVAL::CONTEXT* aCtx, void* self )
170{
171 LIBEVAL::VALUE* result = aCtx->AllocValue();
172
173 result->Set( 0.0 );
174 aCtx->Push( result );
175
176 PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
177 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
178
179 if( !item )
180 return;
181
182 if( item->Type() == PCB_PAD_T && static_cast<PAD*>( item )->GetAttribute() == PAD_ATTRIB::PTH )
183 result->Set( 1.0 );
184 else if( item->Type() == PCB_VIA_T )
185 result->Set( 1.0 );
186}
187
188
189bool collidesWithCourtyard( BOARD_ITEM* aItem, std::shared_ptr<SHAPE>& aItemShape,
190 PCB_EXPR_CONTEXT* aCtx, FOOTPRINT* aFootprint, PCB_LAYER_ID aSide )
191{
192 SHAPE_POLY_SET footprintCourtyard;
193
194 footprintCourtyard = aFootprint->GetCourtyard( aSide );
195
196 if( !aItemShape )
197 {
198 // Since rules are used for zone filling we can't rely on the filled shapes.
199 // Use the zone outline instead.
200 if( ZONE* zone = dynamic_cast<ZONE*>( aItem ) )
201 aItemShape.reset( zone->Outline()->Clone() );
202 else
203 aItemShape = aItem->GetEffectiveShape( aCtx->GetLayer() );
204 }
205
206 return footprintCourtyard.Collide( aItemShape.get() );
207};
208
209
210static bool searchFootprints( BOARD* aBoard, const wxString& aArg, PCB_EXPR_CONTEXT* aCtx,
211 std::function<bool( FOOTPRINT* )> aFunc )
212{
213 if( aArg == wxT( "A" ) )
214 {
215 FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( aCtx->GetItem( 0 ) );
216
217 if( fp && aFunc( fp ) )
218 return 1.0;
219 }
220 else if( aArg == wxT( "B" ) )
221 {
222 FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( aCtx->GetItem( 1 ) );
223
224 if( fp && aFunc( fp ) )
225 return 1.0;
226 }
227 else for( FOOTPRINT* fp : aBoard->Footprints() )
228 {
229 if( fp->GetReference().Matches( aArg ) )
230 {
231 if( aFunc( fp ) )
232 return 1.0;
233 }
234 }
235
236 return 0.0;
237}
238
239
240#define MISSING_FP_ARG( f ) \
241 wxString::Format( _( "Missing footprint argument (A, B, or reference designator) to %s." ), f )
242
243static void intersectsCourtyardFunc( LIBEVAL::CONTEXT* aCtx, void* self )
244{
245 PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
246 LIBEVAL::VALUE* arg = context->Pop();
247 LIBEVAL::VALUE* result = context->AllocValue();
248
249 result->Set( 0.0 );
250 context->Push( result );
251
252 if( !arg )
253 {
254 if( context->HasErrorCallback() )
255 context->ReportError( MISSING_FP_ARG( wxT( "intersectsCourtyard()" ) ) );
256
257 return;
258 }
259
260 PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
261 BOARD_ITEM* item = vref ? vref->GetObject( context ) : nullptr;
262
263 if( !item )
264 return;
265
266 result->SetDeferredEval(
267 [item, arg, context]() -> double
268 {
269 BOARD* board = item->GetBoard();
270 std::shared_ptr<SHAPE> itemShape;
271
272 if( searchFootprints( board, arg->AsString(), context,
273 [&]( FOOTPRINT* fp )
274 {
275 PTR_PTR_CACHE_KEY key = { fp, item };
276 std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );
277
278 auto i = board->m_IntersectsCourtyardCache.find( key );
279
280 if( i != board->m_IntersectsCourtyardCache.end() )
281 return i->second;
282
283 bool res = collidesWithCourtyard( item, itemShape, context, fp, F_Cu )
284 || collidesWithCourtyard( item, itemShape, context, fp, B_Cu );
285
286 board->m_IntersectsCourtyardCache[ key ] = res;
287 return res;
288 } ) )
289 {
290 return 1.0;
291 }
292
293 return 0.0;
294 } );
295}
296
297
298static void intersectsFrontCourtyardFunc( LIBEVAL::CONTEXT* aCtx, void* self )
299{
300 PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
301 LIBEVAL::VALUE* arg = context->Pop();
302 LIBEVAL::VALUE* result = context->AllocValue();
303
304 result->Set( 0.0 );
305 context->Push( result );
306
307 if( !arg )
308 {
309 if( context->HasErrorCallback() )
310 context->ReportError( MISSING_FP_ARG( wxT( "intersectsFrontCourtyard()" ) ) );
311
312 return;
313 }
314
315 PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
316 BOARD_ITEM* item = vref ? vref->GetObject( context ) : nullptr;
317
318 if( !item )
319 return;
320
321 result->SetDeferredEval(
322 [item, arg, context]() -> double
323 {
324 BOARD* board = item->GetBoard();
325 std::shared_ptr<SHAPE> itemShape;
326
327 if( searchFootprints( board, arg->AsString(), context,
328 [&]( FOOTPRINT* fp )
329 {
330 PTR_PTR_CACHE_KEY key = { fp, item };
331 std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );
332
333 auto i = board->m_IntersectsFCourtyardCache.find( key );
334
335 if( i != board->m_IntersectsFCourtyardCache.end() )
336 return i->second;
337
338 bool res = collidesWithCourtyard( item, itemShape, context, fp, F_Cu );
339
340 board->m_IntersectsFCourtyardCache[ key ] = res;
341 return res;
342 } ) )
343 {
344 return 1.0;
345 }
346
347 return 0.0;
348 } );
349}
350
351
352static void intersectsBackCourtyardFunc( LIBEVAL::CONTEXT* aCtx, void* self )
353{
354 PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
355 LIBEVAL::VALUE* arg = context->Pop();
356 LIBEVAL::VALUE* result = context->AllocValue();
357
358 result->Set( 0.0 );
359 context->Push( result );
360
361 if( !arg )
362 {
363 if( context->HasErrorCallback() )
364 context->ReportError( MISSING_FP_ARG( wxT( "intersectsBackCourtyard()" ) ) );
365
366 return;
367 }
368
369 PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
370 BOARD_ITEM* item = vref ? vref->GetObject( context ) : nullptr;
371
372 if( !item )
373 return;
374
375 result->SetDeferredEval(
376 [item, arg, context]() -> double
377 {
378 BOARD* board = item->GetBoard();
379 std::shared_ptr<SHAPE> itemShape;
380
381 if( searchFootprints( board, arg->AsString(), context,
382 [&]( FOOTPRINT* fp )
383 {
384 PTR_PTR_CACHE_KEY key = { fp, item };
385 std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );
386
387 auto i = board->m_IntersectsBCourtyardCache.find( key );
388
389 if( i != board->m_IntersectsBCourtyardCache.end() )
390 return i->second;
391
392 bool res = collidesWithCourtyard( item, itemShape, context, fp, B_Cu );
393
394 board->m_IntersectsBCourtyardCache[ key ] = res;
395 return res;
396 } ) )
397 {
398 return 1.0;
399 }
400
401 return 0.0;
402 } );
403}
404
405
407{
408 BOARD* board = aArea->GetBoard();
409 BOX2I areaBBox = aArea->GetBoundingBox();
410 std::shared_ptr<SHAPE> shape;
411
412 // Collisions include touching, so we need to deflate outline by enough to exclude it.
413 // This is particularly important for detecting copper fills as they will be exactly
414 // touching along the entire exclusion border.
415 SHAPE_POLY_SET areaOutline = aArea->Outline()->CloneDropTriangulation();
416 areaOutline.Deflate( board->GetDesignSettings().GetDRCEpsilon(), 0,
418
419 if( aItem->GetFlags() & HOLE_PROXY )
420 {
421 if( aItem->Type() == PCB_PAD_T )
422 {
423 return areaOutline.Collide( aItem->GetEffectiveHoleShape().get() );
424 }
425 else if( aItem->Type() == PCB_VIA_T )
426 {
427 LSET overlap = aItem->GetLayerSet() & aArea->GetLayerSet();
428
430 if( overlap.any() )
431 {
432 if( aCtx->GetLayer() == UNDEFINED_LAYER || overlap.Contains( aCtx->GetLayer() ) )
433 return areaOutline.Collide( aItem->GetEffectiveHoleShape().get() );
434 }
435 }
436
437 return false;
438 }
439
440 if( aItem->Type() == PCB_FOOTPRINT_T )
441 {
442 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem );
443
444 if( ( footprint->GetFlags() & MALFORMED_COURTYARDS ) != 0 )
445 {
446 if( aCtx->HasErrorCallback() )
447 aCtx->ReportError( _( "Footprint's courtyard is not a single, closed shape." ) );
448
449 return false;
450 }
451
452 if( ( aArea->GetLayerSet() & LSET::FrontMask() ).any() )
453 {
454 const SHAPE_POLY_SET& courtyard = footprint->GetCourtyard( F_CrtYd );
455
456 if( courtyard.OutlineCount() == 0 )
457 {
458 if( aCtx->HasErrorCallback() )
459 aCtx->ReportError( _( "Footprint has no front courtyard." ) );
460
461 return false;
462 }
463 else
464 {
465 return areaOutline.Collide( &courtyard.Outline( 0 ) );
466 }
467 }
468
469 if( ( aArea->GetLayerSet() & LSET::BackMask() ).any() )
470 {
471 const SHAPE_POLY_SET& courtyard = footprint->GetCourtyard( B_CrtYd );
472
473 if( courtyard.OutlineCount() == 0 )
474 {
475 if( aCtx->HasErrorCallback() )
476 aCtx->ReportError( _( "Footprint has no back courtyard." ) );
477
478 return false;
479 }
480 else
481 {
482 return areaOutline.Collide( &courtyard.Outline( 0 ) );
483 }
484 }
485
486 return false;
487 }
488
489 if( aItem->Type() == PCB_ZONE_T || aItem->Type() == PCB_FP_ZONE_T )
490 {
491 ZONE* zone = static_cast<ZONE*>( aItem );
492
493 if( !zone->IsFilled() )
494 return false;
495
496 DRC_RTREE* zoneRTree = board->m_CopperZoneRTreeCache[ zone ].get();
497
498 if( zoneRTree )
499 {
500 for( PCB_LAYER_ID layer : aArea->GetLayerSet().Seq() )
501 {
502 if( aCtx->GetLayer() == layer || aCtx->GetLayer() == UNDEFINED_LAYER )
503 {
504 if( zoneRTree->QueryColliding( areaBBox, &areaOutline, layer ) )
505 return true;
506 }
507 }
508 }
509
510 return false;
511 }
512 else
513 {
514 PCB_LAYER_ID layer = aCtx->GetLayer();
515
516 if( layer != UNDEFINED_LAYER && !( aArea->GetLayerSet().Contains( layer ) ) )
517 return false;
518
519 if( !shape )
520 shape = aItem->GetEffectiveShape( layer );
521
522 return areaOutline.Collide( shape.get() );
523 }
524}
525
526
527bool searchAreas( BOARD* aBoard, const wxString& aArg, PCB_EXPR_CONTEXT* aCtx,
528 std::function<bool( ZONE* )> aFunc )
529{
530 if( aArg == wxT( "A" ) )
531 {
532 return aFunc( dynamic_cast<ZONE*>( aCtx->GetItem( 0 ) ) );
533 }
534 else if( aArg == wxT( "B" ) )
535 {
536 return aFunc( dynamic_cast<ZONE*>( aCtx->GetItem( 1 ) ) );
537 }
538 else if( KIID::SniffTest( aArg ) )
539 {
540 KIID target( aArg );
541
542 for( ZONE* area : aBoard->Zones() )
543 {
544 // Only a single zone can match the UUID; exit once we find a match whether
545 // "inside" or not
546 if( area->m_Uuid == target )
547 return aFunc( area );
548 }
549
550 for( FOOTPRINT* footprint : aBoard->Footprints() )
551 {
552 for( ZONE* area : footprint->Zones() )
553 {
554 // Only a single zone can match the UUID; exit once we find a match
555 // whether "inside" or not
556 if( area->m_Uuid == target )
557 return aFunc( area );
558 }
559 }
560
561 return 0.0;
562 }
563 else // Match on zone name
564 {
565 for( ZONE* area : aBoard->Zones() )
566 {
567 if( area->GetZoneName().Matches( aArg ) )
568 {
569 // Many zones can match the name; exit only when we find an "inside"
570 if( aFunc( area ) )
571 return true;
572 }
573 }
574
575 for( FOOTPRINT* footprint : aBoard->Footprints() )
576 {
577 for( ZONE* area : footprint->Zones() )
578 {
579 // Many zones can match the name; exit only when we find an "inside"
580 if( area->GetZoneName().Matches( aArg ) )
581 {
582 if( aFunc( area ) )
583 return true;
584 }
585 }
586 }
587
588 return false;
589 }
590}
591
592
593#define MISSING_AREA_ARG( f ) \
594 wxString::Format( _( "Missing rule-area argument (A, B, or rule-area name) to %s." ), f )
595
596static void intersectsAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
597{
598 PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
599 LIBEVAL::VALUE* arg = aCtx->Pop();
600 LIBEVAL::VALUE* result = aCtx->AllocValue();
601
602 result->Set( 0.0 );
603 aCtx->Push( result );
604
605 if( !arg )
606 {
607 if( aCtx->HasErrorCallback() )
608 aCtx->ReportError( MISSING_AREA_ARG( wxT( "intersectsArea()" ) ) );
609
610 return;
611 }
612
613 PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
614 BOARD_ITEM* item = vref ? vref->GetObject( context ) : nullptr;
615
616 if( !item )
617 return;
618
619 result->SetDeferredEval(
620 [item, arg, context]() -> double
621 {
622 BOARD* board = item->GetBoard();
623 PCB_LAYER_ID layer = context->GetLayer();
624 BOX2I itemBBox = item->GetBoundingBox();
625
626 if( searchAreas( board, arg->AsString(), context,
627 [&]( ZONE* aArea )
628 {
629 if( !aArea || aArea == item || aArea->GetParent() == item )
630 return false;
631
632 if( !aArea->GetBoundingBox().Intersects( itemBBox ) )
633 return false;
634
635 std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );
636 PTR_PTR_LAYER_CACHE_KEY key = { aArea, item, layer };
637
638 auto i = board->m_IntersectsAreaCache.find( key );
639
640 if( i != board->m_IntersectsAreaCache.end() )
641 return i->second;
642
643 bool collides = collidesWithArea( item, context, aArea );
644
645 board->m_IntersectsAreaCache[ key ] = collides;
646
647 return collides;
648 } ) )
649 {
650 return 1.0;
651 }
652
653 return 0.0;
654 } );
655}
656
657
658static void enclosedByAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
659{
660 PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
661 LIBEVAL::VALUE* arg = aCtx->Pop();
662 LIBEVAL::VALUE* result = aCtx->AllocValue();
663
664 result->Set( 0.0 );
665 aCtx->Push( result );
666
667 if( !arg )
668 {
669 if( aCtx->HasErrorCallback() )
670 aCtx->ReportError( MISSING_AREA_ARG( wxT( "enclosedByArea()" ) ) );
671
672 return;
673 }
674
675 PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
676 BOARD_ITEM* item = vref ? vref->GetObject( context ) : nullptr;
677
678 if( !item )
679 return;
680
681 result->SetDeferredEval(
682 [item, arg, context]() -> double
683 {
684 BOARD* board = item->GetBoard();
685 int maxError = board->GetDesignSettings().m_MaxError;
686 PCB_LAYER_ID layer = context->GetLayer();
687 BOX2I itemBBox = item->GetBoundingBox();
688
689 if( searchAreas( board, arg->AsString(), context,
690 [&]( ZONE* aArea )
691 {
692 if( !aArea || aArea == item || aArea->GetParent() == item )
693 return false;
694
695 if( !aArea->GetBoundingBox().Intersects( itemBBox ) )
696 return false;
697
698 std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );
699 PTR_PTR_LAYER_CACHE_KEY key = { aArea, item, layer };
700
701 auto i = board->m_EnclosedByAreaCache.find( key );
702
703 if( i != board->m_EnclosedByAreaCache.end() )
704 return i->second;
705
706 SHAPE_POLY_SET itemShape;
707 bool enclosedByArea;
708
709 item->TransformShapeWithClearanceToPolygon( itemShape, layer, 0,
710 maxError, ERROR_OUTSIDE );
711
712 if( itemShape.IsEmpty() )
713 {
714 // If it's already empty then our test will have no meaning.
715 enclosedByArea = false;
716 }
717 else
718 {
719 itemShape.BooleanSubtract( *aArea->Outline(),
720 SHAPE_POLY_SET::PM_FAST );
721
722 enclosedByArea = itemShape.IsEmpty();
723 }
724
725 board->m_EnclosedByAreaCache[ key ] = enclosedByArea;
726
727 return enclosedByArea;
728 } ) )
729 {
730 return 1.0;
731 }
732
733 return 0.0;
734 } );
735}
736
737
738#define MISSING_GROUP_ARG( f ) \
739 wxString::Format( _( "Missing group name argument to %s." ), f )
740
741static void memberOfFunc( LIBEVAL::CONTEXT* aCtx, void* self )
742{
743 LIBEVAL::VALUE* arg = aCtx->Pop();
744 LIBEVAL::VALUE* result = aCtx->AllocValue();
745
746 result->Set( 0.0 );
747 aCtx->Push( result );
748
749 if( !arg )
750 {
751 if( aCtx->HasErrorCallback() )
752 aCtx->ReportError( MISSING_GROUP_ARG( wxT( "memberOf()" ) ) );
753
754 return;
755 }
756
757 PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
758 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
759
760 if( !item )
761 return;
762
763 result->SetDeferredEval(
764 [item, arg]() -> double
765 {
766 PCB_GROUP* group = item->GetParentGroup();
767
768 if( !group && item->GetParent() && item->GetParent()->Type() == PCB_FOOTPRINT_T )
769 group = item->GetParent()->GetParentGroup();
770
771 while( group )
772 {
773 if( group->GetName().Matches( arg->AsString() ) )
774 return 1.0;
775
776 group = group->GetParentGroup();
777 }
778
779 return 0.0;
780 } );
781}
782
783
784static void isMicroVia( LIBEVAL::CONTEXT* aCtx, void* self )
785{
786 PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
787 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
788 LIBEVAL::VALUE* result = aCtx->AllocValue();
789
790 result->Set( 0.0 );
791 aCtx->Push( result );
792
793 PCB_VIA* via = dyn_cast<PCB_VIA*>( item );
794
795 if( via && via->GetViaType() == VIATYPE::MICROVIA )
796 result->Set ( 1.0 );
797}
798
799
800static void isBlindBuriedViaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
801{
802 PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
803 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
804 LIBEVAL::VALUE* result = aCtx->AllocValue();
805
806 result->Set( 0.0 );
807 aCtx->Push( result );
808
809 PCB_VIA* via = dyn_cast<PCB_VIA*>( item );
810
811 if( via && via->GetViaType() == VIATYPE::BLIND_BURIED )
812 result->Set ( 1.0 );
813}
814
815
816static void isCoupledDiffPairFunc( LIBEVAL::CONTEXT* aCtx, void* self )
817{
818 PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
819 BOARD_CONNECTED_ITEM* a = dynamic_cast<BOARD_CONNECTED_ITEM*>( context->GetItem( 0 ) );
820 BOARD_CONNECTED_ITEM* b = dynamic_cast<BOARD_CONNECTED_ITEM*>( context->GetItem( 1 ) );
821 LIBEVAL::VALUE* result = aCtx->AllocValue();
822
823 result->Set( 0.0 );
824 aCtx->Push( result );
825
826 result->SetDeferredEval(
827 [a, b, context]() -> double
828 {
829 NETINFO_ITEM* netinfo = a ? a->GetNet() : nullptr;
830
831 if( !netinfo )
832 return 0.0;
833
834 wxString coupledNet;
835 wxString dummy;
836
837 if( !DRC_ENGINE::MatchDpSuffix( netinfo->GetNetname(), coupledNet, dummy ) )
838 return 0.0;
839
842 {
843 // DRC engine evaluates these singly, so we won't have a B item
844 return 1.0;
845 }
846
847 return b && b->GetNetname() == coupledNet;
848 } );
849}
850
851
852#define MISSING_DP_ARG( f ) \
853 wxString::Format( _( "Missing diff-pair name argument to %s." ), f )
854
855static void inDiffPairFunc( LIBEVAL::CONTEXT* aCtx, void* self )
856{
857 LIBEVAL::VALUE* argv = aCtx->Pop();
858 PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
859 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
860 LIBEVAL::VALUE* result = aCtx->AllocValue();
861
862 result->Set( 0.0 );
863 aCtx->Push( result );
864
865 if( !argv )
866 {
867 if( aCtx->HasErrorCallback() )
868 aCtx->ReportError( MISSING_DP_ARG( wxT( "inDiffPair()" ) ) );
869
870 return;
871 }
872
873 if( !item || !item->GetBoard() )
874 return;
875
876 result->SetDeferredEval(
877 [item, argv]() -> double
878 {
879 if( item && item->IsConnected() )
880 {
881 NETINFO_ITEM* netinfo = static_cast<BOARD_CONNECTED_ITEM*>( item )->GetNet();
882
883 wxString refName = netinfo->GetNetname();
884 wxString arg = argv->AsString();
885 wxString baseName, coupledNet;
886 int polarity = DRC_ENGINE::MatchDpSuffix( refName, coupledNet, baseName );
887
888 if( polarity != 0 && item->GetBoard()->FindNet( coupledNet ) )
889 {
890 if( baseName.Matches( arg ) )
891 return 1.0;
892
893 if( baseName.EndsWith( "_" ) && baseName.BeforeLast( '_' ).Matches( arg ) )
894 return 1.0;
895 }
896 }
897
898 return 0.0;
899 } );
900}
901
902
903static void getFieldFunc( LIBEVAL::CONTEXT* aCtx, void* self )
904{
905 LIBEVAL::VALUE* arg = aCtx->Pop();
906 PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
907 BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
908 LIBEVAL::VALUE* result = aCtx->AllocValue();
909
910 result->Set( "" );
911 aCtx->Push( result );
912
913 if( !arg )
914 {
915 if( aCtx->HasErrorCallback() )
916 {
917 aCtx->ReportError( wxString::Format( _( "Missing field name argument to %s." ),
918 wxT( "getField()" ) ) );
919 }
920
921 return;
922 }
923
924 if( !item || !item->GetBoard() )
925 return;
926
927 result->SetDeferredEval(
928 [item, arg]() -> wxString
929 {
930 if( item && item->Type() == PCB_FOOTPRINT_T )
931 {
932 FOOTPRINT* fp = static_cast<FOOTPRINT*>( item );
933
934 if( fp->HasProperty( arg->AsString() ) )
935 return fp->GetProperty( arg->AsString() );
936 }
937
938 return "";
939 } );
940}
941
942
944{
946}
947
948
950{
951 m_funcs.clear();
952
953 RegisterFunc( wxT( "existsOnLayer('x')" ), existsOnLayerFunc );
954
955 RegisterFunc( wxT( "isPlated()" ), isPlatedFunc );
956
957 RegisterFunc( wxT( "insideCourtyard('x') DEPRECATED" ), intersectsCourtyardFunc );
958 RegisterFunc( wxT( "insideFrontCourtyard('x') DEPRECATED" ), intersectsFrontCourtyardFunc );
959 RegisterFunc( wxT( "insideBackCourtyard('x') DEPRECATED" ), intersectsBackCourtyardFunc );
960 RegisterFunc( wxT( "intersectsCourtyard('x')" ), intersectsCourtyardFunc );
961 RegisterFunc( wxT( "intersectsFrontCourtyard('x')" ), intersectsFrontCourtyardFunc );
962 RegisterFunc( wxT( "intersectsBackCourtyard('x')" ), intersectsBackCourtyardFunc );
963
964 RegisterFunc( wxT( "insideArea('x') DEPRECATED" ), intersectsAreaFunc );
965 RegisterFunc( wxT( "intersectsArea('x')" ), intersectsAreaFunc );
966 RegisterFunc( wxT( "enclosedByArea('x')" ), enclosedByAreaFunc );
967
968 RegisterFunc( wxT( "isMicroVia()" ), isMicroVia );
969 RegisterFunc( wxT( "isBlindBuriedVia()" ), isBlindBuriedViaFunc );
970
971 RegisterFunc( wxT( "memberOf('x')" ), memberOfFunc );
972
973 RegisterFunc( wxT( "fromTo('x','y')" ), fromToFunc );
974 RegisterFunc( wxT( "isCoupledDiffPair()" ), isCoupledDiffPairFunc );
975 RegisterFunc( wxT( "inDiffPair('x')" ), inDiffPairFunc );
976
977 RegisterFunc( wxT( "getField('x')" ), getFieldFunc );
978}
979
980
982{
983 wxASSERT( dynamic_cast<const PCB_EXPR_CONTEXT*>( aCtx ) );
984
985 const PCB_EXPR_CONTEXT* ctx = static_cast<const PCB_EXPR_CONTEXT*>( aCtx );
986 BOARD_ITEM* item = ctx->GetItem( m_itemIndex );
987 return item;
988}
989
990
992{
993public:
995 LIBEVAL::VALUE( LayerName( aLayer ) ),
996 m_layer( aLayer )
997 {};
998
999 virtual bool EqualTo( LIBEVAL::CONTEXT* aCtx, const VALUE* b ) const override
1000 {
1001 // For boards with user-defined layer names there will be 2 entries for each layer
1002 // in the ENUM_MAP: one for the canonical layer name and one for the user layer name.
1003 // We need to check against both.
1004
1005 wxPGChoices& layerMap = ENUM_MAP<PCB_LAYER_ID>::Instance().Choices();
1006 const wxString& layerName = b->AsString();
1007 BOARD* board = static_cast<PCB_EXPR_CONTEXT*>( aCtx )->GetBoard();
1008 std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );
1009 auto i = board->m_LayerExpressionCache.find( layerName );
1010 LSET mask;
1011
1012 if( i == board->m_LayerExpressionCache.end() )
1013 {
1014 for( unsigned ii = 0; ii < layerMap.GetCount(); ++ii )
1015 {
1016 wxPGChoiceEntry& entry = layerMap[ii];
1017
1018 if( entry.GetText().Matches( layerName ) )
1019 mask.set( ToLAYER_ID( entry.GetValue() ) );
1020 }
1021
1022 board->m_LayerExpressionCache[ layerName ] = mask;
1023 }
1024 else
1025 {
1026 mask = i->second;
1027 }
1028
1029 return mask.Contains( m_layer );
1030 }
1031
1032protected:
1034};
1035
1036
1038{
1039 PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
1040
1041 if( m_itemIndex == 2 )
1042 return new PCB_LAYER_VALUE( context->GetLayer() );
1043
1044 BOARD_ITEM* item = GetObject( aCtx );
1045
1046 if( !item )
1047 return new LIBEVAL::VALUE();
1048
1049 auto it = m_matchingTypes.find( TYPE_HASH( *item ) );
1050
1051 if( it == m_matchingTypes.end() )
1052 {
1053 // Don't force user to type "A.Type == 'via' && A.Via_Type == 'buried'" when the
1054 // simpler "A.Via_Type == 'buried'" is perfectly clear. Instead, return an undefined
1055 // value when the property doesn't appear on a particular object.
1056
1057 return new LIBEVAL::VALUE();
1058 }
1059 else
1060 {
1062 return new LIBEVAL::VALUE( (double) item->Get<int>( it->second ) );
1063 else
1064 {
1065 wxString str;
1066
1067 if( !m_isEnum )
1068 {
1069 str = item->Get<wxString>( it->second );
1070 return new LIBEVAL::VALUE( str );
1071 }
1072 else
1073 {
1074 const wxAny& any = item->Get( it->second );
1075 bool valid = any.GetAs<wxString>( &str );
1076
1077 if( valid )
1078 {
1079 if( it->second->Name() == wxT( "Layer" ) )
1080 return new PCB_LAYER_VALUE( context->GetBoard()->GetLayerID( str ) );
1081 else
1082 return new LIBEVAL::VALUE( str );
1083 }
1084 }
1085
1086 return new LIBEVAL::VALUE();
1087 }
1088 }
1089}
1090
1091
1093{
1094 BOARD_CONNECTED_ITEM* item = dynamic_cast<BOARD_CONNECTED_ITEM*>( GetObject( aCtx ) );
1095
1096 if( !item )
1097 return new LIBEVAL::VALUE();
1098
1099 return new LIBEVAL::VALUE( item->GetEffectiveNetClass()->GetName() );
1100}
1101
1102
1104{
1105 BOARD_CONNECTED_ITEM* item = dynamic_cast<BOARD_CONNECTED_ITEM*>( GetObject( aCtx ) );
1106
1107 if( !item )
1108 return new LIBEVAL::VALUE();
1109
1110 return new LIBEVAL::VALUE( item->GetNetname() );
1111}
1112
1113
1115{
1116 BOARD_ITEM* item = GetObject( aCtx );
1117
1118 if( !item )
1119 return new LIBEVAL::VALUE();
1120
1121 return new LIBEVAL::VALUE( ENUM_MAP<KICAD_T>::Instance().ToString( item->Type() ) );
1122}
1123
1124
1126{
1128
1129 return registry.Get( aName.Lower() );
1130}
1131
1132
1133std::unique_ptr<LIBEVAL::VAR_REF> PCB_EXPR_UCODE::CreateVarRef( const wxString& aVar,
1134 const wxString& aField )
1135{
1137 std::unique_ptr<PCB_EXPR_VAR_REF> vref;
1138
1139 // Check for a couple of very common cases and compile them straight to "object code".
1140
1141 if( aField.CmpNoCase( wxT( "NetClass" ) ) == 0 )
1142 {
1143 if( aVar == wxT( "A" ) )
1144 return std::make_unique<PCB_EXPR_NETCLASS_REF>( 0 );
1145 else if( aVar == wxT( "B" ) )
1146 return std::make_unique<PCB_EXPR_NETCLASS_REF>( 1 );
1147 else
1148 return nullptr;
1149 }
1150 else if( aField.CmpNoCase( wxT( "NetName" ) ) == 0 )
1151 {
1152 if( aVar == wxT( "A" ) )
1153 return std::make_unique<PCB_EXPR_NETNAME_REF>( 0 );
1154 else if( aVar == wxT( "B" ) )
1155 return std::make_unique<PCB_EXPR_NETNAME_REF>( 1 );
1156 else
1157 return nullptr;
1158 }
1159 else if( aField.CmpNoCase( wxT( "Type" ) ) == 0 )
1160 {
1161 if( aVar == wxT( "A" ) )
1162 return std::make_unique<PCB_EXPR_TYPE_REF>( 0 );
1163 else if( aVar == wxT( "B" ) )
1164 return std::make_unique<PCB_EXPR_TYPE_REF>( 1 );
1165 else
1166 return nullptr;
1167 }
1168
1169 if( aVar == wxT( "A" ) || aVar == wxT( "AB" ) )
1170 vref = std::make_unique<PCB_EXPR_VAR_REF>( 0 );
1171 else if( aVar == wxT( "B" ) )
1172 vref = std::make_unique<PCB_EXPR_VAR_REF>( 1 );
1173 else if( aVar == wxT( "L" ) )
1174 vref = std::make_unique<PCB_EXPR_VAR_REF>( 2 );
1175 else
1176 return nullptr;
1177
1178 if( aField.length() == 0 ) // return reference to base object
1179 {
1180 return std::move( vref );
1181 }
1182
1183 wxString field( aField );
1184 field.Replace( wxT( "_" ), wxT( " " ) );
1185
1186 for( const PROPERTY_MANAGER::CLASS_INFO& cls : propMgr.GetAllClasses() )
1187 {
1188 if( propMgr.IsOfType( cls.type, TYPE_HASH( BOARD_ITEM ) ) )
1189 {
1190 PROPERTY_BASE* prop = propMgr.GetProperty( cls.type, field );
1191
1192 if( prop )
1193 {
1194 vref->AddAllowedClass( cls.type, prop );
1195
1196 if( prop->TypeHash() == TYPE_HASH( int ) )
1197 {
1198 vref->SetType( LIBEVAL::VT_NUMERIC );
1199 }
1200 else if( prop->TypeHash() == TYPE_HASH( wxString ) )
1201 {
1202 vref->SetType( LIBEVAL::VT_STRING );
1203 }
1204 else if ( prop->HasChoices() )
1205 { // it's an enum, we treat it as string
1206 vref->SetType( LIBEVAL::VT_STRING );
1207 vref->SetIsEnum ( true );
1208 }
1209 else
1210 {
1211 wxFAIL_MSG( wxT( "PCB_EXPR_UCODE::createVarRef: Unknown property type." ) );
1212 }
1213 }
1214 }
1215 }
1216
1217 if( vref->GetType() == LIBEVAL::VT_UNDEFINED )
1218 vref->SetType( LIBEVAL::VT_PARSE_ERROR );
1219
1220 return std::move( vref );
1221}
1222
1223
1225{
1226 if( m_items[0] )
1227 return m_items[0]->GetBoard();
1228
1229 return nullptr;
1230}
1231
1232
1234{
1235public:
1237 {
1238 }
1239
1240 virtual const std::vector<wxString>& GetSupportedUnits() const override
1241 {
1242 static const std::vector<wxString> pcbUnits = { wxT( "mil" ), wxT( "mm" ), wxT( "in" ) };
1243
1244 return pcbUnits;
1245 }
1246
1247 virtual wxString GetSupportedUnitsMessage() const override
1248 {
1249 return _( "must be mm, in, or mil" );
1250 }
1251
1252 virtual double Convert( const wxString& aString, int unitId ) const override
1253 {
1254 double v = wxAtof( aString );
1255
1256 switch( unitId )
1257 {
1261 default: return v;
1262 }
1263 };
1264};
1265
1266
1268{
1269 m_unitResolver = std::make_unique<PCB_UNIT_RESOLVER>();
1270}
1271
1272
1274 m_result( 0 ),
1275 m_compiler(),
1276 m_ucode(),
1277 m_errorStatus()
1278{
1279}
1280
1281
1283{
1284}
1285
1286
1287bool PCB_EXPR_EVALUATOR::Evaluate( const wxString& aExpr )
1288{
1289 PCB_EXPR_UCODE ucode;
1290 PCB_EXPR_CONTEXT preflightContext( NULL_CONSTRAINT, F_Cu );
1291
1292 if( !m_compiler.Compile( aExpr.ToUTF8().data(), &ucode, &preflightContext ) )
1293 return false;
1294
1295 PCB_EXPR_CONTEXT evaluationContext( NULL_CONSTRAINT, F_Cu );
1296 LIBEVAL::VALUE* result = ucode.Run( &evaluationContext );
1297
1298 if( result->GetType() == LIBEVAL::VT_NUMERIC )
1299 m_result = KiROUND( result->AsDouble() );
1300
1301 return true;
1302}
1303
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
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.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:50
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition: board_item.h:105
PCB_GROUP * GetParentGroup() const
Definition: board_item.h:62
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition: board_item.h:232
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.
Definition: board_item.cpp:197
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Definition: board_item.cpp:37
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:172
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:150
virtual void TransformShapeWithClearanceToPolygon(SHAPE_POLY_SET &aCornerBuffer, PCB_LAYER_ID aLayer, int aClearanceValue, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const
Convert the item shape to a closed polygon.
Definition: board_item.cpp:172
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
Definition: board_item.cpp:207
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:265
std::unordered_map< PTR_PTR_LAYER_CACHE_KEY, bool > m_EnclosedByAreaCache
Definition: board.h:1150
ZONES & Zones()
Definition: board.h:313
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:1392
FOOTPRINTS & Footprints()
Definition: board.h:307
const PCB_LAYER_ID GetLayerID(const wxString &aLayerName) const
Return the ID of a layer.
Definition: board.cpp:380
std::unordered_map< PTR_PTR_CACHE_KEY, bool > m_IntersectsCourtyardCache
Definition: board.h:1146
std::unordered_map< PTR_PTR_CACHE_KEY, bool > m_IntersectsFCourtyardCache
Definition: board.h:1147
std::unordered_map< wxString, LSET > m_LayerExpressionCache
Definition: board.h:1151
std::unordered_map< PTR_PTR_CACHE_KEY, bool > m_IntersectsBCourtyardCache
Definition: board.h:1148
std::unordered_map< ZONE *, std::unique_ptr< DRC_RTREE > > m_CopperZoneRTreeCache
Definition: board.h:1152
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:618
std::unordered_map< PTR_PTR_LAYER_CACHE_KEY, bool > m_IntersectsAreaCache
Definition: board.h:1149
std::mutex m_CachesMutex
Definition: board.h:1145
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:424
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:48
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:211
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition: eda_item.cpp:74
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:144
static ENUM_MAP< T > & Instance()
Definition: property.h:512
bool HasProperty(const wxString &aKey)
Definition: footprint.h:565
const SHAPE_POLY_SET & GetCourtyard(PCB_LAYER_ID aLayer) const
Used in DRC to test the courtyard area (a complex polygon).
Definition: footprint.cpp:2183
const wxString & GetProperty(const wxString &aKey)
Definition: footprint.h:564
wxAny Get(PROPERTY_BASE *aProperty) const
Definition: inspectable.h:84
Definition: kiid.h:47
static bool SniffTest(const wxString &aCandidate)
Returns true if a string has the correct formatting to be a KIID.
Definition: kiid.cpp:171
std::unique_ptr< UNIT_RESOLVER > m_unitResolver
bool Compile(const wxString &aString, UCODE *aCode, CONTEXT *aPreflightContext)
void ReportError(const wxString &aErrorMsg)
void Push(VALUE *v)
VALUE * Run(CONTEXT *ctx)
void Set(double aValue)
virtual const wxString & AsString() const
virtual double AsDouble() const
VAR_TYPE_T GetType() const
void SetDeferredEval(std::function< double()> aLambda)
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:530
LSEQ Seq(const PCB_LAYER_ID *aWishListSequence, unsigned aCount) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:411
bool Contains(PCB_LAYER_ID aLayer)
See if the layer set contains a PCB layer.
Definition: layer_ids.h:600
static LSET FrontMask()
Return a mask holding all technical layers and the external CU layer on front side.
Definition: lset.cpp:895
static LSET BackMask()
Return a mask holding all technical layers and the external CU layer on back side.
Definition: lset.cpp:902
const wxString GetName() const
Definition: netclass.h:65
Handle the data for a net.
Definition: netinfo.h:66
const wxString & GetNetname() const
Definition: netinfo.h:119
Definition: pad.h:58
void RegisterFunc(const wxString &funcSignature, LIBEVAL::FUNC_CALL_REF funcPtr)
LIBEVAL::FUNC_CALL_REF Get(const wxString &name)
std::map< wxString, LIBEVAL::FUNC_CALL_REF > m_funcs
static PCB_EXPR_BUILTIN_FUNCTIONS & Instance()
BOARD_ITEM * GetItem(int index) const
BOARD_ITEM * m_items[2]
BOARD * GetBoard() const
PCB_LAYER_ID GetLayer() const
int GetConstraint() const
bool Evaluate(const wxString &aExpr)
PCB_EXPR_COMPILER m_compiler
LIBEVAL::VALUE * GetValue(LIBEVAL::CONTEXT *aCtx) override
LIBEVAL::VALUE * GetValue(LIBEVAL::CONTEXT *aCtx) override
LIBEVAL::VALUE * GetValue(LIBEVAL::CONTEXT *aCtx) override
virtual LIBEVAL::FUNC_CALL_REF CreateFuncCall(const wxString &aName) override
virtual std::unique_ptr< LIBEVAL::VAR_REF > CreateVarRef(const wxString &aVar, const wxString &aField) override
BOARD_ITEM * GetObject(const LIBEVAL::CONTEXT *aCtx) const
std::unordered_map< TYPE_ID, PROPERTY_BASE * > m_matchingTypes
LIBEVAL::VAR_TYPE_T m_type
LIBEVAL::VALUE * GetValue(LIBEVAL::CONTEXT *aCtx) override
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:51
PCB_LAYER_VALUE(PCB_LAYER_ID aLayer)
virtual bool EqualTo(LIBEVAL::CONTEXT *aCtx, const VALUE *b) const override
virtual const std::vector< wxString > & GetSupportedUnits() const override
virtual wxString GetSupportedUnitsMessage() const override
virtual double Convert(const wxString &aString, int unitId) const override
virtual size_t TypeHash() const =0
Return type-id of the property type.
virtual bool HasChoices() const
Return true if this PROPERTY has a limited set of possible values.
Definition: property.h:218
Provide class metadata.Helper macro to map type hashes to names.
Definition: property_mgr.h:63
CLASSES_INFO GetAllClasses()
static PROPERTY_MANAGER & Instance()
Definition: property_mgr.h:65
PROPERTY_BASE * GetProperty(TYPE_ID aType, const wxString &aProperty) const
Return a property for a specific type.
bool IsOfType(TYPE_ID aDerived, TYPE_ID aBase) const
Return true if aDerived is inherited from aBase.
Represent a set of closed polygons.
@ ALLOW_ACUTE_CORNERS
just inflate the polygon. Acute angles create spikes
void Deflate(int aAmount, int aCircleSegmentsCount, CORNER_STRATEGY aCornerStrategy=ROUND_ALL_CORNERS)
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)
int OutlineCount() const
Return the number of vertices in a given outline/hole.
SHAPE_POLY_SET CloneDropTriangulation() const
Creates a new empty polygon in the set and returns its index.
Handle a list of polygons defining a copper zone.
Definition: zone.h:57
const BOX2I GetBoundingBox() const override
Definition: zone.cpp:309
bool IsFilled() const
Definition: zone.h:235
SHAPE_POLY_SET * Outline()
Definition: zone.h:305
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: zone.h:115
@ LENGTH_CONSTRAINT
Definition: drc_rule.h:63
@ NULL_CONSTRAINT
Definition: drc_rule.h:45
@ SKEW_CONSTRAINT
Definition: drc_rule.h:64
#define _(s)
#define HOLE_PROXY
Indicates the BOARD_ITEM is a proxy for its hole.
#define MALFORMED_COURTYARDS
@ ERROR_OUTSIDE
wxString LayerName(int aLayer)
Returns the default display name for a given layer.
Definition: layer_id.cpp:30
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
@ F_CrtYd
Definition: layer_ids.h:117
@ B_Cu
Definition: layer_ids.h:95
@ B_CrtYd
Definition: layer_ids.h:116
@ UNDEFINED_LAYER
Definition: layer_ids.h:60
@ F_Cu
Definition: layer_ids.h:64
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:926
double DoubleValueFromString(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Function DoubleValueFromString converts aTextValue to a double.
Definition: eda_units.cpp:451
std::function< void(CONTEXT *, void *)> FUNC_CALL_REF
@ PTH
Plated through hole pad.
static void intersectsFrontCourtyardFunc(LIBEVAL::CONTEXT *aCtx, void *self)
#define MISSING_LAYER_ARG(f)
bool collidesWithCourtyard(BOARD_ITEM *aItem, std::shared_ptr< SHAPE > &aItemShape, PCB_EXPR_CONTEXT *aCtx, FOOTPRINT *aFootprint, PCB_LAYER_ID aSide)
static void intersectsBackCourtyardFunc(LIBEVAL::CONTEXT *aCtx, void *self)
bool collidesWithArea(BOARD_ITEM *aItem, PCB_EXPR_CONTEXT *aCtx, ZONE *aArea)
#define MISSING_AREA_ARG(f)
static void isCoupledDiffPairFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void isPlatedFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void existsOnLayerFunc(LIBEVAL::CONTEXT *aCtx, void *self)
#define MISSING_GROUP_ARG(f)
static void isBlindBuriedViaFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static bool searchFootprints(BOARD *aBoard, const wxString &aArg, PCB_EXPR_CONTEXT *aCtx, std::function< bool(FOOTPRINT *)> aFunc)
#define MISSING_DP_ARG(f)
static void enclosedByAreaFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void memberOfFunc(LIBEVAL::CONTEXT *aCtx, void *self)
static void isMicroVia(LIBEVAL::CONTEXT *aCtx, void *self)
static void getFieldFunc(LIBEVAL::CONTEXT *aCtx, void *self)
bool fromToFunc(LIBEVAL::CONTEXT *aCtx, void *self)
bool searchAreas(BOARD *aBoard, const wxString &aArg, PCB_EXPR_CONTEXT *aCtx, std::function< bool(ZONE *)> aFunc)
static void intersectsCourtyardFunc(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)
@ BLIND_BURIED
BOARD * GetBoard()
#define TYPE_HASH(x)
Definition: property.h:60
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
static LIB_SYMBOL * dummy()
Used to draw a dummy shape when a LIB_SYMBOL is not found in library.
Definition: sch_symbol.cpp:74
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:102
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:112
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition: typeinfo.h:86
@ PCB_FP_ZONE_T
class ZONE, managed by a footprint
Definition: typeinfo.h:100
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:80