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-2020 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>
28 #include <track.h>
29 #include <geometry/shape_segment.h>
30 #include <pcb_expr_evaluator.h>
31 
35 
36 #include <drc/drc_engine.h>
37 #include <geometry/shape_circle.h>
38 
39 bool exprFromTo( LIBEVAL::CONTEXT* aCtx, void* self )
40 {
41  PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
42  BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
43  LIBEVAL::VALUE* result = aCtx->AllocValue();
44 
45  LIBEVAL::VALUE* argTo = aCtx->Pop();
46  LIBEVAL::VALUE* argFrom = aCtx->Pop();
47 
48  result->Set(0.0);
49  aCtx->Push( result );
50 
51  if(!item)
52  return false;
53 
54  auto ftCache = item->GetBoard()->GetConnectivity()->GetFromToCache();
55 
56  if( !ftCache )
57  {
58  wxLogWarning( "Attempting to call fromTo() with non-existent from-to cache, aborting...");
59  return true;
60  }
61 
62  if( ftCache->IsOnFromToPath( static_cast<BOARD_CONNECTED_ITEM*>( item ),
63  argFrom->AsString(), argTo->AsString() ) )
64  {
65  result->Set(1.0);
66  }
67 
68  return true;
69 }
70 
71 
72 static void existsOnLayer( LIBEVAL::CONTEXT* aCtx, void *self )
73 {
74  PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
75  BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
76 
77  LIBEVAL::VALUE* arg = aCtx->Pop();
78  LIBEVAL::VALUE* result = aCtx->AllocValue();
79 
80  result->Set( 0.0 );
81  aCtx->Push( result );
82 
83  if( !item )
84  return;
85 
86  if( !arg )
87  {
88  aCtx->ReportError( wxString::Format( _( "Missing argument to '%s'" ),
89  wxT( "existsOnLayer()" ) ) );
90  return;
91  }
92 
93  wxString layerName = arg->AsString();
94  wxPGChoices& layerMap = ENUM_MAP<PCB_LAYER_ID>::Instance().Choices();
95  bool anyMatch = false;
96 
97  for( unsigned ii = 0; ii < layerMap.GetCount(); ++ii )
98  {
99  wxPGChoiceEntry& entry = layerMap[ii];
100 
101  if( entry.GetText().Matches( layerName ) )
102  {
103  anyMatch = true;
104 
105  if( item->IsOnLayer( ToLAYER_ID( entry.GetValue() ) ) )
106  {
107  result->Set( 1.0 );
108  return;
109  }
110  }
111  }
112 
113  if( !anyMatch )
114  aCtx->ReportError( wxString::Format( _( "Unrecognized layer '%s'" ), layerName ) );
115 }
116 
117 
118 static void isPlated( LIBEVAL::CONTEXT* aCtx, void* self )
119 {
120  LIBEVAL::VALUE* result = aCtx->AllocValue();
121 
122  result->Set( 0.0 );
123  aCtx->Push( result );
124 
125  PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
126  BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
127 
128  if( !item )
129  return;
130 
131  if( item->Type() == PCB_PAD_T && static_cast<PAD*>( item )->GetAttribute() == PAD_ATTRIB_PTH )
132  result->Set( 1.0 );
133  else if( item->Type() == PCB_VIA_T )
134  result->Set( 1.0 );
135 }
136 
137 
138 static void insideCourtyard( LIBEVAL::CONTEXT* aCtx, void* self )
139 {
140  PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
141  LIBEVAL::VALUE* arg = aCtx->Pop();
142  LIBEVAL::VALUE* result = aCtx->AllocValue();
143 
144  result->Set( 0.0 );
145  aCtx->Push( result );
146 
147  if( !arg )
148  {
149  aCtx->ReportError( wxString::Format( _( "Missing argument to '%s'" ),
150  wxT( "insideCourtyard()" ) ) );
151  return;
152  }
153 
154  PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
155  BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
156 
157  if( !item )
158  return;
159 
160  BOARD* board = item->GetBoard();
161  EDA_RECT itemBBox;
162  std::shared_ptr<SHAPE> shape;
163 
164  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
165  itemBBox = static_cast<ZONE*>( item )->GetCachedBoundingBox();
166  else
167  itemBBox = item->GetBoundingBox();
168 
169  auto realInsideFootprint =
170  [&]( FOOTPRINT* footprint ) -> bool
171  {
172  SHAPE_POLY_SET footprintCourtyard;
173 
174  if( footprint->IsFlipped() )
175  footprintCourtyard = footprint->GetPolyCourtyardBack();
176  else
177  footprintCourtyard = footprint->GetPolyCourtyardFront();
178 
179  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
180  {
181  // A zone must be entirely inside the courtyard to be considered
182  if( !footprint->GetBoundingBox().Contains( itemBBox ) )
183  return false;
184  }
185  else
186  {
187  if( !footprint->GetBoundingBox().Intersects( itemBBox ) )
188  return false;
189  }
190 
191  if( !shape )
192  shape = item->GetEffectiveShape( context->GetLayer() );
193 
194  return footprintCourtyard.Collide( shape.get() );
195  };
196 
197  auto insideFootprint =
198  [&]( FOOTPRINT* footprint ) -> bool
199  {
200  if( !footprint )
201  return false;
202 
203  std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );
204  std::pair<BOARD_ITEM*, BOARD_ITEM*> key( footprint, item );
205  auto i = board->m_InsideCourtyardCache.find( key );
206 
207  if( i != board->m_InsideCourtyardCache.end() )
208  return i->second;
209 
210  bool isInside = realInsideFootprint( footprint );
211 
212  board->m_InsideCourtyardCache[ key ] = isInside;
213  return isInside;
214  };
215 
216  if( arg->AsString() == "A" )
217  {
218  if( insideFootprint( dynamic_cast<FOOTPRINT*>( context->GetItem( 0 ) ) ) )
219  result->Set( 1.0 );
220  }
221  else if( arg->AsString() == "B" )
222  {
223  if( insideFootprint( dynamic_cast<FOOTPRINT*>( context->GetItem( 1 ) ) ) )
224  result->Set( 1.0 );
225  }
226  else
227  {
228  for( FOOTPRINT* candidate : board->Footprints() )
229  {
230  if( candidate->GetReference().Matches( arg->AsString() ) )
231  {
232  if( insideFootprint( candidate ) )
233  {
234  result->Set( 1.0 );
235  return;
236  }
237  }
238  }
239  }
240 }
241 
242 
243 static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self )
244 {
245  PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
246  LIBEVAL::VALUE* arg = aCtx->Pop();
247  LIBEVAL::VALUE* result = aCtx->AllocValue();
248 
249  result->Set( 0.0 );
250  aCtx->Push( result );
251 
252  if( !arg )
253  {
254  aCtx->ReportError( wxString::Format( _( "Missing argument to '%s'" ),
255  wxT( "insideArea()" ) ) );
256  return;
257  }
258 
259  PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
260  BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
261 
262  if( !item )
263  return;
264 
265  BOARD* board = item->GetBoard();
266  EDA_RECT itemBBox;
267  std::shared_ptr<SHAPE> shape;
268 
269  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
270  itemBBox = static_cast<ZONE*>( item )->GetCachedBoundingBox();
271  else
272  itemBBox = item->GetBoundingBox();
273 
274  auto realInsideZone =
275  [&]( ZONE* zone ) -> bool
276  {
277  if( !zone->GetCachedBoundingBox().Intersects( itemBBox ) )
278  return false;
279 
280  // Collisions include touching, so we need to deflate outline by enough to
281  // exclude touching. This is particularly important for detecting copper fills
282  // as they will be exactly touching along the entire border.
283  SHAPE_POLY_SET zoneOutline = *zone->Outline();
284  zoneOutline.Deflate( Millimeter2iu( 0.001 ), 4 );
285 
286  if( item->GetFlags() & HOLE_PROXY )
287  {
288  if( item->Type() == PCB_PAD_T )
289  {
290  PAD* pad = static_cast<PAD*>( item );
291  const SHAPE_SEGMENT* holeShape = pad->GetEffectiveHoleShape();
292 
293  return zoneOutline.Collide( holeShape );
294  }
295  else if( item->Type() == PCB_VIA_T )
296  {
297  VIA* via = static_cast<VIA*>( item );
298  const SHAPE_CIRCLE holeShape( via->GetPosition(), via->GetDrillValue() );
299 
300  return zoneOutline.Collide( &holeShape );
301  }
302 
303  return false;
304  }
305 
306  if( item->Type() == PCB_FOOTPRINT_T )
307  {
308  FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
309 
310  if( ( footprint->GetFlags() & MALFORMED_COURTYARDS ) != 0 )
311  {
312  aCtx->ReportError( _( "Footprint's courtyard is not a single, closed shape." ) );
313  return false;
314  }
315 
316  if( ( zone->GetLayerSet() & LSET::FrontMask() ).any() )
317  {
318  SHAPE_POLY_SET courtyard = footprint->GetPolyCourtyardFront();
319 
320  if( courtyard.OutlineCount() == 0 )
321  {
322  aCtx->ReportError( _( "Footprint has no front courtyard." ) );
323  return false;
324  }
325  else
326  {
327  return zoneOutline.Collide( &courtyard.Outline( 0 ) );
328  }
329  }
330 
331  if( ( zone->GetLayerSet() & LSET::BackMask() ).any() )
332  {
333  SHAPE_POLY_SET courtyard = footprint->GetPolyCourtyardBack();
334 
335  if( courtyard.OutlineCount() == 0 )
336  {
337  aCtx->ReportError( _( "Footprint has no back courtyard." ) );
338  return false;
339  }
340  else
341  {
342  return zoneOutline.Collide( &courtyard.Outline( 0 ) );
343  }
344  }
345 
346  return false;
347  }
348 
349  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
350  {
351  ZONE* itemZone = static_cast<ZONE*>( item );
352 
353  if( !itemZone->IsFilled() )
354  return false;
355 
356  DRC_RTREE* itemRTree = board->m_CopperZoneRTrees[ itemZone ].get();
357 
358  for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
359  {
360  if( itemRTree->QueryColliding( zone->GetCachedBoundingBox(), &zoneOutline,
361  layer, 0, nullptr, nullptr ) )
362  {
363  return true;
364  }
365  }
366 
367  return false;
368  }
369  else
370  {
371  if( !shape )
372  shape = item->GetEffectiveShape( context->GetLayer() );
373 
374  return zoneOutline.Collide( shape.get() );
375  }
376  };
377 
378  auto insideZone =
379  [&]( ZONE* zone ) -> bool
380  {
381  if( !zone || zone == item || zone->GetParent() == item )
382  return false;
383 
384  std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );
385  std::pair<BOARD_ITEM*, BOARD_ITEM*> key( zone, item );
386  auto i = board->m_InsideAreaCache.find( key );
387 
388  if( i != board->m_InsideAreaCache.end() )
389  return i->second;
390 
391  bool isInside = realInsideZone( zone );
392 
393  board->m_InsideAreaCache[ key ] = isInside;
394  return isInside;
395  };
396 
397  if( arg->AsString() == "A" )
398  {
399  if( insideZone( dynamic_cast<ZONE*>( context->GetItem( 0 ) ) ) )
400  result->Set( 1.0 );
401  }
402  else if( arg->AsString() == "B" )
403  {
404  if( insideZone( dynamic_cast<ZONE*>( context->GetItem( 1 ) ) ) )
405  result->Set( 1.0 );
406  }
407  else if( KIID::SniffTest( arg->AsString() ) )
408  {
409  KIID target( arg->AsString() );
410 
411  for( ZONE* candidate : board->Zones() )
412  {
413  // Only a single zone can match the UUID; exit once we find a match whether
414  // "inside" or not
415  if( candidate->m_Uuid == target )
416  {
417  if( insideZone( candidate ) )
418  result->Set( 1.0 );
419 
420  return;
421  }
422  }
423 
424  for( FOOTPRINT* footprint : board->Footprints() )
425  {
426  for( ZONE* candidate : footprint->Zones() )
427  {
428  // Only a single zone can match the UUID; exit once we find a match whether
429  // "inside" or not
430  if( candidate->m_Uuid == target )
431  {
432  if( insideZone( candidate ) )
433  result->Set( 1.0 );
434 
435  return;
436  }
437  }
438  }
439  }
440  else // Match on zone name
441  {
442  for( ZONE* candidate : board->Zones() )
443  {
444  if( candidate->GetZoneName().Matches( arg->AsString() ) )
445  {
446  // Many zones can match the name; exit only when we find an "inside"
447  if( insideZone( candidate ) )
448  {
449  result->Set( 1.0 );
450  return;
451  }
452  }
453  }
454 
455  for( FOOTPRINT* footprint : board->Footprints() )
456  {
457  for( ZONE* candidate : footprint->Zones() )
458  {
459  // Many zones can match the name; exit only when we find an "inside"
460  if( candidate->GetZoneName().Matches( arg->AsString() ) )
461  {
462  if( insideZone( candidate ) )
463  {
464  result->Set( 1.0 );
465  return;
466  }
467  }
468  }
469  }
470  }
471 }
472 
473 
474 static void memberOf( LIBEVAL::CONTEXT* aCtx, void* self )
475 {
476  LIBEVAL::VALUE* arg = aCtx->Pop();
477  LIBEVAL::VALUE* result = aCtx->AllocValue();
478 
479  result->Set( 0.0 );
480  aCtx->Push( result );
481 
482  if( !arg )
483  {
484  aCtx->ReportError( wxString::Format( _( "Missing argument to '%s'" ),
485  wxT( "memberOf()" ) ) );
486  return;
487  }
488 
489  PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
490  BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
491 
492  if( !item )
493  return;
494 
495  PCB_GROUP* group = item->GetParentGroup();
496 
497  if( !group && item->GetParent() && item->GetParent()->Type() == PCB_FOOTPRINT_T )
498  group = item->GetParent()->GetParentGroup();
499 
500  while( group )
501  {
502  if( group->GetName().Matches( arg->AsString() ) )
503  {
504  result->Set( 1.0 );
505  return;
506  }
507 
508  group = group->GetParentGroup();
509  }
510 }
511 
512 
513 static void isMicroVia( LIBEVAL::CONTEXT* aCtx, void* self )
514 {
515  PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
516  BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
517  LIBEVAL::VALUE* result = aCtx->AllocValue();
518 
519  result->Set( 0.0 );
520  aCtx->Push( result );
521 
522  auto via = dyn_cast<VIA*>( item );
523 
524  if( via && via->GetViaType() == VIATYPE::MICROVIA )
525  {
526  result->Set ( 1.0 );
527  }
528 }
529 
530 
531 static void isBlindBuriedVia( LIBEVAL::CONTEXT* aCtx, void* self )
532 {
533  PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
534  BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
535  LIBEVAL::VALUE* result = aCtx->AllocValue();
536 
537  result->Set( 0.0 );
538  aCtx->Push( result );
539 
540  auto via = dyn_cast<VIA*>( item );
541 
542  if( via && via->GetViaType() == VIATYPE::BLIND_BURIED )
543  {
544  result->Set ( 1.0 );
545  }
546 }
547 
548 
549 static void isDiffPair( LIBEVAL::CONTEXT* aCtx, void* self )
550 {
551  PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
552  BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
553  LIBEVAL::VALUE* result = aCtx->AllocValue();
554 
555  result->Set( 0.0 );
556  aCtx->Push( result );
557 
558  if( item && item->IsConnected() )
559  {
560  NETINFO_ITEM* netinfo = static_cast<BOARD_CONNECTED_ITEM*>( item )->GetNet();
561  int dummy_p, dummy_n;
562 
563  if( netinfo && DRC_ENGINE::IsNetADiffPair( item->GetBoard(), netinfo, dummy_p, dummy_n ) )
564  result->Set( 1.0 );
565  }
566 }
567 
568 
569 static void inDiffPair( LIBEVAL::CONTEXT* aCtx, void* self )
570 {
571  LIBEVAL::VALUE* arg = aCtx->Pop();
572  PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
573  BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
574  LIBEVAL::VALUE* result = aCtx->AllocValue();
575 
576  result->Set( 0.0 );
577  aCtx->Push( result );
578 
579  if( !arg )
580  {
581  aCtx->ReportError( wxString::Format( _( "Missing argument to '%s'" ),
582  wxT( "inDiffPair()" ) ) );
583  return;
584  }
585 
586  if( item && item->IsConnected() )
587  {
588  NETINFO_ITEM* netinfo = static_cast<BOARD_CONNECTED_ITEM*>( item )->GetNet();
589 
590  wxString refName = netinfo->GetNetname();
591  wxString baseName, coupledNet;
592 
593  int polarity = DRC_ENGINE::MatchDpSuffix( refName, coupledNet, baseName );
594 
595  if( polarity == 0 )
596  return;
597 
598  if( baseName.Matches( arg->AsString() ) )
599  result->Set( 1.0 );
600  }
601 }
602 
603 
605 {
607 }
608 
609 
611 {
612  m_funcs.clear();
613  RegisterFunc( "existsOnLayer('x')", existsOnLayer );
614  RegisterFunc( "isPlated()", isPlated );
615  RegisterFunc( "insideCourtyard('x')", insideCourtyard );
616  RegisterFunc( "insideArea('x')", insideArea );
617  RegisterFunc( "isMicroVia()", isMicroVia );
618  RegisterFunc( "isBlindBuriedVia()", isBlindBuriedVia );
619  RegisterFunc( "memberOf('x')", memberOf );
620  RegisterFunc( "fromTo('x','y')", exprFromTo );
621  RegisterFunc( "isDiffPair()", isDiffPair );
622  RegisterFunc( "inDiffPair('x')", inDiffPair );
623 }
624 
625 
627 {
628  wxASSERT( dynamic_cast<const PCB_EXPR_CONTEXT*>( aCtx ) );
629 
630  const PCB_EXPR_CONTEXT* ctx = static_cast<const PCB_EXPR_CONTEXT*>( aCtx );
631  BOARD_ITEM* item = ctx->GetItem( m_itemIndex );
632  return item;
633 }
634 
635 
637 {
638 public:
640  LIBEVAL::VALUE( double( aLayer ) )
641  {};
642 
643  virtual bool EqualTo( const VALUE* b ) const override
644  {
645  // For boards with user-defined layer names there will be 2 entries for each layer
646  // in the ENUM_MAP: one for the canonical layer name and one for the user layer name.
647  // We need to check against both.
648 
649  wxPGChoices& layerMap = ENUM_MAP<PCB_LAYER_ID>::Instance().Choices();
650  PCB_LAYER_ID layerId = ToLAYER_ID( (int) AsDouble() );
651 
652  for( unsigned ii = 0; ii < layerMap.GetCount(); ++ii )
653  {
654  wxPGChoiceEntry& entry = layerMap[ii];
655 
656  if( entry.GetValue() == layerId && entry.GetText().Matches( b->AsString() ) )
657  return true;
658  }
659 
660  return false;
661  }
662 };
663 
664 
666 {
667  if( m_itemIndex == 2 )
668  {
669  PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
670  return PCB_LAYER_VALUE( context->GetLayer() );
671  }
672 
673  BOARD_ITEM* item = GetObject( aCtx );
674 
675  if( !item )
676  return LIBEVAL::VALUE();
677 
678  auto it = m_matchingTypes.find( TYPE_HASH( *item ) );
679 
680  if( it == m_matchingTypes.end() )
681  {
682  // Don't force user to type "A.Type == 'via' && A.Via_Type == 'buried'" when the
683  // simplier "A.Via_Type == 'buried'" is perfectly clear. Instead, return an undefined
684  // value when the property doesn't appear on a particular object.
685 
686  return LIBEVAL::VALUE();
687  }
688  else
689  {
690  if( m_type == LIBEVAL::VT_NUMERIC )
691  return LIBEVAL::VALUE( (double) item->Get<int>( it->second ) );
692  else
693  {
694  wxString str;
695 
696  if( !m_isEnum )
697  {
698  str = item->Get<wxString>( it->second );
699  }
700  else
701  {
702  const wxAny& any = item->Get( it->second );
703  any.GetAs<wxString>( &str );
704  }
705 
706  if( str == "UNDEFINED" )
707  return LIBEVAL::VALUE();
708  else
709  return LIBEVAL::VALUE( str );
710  }
711  }
712 }
713 
714 
716 {
717  BOARD_ITEM* item = GetObject( aCtx );
718 
719  if( !item )
720  return LIBEVAL::VALUE();
721 
722  if( item->IsConnected() )
723  return LIBEVAL::VALUE( static_cast<BOARD_CONNECTED_ITEM*>( item )->GetNetClassName() );
724  else
725  return LIBEVAL::VALUE();
726 }
727 
728 
730 {
731  BOARD_ITEM* item = GetObject( aCtx );
732 
733  if( !item )
734  return LIBEVAL::VALUE();
735 
736  if( item->IsConnected() )
737  return LIBEVAL::VALUE( static_cast<BOARD_CONNECTED_ITEM*>( item )->GetNetname() );
738  else
739  return LIBEVAL::VALUE();
740 }
741 
742 
744 {
746 
747  return registry.Get( aName.Lower() );
748 }
749 
750 
751 std::unique_ptr<LIBEVAL::VAR_REF> PCB_EXPR_UCODE::CreateVarRef( const wxString& aVar,
752  const wxString& aField )
753 {
755  std::unique_ptr<PCB_EXPR_VAR_REF> vref;
756 
757  // Check for a couple of very common cases and compile them straight to "object code".
758 
759  if( aField.CmpNoCase( "NetClass" ) == 0 )
760  {
761  if( aVar == "A" )
762  return std::make_unique<PCB_EXPR_NETCLASS_REF>( 0 );
763  else if( aVar == "B" )
764  return std::make_unique<PCB_EXPR_NETCLASS_REF>( 1 );
765  else
766  return nullptr;
767  }
768  else if( aField.CmpNoCase( "NetName" ) == 0 )
769  {
770  if( aVar == "A" )
771  return std::make_unique<PCB_EXPR_NETNAME_REF>( 0 );
772  else if( aVar == "B" )
773  return std::make_unique<PCB_EXPR_NETNAME_REF>( 1 );
774  else
775  return nullptr;
776  }
777 
778  if( aVar == "A" )
779  vref = std::make_unique<PCB_EXPR_VAR_REF>( 0 );
780  else if( aVar == "B" )
781  vref = std::make_unique<PCB_EXPR_VAR_REF>( 1 );
782  else if( aVar == "L" )
783  vref = std::make_unique<PCB_EXPR_VAR_REF>( 2 );
784  else
785  return nullptr;
786 
787  if( aField.length() == 0 ) // return reference to base object
788  {
789  return std::move( vref );
790  }
791 
792  wxString field( aField );
793  field.Replace( "_", " " );
794 
795  for( const PROPERTY_MANAGER::CLASS_INFO& cls : propMgr.GetAllClasses() )
796  {
797  if( propMgr.IsOfType( cls.type, TYPE_HASH( BOARD_ITEM ) ) )
798  {
799  PROPERTY_BASE* prop = propMgr.GetProperty( cls.type, field );
800 
801  if( prop )
802  {
803  vref->AddAllowedClass( cls.type, prop );
804 
805  if( prop->TypeHash() == TYPE_HASH( int ) )
806  {
807  vref->SetType( LIBEVAL::VT_NUMERIC );
808  }
809  else if( prop->TypeHash() == TYPE_HASH( wxString ) )
810  {
811  vref->SetType( LIBEVAL::VT_STRING );
812  }
813  else if ( prop->HasChoices() )
814  { // it's an enum, we treat it as string
815  vref->SetType( LIBEVAL::VT_STRING );
816  vref->SetIsEnum ( true );
817  }
818  else
819  {
820  wxFAIL_MSG( "PCB_EXPR_UCODE::createVarRef: Unknown property type." );
821  }
822  }
823  }
824  }
825 
826  if( vref->GetType() == LIBEVAL::VT_UNDEFINED )
827  vref->SetType( LIBEVAL::VT_PARSE_ERROR );
828 
829  return std::move( vref );
830 }
831 
832 
834 {
835 public:
837  {
838  }
839 
840  virtual const std::vector<wxString>& GetSupportedUnits() const override
841  {
842  static const std::vector<wxString> pcbUnits = { "mil", "mm", "in" };
843 
844  return pcbUnits;
845  }
846 
847  virtual wxString GetSupportedUnitsMessage() const override
848  {
849  return _( "must be mm, in, or mil" );
850  }
851 
852  virtual double Convert( const wxString& aString, int unitId ) const override
853  {
854  double v = wxAtof( aString );
855 
856  switch( unitId )
857  {
858  case 0: return DoubleValueFromString( EDA_UNITS::MILS, aString );
859  case 1: return DoubleValueFromString( EDA_UNITS::MILLIMETRES, aString );
860  case 2: return DoubleValueFromString( EDA_UNITS::INCHES, aString );
861  default: return v;
862  }
863  };
864 };
865 
866 
868 {
869  m_unitResolver = std::make_unique<PCB_UNIT_RESOLVER>();
870 }
871 
872 
874 {
875  m_result = 0;
876 }
877 
879 {
880 }
881 
882 
883 bool PCB_EXPR_EVALUATOR::Evaluate( const wxString& aExpr )
884 {
885  PCB_EXPR_UCODE ucode;
886  PCB_EXPR_CONTEXT preflightContext( F_Cu );
887 
888  if( !m_compiler.Compile( aExpr.ToUTF8().data(), &ucode, &preflightContext ) )
889  return false;
890 
891  PCB_EXPR_CONTEXT evaluationContext( F_Cu );
892  LIBEVAL::VALUE* result = ucode.Run( &evaluationContext );
893 
894  if( result->GetType() == LIBEVAL::VT_NUMERIC )
895  m_result = KiROUND( result->AsDouble() );
896 
897  return true;
898 }
899 
PCB_GROUP * GetParentGroup() const
Definition: board_item.h:93
BOARD_ITEM * GetItem(int index) const
Definition: track.h:343
virtual bool HasChoices() const
Return true if this PROPERTY has a limited set of possible values.
Definition: property.h:214
static PROPERTY_MANAGER & Instance()
Definition: property_mgr.h:66
const SHAPE_POLY_SET & GetPolyCourtyardFront() const
Used in DRC to test the courtyard area (a complex polygon).
Definition: footprint.h:648
#define TYPE_HASH(x)
Definition: property.h:57
int OutlineCount() const
Return the number of vertices in a given outline/hole.
std::unique_ptr< UNIT_RESOLVER > m_unitResolver
std::map< std::pair< BOARD_ITEM *, BOARD_ITEM * >, bool > m_InsideCourtyardCache
Definition: board.h:1151
ZONES & Zones()
Definition: board.h:309
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:82
virtual LIBEVAL::FUNC_CALL_REF CreateFuncCall(const wxString &aName) override
static bool IsNetADiffPair(BOARD *aBoard, NETINFO_ITEM *aNet, int &aNetP, int &aNetN)
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:50
void RegisterFunc(const wxString &funcSignature, LIBEVAL::FUNC_CALL_REF funcPtr)
static void isDiffPair(LIBEVAL::CONTEXT *aCtx, void *self)
LIBEVAL::FUNC_CALL_REF Get(const wxString &name)
virtual size_t TypeHash() const =0
Return type-id of the property type.
static ENUM_MAP< T > & Instance()
Definition: property.h:508
static void isPlated(LIBEVAL::CONTEXT *aCtx, void *self)
bool exprFromTo(LIBEVAL::CONTEXT *aCtx, void *self)
const SHAPE_SEGMENT * GetEffectiveHoleShape() const
Return a SHAPE object representing the pad's hole.
Definition: pcbnew/pad.cpp:286
class PAD, a pad in a footprint
Definition: typeinfo.h:89
bool IsFilled() const
Definition: zone.h:237
static void inDiffPair(LIBEVAL::CONTEXT *aCtx, void *self)
static int MatchDpSuffix(const wxString &aNetName, wxString &aComplementNet, wxString &aBaseDpName)
Checks if the given net is a diff pair, returning its polarity and complement if so.
virtual wxString GetSupportedUnitsMessage() const override
static LSET FrontMask()
Return a mask holding all technical layers and the external CU layer on front side.
Definition: lset.cpp:874
std::mutex m_CachesMutex
Definition: board.h:1150
bool Compile(const wxString &aString, UCODE *aCode, CONTEXT *aPreflightContext)
std::map< wxString, LIBEVAL::FUNC_CALL_REF > m_funcs
PROPERTY_BASE * GetProperty(TYPE_ID aType, const wxString &aProperty) const
Return a property for a specific type.
Definition: kiid.h:44
PCB_LAYER_ID
A quick note on layer IDs:
PCB_EXPR_COMPILER m_compiler
VAR_TYPE_T GetType() const
std::function< void(CONTEXT *, void *)> FUNC_CALL_REF
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition: board_item.h:231
static void isBlindBuriedVia(LIBEVAL::CONTEXT *aCtx, void *self)
int GetDrillValue() const
Function GetDrillValue "calculates" the drill value for vias (m-Drill if > 0, or default drill value ...
Definition: track.cpp:173
wxString GetName() const
Definition: pcb_group.h:65
LIBEVAL::VAR_TYPE_T m_type
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
static bool SniffTest(const wxString &aCandidate)
Definition: kiid.cpp:135
static void existsOnLayer(LIBEVAL::CONTEXT *aCtx, void *self)
FOOTPRINTS & Footprints()
Definition: board.h:303
wxAny Get(PROPERTY_BASE *aProperty)
Definition: inspectable.h:84
virtual std::unique_ptr< LIBEVAL::VAR_REF > CreateVarRef(const wxString &aVar, const wxString &aField) override
virtual BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Definition: board_item.cpp:46
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:414
void Deflate(int aAmount, int aCircleSegmentsCount, CORNER_STRATEGY aCornerStrategy=ROUND_ALL_CORNERS)
const wxString & GetNetname() const
Definition: netinfo.h:119
const SHAPE_POLY_SET & GetPolyCourtyardBack() const
Definition: footprint.h:649
#define MALFORMED_COURTYARDS
Definition: eda_item.h:125
ZONE handles a list of polygons defining a copper zone.
Definition: zone.h:57
class ZONE, a copper pour area
Definition: typeinfo.h:105
PCB_LAYER_VALUE(PCB_LAYER_ID aLayer)
static void insideArea(LIBEVAL::CONTEXT *aCtx, void *self)
class FOOTPRINT, a footprint
Definition: typeinfo.h:88
LIBEVAL::VALUE GetValue(LIBEVAL::CONTEXT *aCtx) override
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
#define HOLE_PROXY
Indicates the BOARD_ITEM is a proxy for its hole.
Definition: eda_item.h:128
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,...
virtual LIBEVAL::VALUE GetValue(LIBEVAL::CONTEXT *aCtx) override
void ReportError(const wxString &aErrorMsg)
Handle the data for a net.
Definition: netinfo.h:64
bool IsOfType(TYPE_ID aDerived, TYPE_ID aBase) const
Return true if aDerived is inherited from aBase.
virtual double AsDouble() const
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
static void memberOf(LIBEVAL::CONTEXT *aCtx, void *self)
#define _(s)
Definition: 3d_actions.cpp:33
virtual const wxString & AsString() const
class ZONE, managed by a footprint
Definition: typeinfo.h:94
static void insideCourtyard(LIBEVAL::CONTEXT *aCtx, void *self)
Plated through hole pad.
Definition: pad_shapes.h:80
Handle the component boundary box.
Definition: eda_rect.h:42
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:68
bool Evaluate(const wxString &aExpr)
static void isMicroVia(LIBEVAL::CONTEXT *aCtx, void *self)
virtual double Convert(const wxString &aString, int unitId) const override
static LSET BackMask()
Return a mask holding all technical layers and the external CU layer on back side.
Definition: lset.cpp:881
std::map< ZONE *, std::unique_ptr< DRC_RTREE > > m_CopperZoneRTrees
Definition: board.h:1154
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER) const
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Definition: board_item.cpp:153
virtual const std::vector< wxString > & GetSupportedUnits() const override
CLASSES_INFO GetAllClasses()
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition: board_item.h:136
Provide class metadata.Helper macro to map type hashes to names.
Definition: property_mgr.h:63
virtual bool EqualTo(const VALUE *b) const override
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
VALUE * Run(CONTEXT *ctx)
virtual const EDA_RECT GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition: eda_item.cpp:89
Definition: pad.h:60
double DoubleValueFromString(EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType)
Function DoubleValueFromString converts aTextValue to a double.
Definition: base_units.cpp:338
DRC_RTREE - Implements an R-tree for fast spatial and layer indexing of connectable items.
Definition: drc_rtree.h:43
wxPoint GetPosition() const override
Definition: track.h:411
STATUS_FLAGS GetFlags() const
Definition: eda_item.h:204
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:168
LIBEVAL::VALUE GetValue(LIBEVAL::CONTEXT *aCtx) override
static constexpr int Millimeter2iu(double mm)
std::map< std::pair< BOARD_ITEM *, BOARD_ITEM * >, bool > m_InsideAreaCache
Definition: board.h:1152
void Push(VALUE *v)
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:905
void Set(double aValue)
BOARD_ITEM * GetObject(const LIBEVAL::CONTEXT *aCtx) const
PCB_LAYER_ID GetLayer() const
std::unordered_map< TYPE_ID, PROPERTY_BASE * > m_matchingTypes
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:162
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:190
static PCB_EXPR_BUILTIN_FUNCTIONS & Instance()