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 bool insideFootprintCourtyard( BOARD_ITEM* aItem, const EDA_RECT& aItemBBox,
139  std::shared_ptr<SHAPE> aItemShape, PCB_EXPR_CONTEXT* aCtx,
140  FOOTPRINT* aFootprint, PCB_LAYER_ID aSide = UNDEFINED_LAYER )
141 {
142  SHAPE_POLY_SET footprintCourtyard;
143 
144  if( aSide == F_Cu )
145  footprintCourtyard = aFootprint->GetPolyCourtyardFront();
146  else if( aSide == B_Cu )
147  footprintCourtyard = aFootprint->GetPolyCourtyardBack();
148  else if( aFootprint->IsFlipped() )
149  footprintCourtyard = aFootprint->GetPolyCourtyardBack();
150  else
151  footprintCourtyard = aFootprint->GetPolyCourtyardFront();
152 
153  if( aItem->Type() == PCB_ZONE_T || aItem->Type() == PCB_FP_ZONE_T )
154  {
155  // A zone must be entirely inside the courtyard to be considered
156  if( !aFootprint->GetBoundingBox().Contains( aItemBBox ) )
157  return false;
158  }
159  else
160  {
161  if( !aFootprint->GetBoundingBox().Intersects( aItemBBox ) )
162  return false;
163  }
164 
165  if( !aItemShape )
166  aItemShape = aItem->GetEffectiveShape( aCtx->GetLayer() );
167 
168  return footprintCourtyard.Collide( aItemShape.get() );
169 };
170 
171 
172 static void insideCourtyard( LIBEVAL::CONTEXT* aCtx, void* self )
173 {
174  PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
175  LIBEVAL::VALUE* arg = aCtx->Pop();
176  LIBEVAL::VALUE* result = aCtx->AllocValue();
177 
178  result->Set( 0.0 );
179  aCtx->Push( result );
180 
181  if( !arg )
182  {
183  aCtx->ReportError( wxString::Format( _( "Missing argument to '%s'" ),
184  wxT( "insideCourtyard()" ) ) );
185  return;
186  }
187 
188  PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
189  BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
190 
191  if( !item )
192  return;
193 
194  BOARD* board = item->GetBoard();
195  EDA_RECT itemBBox;
196  std::shared_ptr<SHAPE> shape;
197 
198  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
199  itemBBox = static_cast<ZONE*>( item )->GetCachedBoundingBox();
200  else
201  itemBBox = item->GetBoundingBox();
202 
203  auto insideFootprint =
204  [&]( FOOTPRINT* footprint ) -> bool
205  {
206  if( !footprint )
207  return false;
208 
209  std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );
210  std::pair<BOARD_ITEM*, BOARD_ITEM*> key( footprint, item );
211  auto i = board->m_InsideCourtyardCache.find( key );
212 
213  if( i != board->m_InsideCourtyardCache.end() )
214  return i->second;
215 
216  bool res = insideFootprintCourtyard( item, itemBBox, shape, context, footprint );
217 
218  board->m_InsideCourtyardCache[ key ] = res;
219  return res;
220  };
221 
222  if( arg->AsString() == "A" )
223  {
224  if( insideFootprint( dynamic_cast<FOOTPRINT*>( context->GetItem( 0 ) ) ) )
225  result->Set( 1.0 );
226  }
227  else if( arg->AsString() == "B" )
228  {
229  if( insideFootprint( dynamic_cast<FOOTPRINT*>( context->GetItem( 1 ) ) ) )
230  result->Set( 1.0 );
231  }
232  else
233  {
234  for( FOOTPRINT* candidate : board->Footprints() )
235  {
236  if( candidate->GetReference().Matches( arg->AsString() ) )
237  {
238  if( insideFootprint( candidate ) )
239  {
240  result->Set( 1.0 );
241  return;
242  }
243  }
244  }
245  }
246 }
247 
248 
249 static void insideFrontCourtyard( LIBEVAL::CONTEXT* aCtx, void* self )
250 {
251  PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
252  LIBEVAL::VALUE* arg = aCtx->Pop();
253  LIBEVAL::VALUE* result = aCtx->AllocValue();
254 
255  result->Set( 0.0 );
256  aCtx->Push( result );
257 
258  if( !arg )
259  {
260  aCtx->ReportError( wxString::Format( _( "Missing argument to '%s'" ),
261  wxT( "insideFrontCourtyard()" ) ) );
262  return;
263  }
264 
265  PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
266  BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
267 
268  if( !item )
269  return;
270 
271  BOARD* board = item->GetBoard();
272  EDA_RECT itemBBox;
273  std::shared_ptr<SHAPE> shape;
274 
275  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
276  itemBBox = static_cast<ZONE*>( item )->GetCachedBoundingBox();
277  else
278  itemBBox = item->GetBoundingBox();
279 
280  auto insideFootprint =
281  [&]( FOOTPRINT* footprint ) -> bool
282  {
283  if( !footprint )
284  return false;
285 
286  std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );
287  std::pair<BOARD_ITEM*, BOARD_ITEM*> key( footprint, item );
288  auto i = board->m_InsideFCourtyardCache.find( key );
289 
290  if( i != board->m_InsideFCourtyardCache.end() )
291  return i->second;
292 
293  bool res = insideFootprintCourtyard( item, itemBBox, shape, context, footprint,
294  F_Cu );
295 
296  board->m_InsideFCourtyardCache[ key ] = res;
297  return res;
298  };
299 
300  if( arg->AsString() == "A" )
301  {
302  if( insideFootprint( dynamic_cast<FOOTPRINT*>( context->GetItem( 0 ) ) ) )
303  result->Set( 1.0 );
304  }
305  else if( arg->AsString() == "B" )
306  {
307  if( insideFootprint( dynamic_cast<FOOTPRINT*>( context->GetItem( 1 ) ) ) )
308  result->Set( 1.0 );
309  }
310  else
311  {
312  for( FOOTPRINT* candidate : board->Footprints() )
313  {
314  if( candidate->GetReference().Matches( arg->AsString() ) )
315  {
316  if( insideFootprint( candidate ) )
317  {
318  result->Set( 1.0 );
319  return;
320  }
321  }
322  }
323  }
324 }
325 
326 
327 static void insideBackCourtyard( LIBEVAL::CONTEXT* aCtx, void* self )
328 {
329  PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
330  LIBEVAL::VALUE* arg = aCtx->Pop();
331  LIBEVAL::VALUE* result = aCtx->AllocValue();
332 
333  result->Set( 0.0 );
334  aCtx->Push( result );
335 
336  if( !arg )
337  {
338  aCtx->ReportError( wxString::Format( _( "Missing argument to '%s'" ),
339  wxT( "insideBackCourtyard()" ) ) );
340  return;
341  }
342 
343  PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
344  BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
345 
346  if( !item )
347  return;
348 
349  BOARD* board = item->GetBoard();
350  EDA_RECT itemBBox;
351  std::shared_ptr<SHAPE> shape;
352 
353  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
354  itemBBox = static_cast<ZONE*>( item )->GetCachedBoundingBox();
355  else
356  itemBBox = item->GetBoundingBox();
357 
358  auto insideFootprint =
359  [&]( FOOTPRINT* footprint ) -> bool
360  {
361  if( !footprint )
362  return false;
363 
364  std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );
365  std::pair<BOARD_ITEM*, BOARD_ITEM*> key( footprint, item );
366  auto i = board->m_InsideBCourtyardCache.find( key );
367 
368  if( i != board->m_InsideBCourtyardCache.end() )
369  return i->second;
370 
371  bool res = insideFootprintCourtyard( item, itemBBox, shape, context, footprint,
372  B_Cu );
373 
374  board->m_InsideBCourtyardCache[ key ] = res;
375  return res;
376  };
377 
378  if( arg->AsString() == "A" )
379  {
380  if( insideFootprint( dynamic_cast<FOOTPRINT*>( context->GetItem( 0 ) ) ) )
381  result->Set( 1.0 );
382  }
383  else if( arg->AsString() == "B" )
384  {
385  if( insideFootprint( dynamic_cast<FOOTPRINT*>( context->GetItem( 1 ) ) ) )
386  result->Set( 1.0 );
387  }
388  else
389  {
390  for( FOOTPRINT* candidate : board->Footprints() )
391  {
392  if( candidate->GetReference().Matches( arg->AsString() ) )
393  {
394  if( insideFootprint( candidate ) )
395  {
396  result->Set( 1.0 );
397  return;
398  }
399  }
400  }
401  }
402 }
403 
404 
405 static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self )
406 {
407  PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
408  LIBEVAL::VALUE* arg = aCtx->Pop();
409  LIBEVAL::VALUE* result = aCtx->AllocValue();
410 
411  result->Set( 0.0 );
412  aCtx->Push( result );
413 
414  if( !arg )
415  {
416  aCtx->ReportError( wxString::Format( _( "Missing argument to '%s'" ),
417  wxT( "insideArea()" ) ) );
418  return;
419  }
420 
421  PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
422  BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
423 
424  if( !item )
425  return;
426 
427  BOARD* board = item->GetBoard();
428  EDA_RECT itemBBox;
429  std::shared_ptr<SHAPE> shape;
430 
431  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
432  itemBBox = static_cast<ZONE*>( item )->GetCachedBoundingBox();
433  else
434  itemBBox = item->GetBoundingBox();
435 
436  auto realInsideZone =
437  [&]( ZONE* zone ) -> bool
438  {
439  if( !zone->GetCachedBoundingBox().Intersects( itemBBox ) )
440  return false;
441 
442  // Collisions include touching, so we need to deflate outline by enough to
443  // exclude touching. This is particularly important for detecting copper fills
444  // as they will be exactly touching along the entire border.
445  SHAPE_POLY_SET zoneOutline = *zone->Outline();
446  zoneOutline.Deflate( Millimeter2iu( 0.001 ), 4 );
447 
448  if( item->GetFlags() & HOLE_PROXY )
449  {
450  if( item->Type() == PCB_PAD_T )
451  {
452  PAD* pad = static_cast<PAD*>( item );
453  const SHAPE_SEGMENT* holeShape = pad->GetEffectiveHoleShape();
454 
455  return zoneOutline.Collide( holeShape );
456  }
457  else if( item->Type() == PCB_VIA_T )
458  {
459  VIA* via = static_cast<VIA*>( item );
460  const SHAPE_CIRCLE holeShape( via->GetPosition(), via->GetDrillValue() );
461 
462  return zoneOutline.Collide( &holeShape );
463  }
464 
465  return false;
466  }
467 
468  if( item->Type() == PCB_FOOTPRINT_T )
469  {
470  FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
471 
472  if( ( footprint->GetFlags() & MALFORMED_COURTYARDS ) != 0 )
473  {
474  aCtx->ReportError( _( "Footprint's courtyard is not a single, closed shape." ) );
475  return false;
476  }
477 
478  if( ( zone->GetLayerSet() & LSET::FrontMask() ).any() )
479  {
480  SHAPE_POLY_SET courtyard = footprint->GetPolyCourtyardFront();
481 
482  if( courtyard.OutlineCount() == 0 )
483  {
484  aCtx->ReportError( _( "Footprint has no front courtyard." ) );
485  return false;
486  }
487  else
488  {
489  return zoneOutline.Collide( &courtyard.Outline( 0 ) );
490  }
491  }
492 
493  if( ( zone->GetLayerSet() & LSET::BackMask() ).any() )
494  {
495  SHAPE_POLY_SET courtyard = footprint->GetPolyCourtyardBack();
496 
497  if( courtyard.OutlineCount() == 0 )
498  {
499  aCtx->ReportError( _( "Footprint has no back courtyard." ) );
500  return false;
501  }
502  else
503  {
504  return zoneOutline.Collide( &courtyard.Outline( 0 ) );
505  }
506  }
507 
508  return false;
509  }
510 
511  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
512  {
513  ZONE* itemZone = static_cast<ZONE*>( item );
514 
515  if( !itemZone->IsFilled() )
516  return false;
517 
518  DRC_RTREE* itemRTree = board->m_CopperZoneRTrees[ itemZone ].get();
519 
520  if( itemRTree )
521  {
522  for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
523  {
524  if( itemRTree->QueryColliding( itemBBox, &zoneOutline, layer, 0 ) )
525  return true;
526  }
527  }
528 
529  return false;
530  }
531  else
532  {
533  if( !shape )
534  shape = item->GetEffectiveShape( context->GetLayer() );
535 
536  return zoneOutline.Collide( shape.get() );
537  }
538  };
539 
540  auto insideZone =
541  [&]( ZONE* zone ) -> bool
542  {
543  if( !zone || zone == item || zone->GetParent() == item )
544  return false;
545 
546  std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );
547  std::pair<BOARD_ITEM*, BOARD_ITEM*> key( zone, item );
548  auto i = board->m_InsideAreaCache.find( key );
549 
550  if( i != board->m_InsideAreaCache.end() )
551  return i->second;
552 
553  bool isInside = realInsideZone( zone );
554 
555  board->m_InsideAreaCache[ key ] = isInside;
556  return isInside;
557  };
558 
559  if( arg->AsString() == "A" )
560  {
561  if( insideZone( dynamic_cast<ZONE*>( context->GetItem( 0 ) ) ) )
562  result->Set( 1.0 );
563  }
564  else if( arg->AsString() == "B" )
565  {
566  if( insideZone( dynamic_cast<ZONE*>( context->GetItem( 1 ) ) ) )
567  result->Set( 1.0 );
568  }
569  else if( KIID::SniffTest( arg->AsString() ) )
570  {
571  KIID target( arg->AsString() );
572 
573  for( ZONE* candidate : board->Zones() )
574  {
575  // Only a single zone can match the UUID; exit once we find a match whether
576  // "inside" or not
577  if( candidate->m_Uuid == target )
578  {
579  if( insideZone( candidate ) )
580  result->Set( 1.0 );
581 
582  return;
583  }
584  }
585 
586  for( FOOTPRINT* footprint : board->Footprints() )
587  {
588  for( ZONE* candidate : footprint->Zones() )
589  {
590  // Only a single zone can match the UUID; exit once we find a match whether
591  // "inside" or not
592  if( candidate->m_Uuid == target )
593  {
594  if( insideZone( candidate ) )
595  result->Set( 1.0 );
596 
597  return;
598  }
599  }
600  }
601  }
602  else // Match on zone name
603  {
604  for( ZONE* candidate : board->Zones() )
605  {
606  if( candidate->GetZoneName().Matches( arg->AsString() ) )
607  {
608  // Many zones can match the name; exit only when we find an "inside"
609  if( insideZone( candidate ) )
610  {
611  result->Set( 1.0 );
612  return;
613  }
614  }
615  }
616 
617  for( FOOTPRINT* footprint : board->Footprints() )
618  {
619  for( ZONE* candidate : footprint->Zones() )
620  {
621  // Many zones can match the name; exit only when we find an "inside"
622  if( candidate->GetZoneName().Matches( arg->AsString() ) )
623  {
624  if( insideZone( candidate ) )
625  {
626  result->Set( 1.0 );
627  return;
628  }
629  }
630  }
631  }
632  }
633 }
634 
635 
636 static void memberOf( LIBEVAL::CONTEXT* aCtx, void* self )
637 {
638  LIBEVAL::VALUE* arg = aCtx->Pop();
639  LIBEVAL::VALUE* result = aCtx->AllocValue();
640 
641  result->Set( 0.0 );
642  aCtx->Push( result );
643 
644  if( !arg )
645  {
646  aCtx->ReportError( wxString::Format( _( "Missing argument to '%s'" ),
647  wxT( "memberOf()" ) ) );
648  return;
649  }
650 
651  PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
652  BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
653 
654  if( !item )
655  return;
656 
657  PCB_GROUP* group = item->GetParentGroup();
658 
659  if( !group && item->GetParent() && item->GetParent()->Type() == PCB_FOOTPRINT_T )
660  group = item->GetParent()->GetParentGroup();
661 
662  while( group )
663  {
664  if( group->GetName().Matches( arg->AsString() ) )
665  {
666  result->Set( 1.0 );
667  return;
668  }
669 
670  group = group->GetParentGroup();
671  }
672 }
673 
674 
675 static void isMicroVia( LIBEVAL::CONTEXT* aCtx, void* self )
676 {
677  PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
678  BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
679  LIBEVAL::VALUE* result = aCtx->AllocValue();
680 
681  result->Set( 0.0 );
682  aCtx->Push( result );
683 
684  auto via = dyn_cast<VIA*>( item );
685 
686  if( via && via->GetViaType() == VIATYPE::MICROVIA )
687  {
688  result->Set ( 1.0 );
689  }
690 }
691 
692 
693 static void isBlindBuriedVia( LIBEVAL::CONTEXT* aCtx, void* self )
694 {
695  PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
696  BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
697  LIBEVAL::VALUE* result = aCtx->AllocValue();
698 
699  result->Set( 0.0 );
700  aCtx->Push( result );
701 
702  auto via = dyn_cast<VIA*>( item );
703 
704  if( via && via->GetViaType() == VIATYPE::BLIND_BURIED )
705  {
706  result->Set ( 1.0 );
707  }
708 }
709 
710 
711 static void isCoupledDiffPair( LIBEVAL::CONTEXT* aCtx, void* self )
712 {
713  PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
714  BOARD_CONNECTED_ITEM* a = dynamic_cast<BOARD_CONNECTED_ITEM*>( context->GetItem( 0 ) );
715  BOARD_CONNECTED_ITEM* b = dynamic_cast<BOARD_CONNECTED_ITEM*>( context->GetItem( 1 ) );
716  LIBEVAL::VALUE* result = aCtx->AllocValue();
717 
718  result->Set( 0.0 );
719  aCtx->Push( result );
720 
721  if( a && b )
722  {
723  NETINFO_ITEM* netinfo = a->GetNet();
724  wxString coupledNet, dummy;
725 
726  if( netinfo && DRC_ENGINE::MatchDpSuffix( netinfo->GetNetname(), coupledNet, dummy ) != 0 )
727  {
728  if( b->GetNetname() == coupledNet )
729  result->Set( 1.0 );
730  }
731  }
732 }
733 
734 
735 static void inDiffPair( LIBEVAL::CONTEXT* aCtx, void* self )
736 {
737  LIBEVAL::VALUE* arg = aCtx->Pop();
738  PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
739  BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
740  LIBEVAL::VALUE* result = aCtx->AllocValue();
741 
742  result->Set( 0.0 );
743  aCtx->Push( result );
744 
745  if( !arg )
746  {
747  aCtx->ReportError( wxString::Format( _( "Missing argument to '%s'" ),
748  wxT( "inDiffPair()" ) ) );
749  return;
750  }
751 
752  if( item && item->IsConnected() )
753  {
754  NETINFO_ITEM* netinfo = static_cast<BOARD_CONNECTED_ITEM*>( item )->GetNet();
755 
756  wxString refName = netinfo->GetNetname();
757  wxString baseName, coupledNet;
758 
759  int polarity = DRC_ENGINE::MatchDpSuffix( refName, coupledNet, baseName );
760 
761  if( polarity == 0 )
762  return;
763 
764  if( BOARD* board = item->GetBoard() )
765  {
766  if( !board->FindNet( coupledNet ) )
767  return;
768  }
769 
770  if( baseName.Matches( arg->AsString() ) )
771  result->Set( 1.0 );
772  }
773 }
774 
775 
777 {
779 }
780 
781 
783 {
784  m_funcs.clear();
785  RegisterFunc( "existsOnLayer('x')", existsOnLayer );
786  RegisterFunc( "isPlated()", isPlated );
787  RegisterFunc( "insideCourtyard('x')", insideCourtyard );
788  RegisterFunc( "insideFrontCourtyard('x')", insideFrontCourtyard );
789  RegisterFunc( "insideBackCourtyard('x')", insideBackCourtyard );
790  RegisterFunc( "insideArea('x')", insideArea );
791  RegisterFunc( "isMicroVia()", isMicroVia );
792  RegisterFunc( "isBlindBuriedVia()", isBlindBuriedVia );
793  RegisterFunc( "memberOf('x')", memberOf );
794  RegisterFunc( "fromTo('x','y')", exprFromTo );
795  RegisterFunc( "isCoupledDiffPair()", isCoupledDiffPair );
796  RegisterFunc( "inDiffPair('x')", inDiffPair );
797 }
798 
799 
801 {
802  wxASSERT( dynamic_cast<const PCB_EXPR_CONTEXT*>( aCtx ) );
803 
804  const PCB_EXPR_CONTEXT* ctx = static_cast<const PCB_EXPR_CONTEXT*>( aCtx );
805  BOARD_ITEM* item = ctx->GetItem( m_itemIndex );
806  return item;
807 }
808 
809 
811 {
812 public:
814  LIBEVAL::VALUE( double( aLayer ) )
815  {};
816 
817  virtual bool EqualTo( const VALUE* b ) const override
818  {
819  // For boards with user-defined layer names there will be 2 entries for each layer
820  // in the ENUM_MAP: one for the canonical layer name and one for the user layer name.
821  // We need to check against both.
822 
823  wxPGChoices& layerMap = ENUM_MAP<PCB_LAYER_ID>::Instance().Choices();
824  PCB_LAYER_ID layerId = ToLAYER_ID( (int) AsDouble() );
825 
826  for( unsigned ii = 0; ii < layerMap.GetCount(); ++ii )
827  {
828  wxPGChoiceEntry& entry = layerMap[ii];
829 
830  if( entry.GetValue() == layerId && entry.GetText().Matches( b->AsString() ) )
831  return true;
832  }
833 
834  return false;
835  }
836 };
837 
838 
840 {
841  if( m_itemIndex == 2 )
842  {
843  PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
844  return PCB_LAYER_VALUE( context->GetLayer() );
845  }
846 
847  BOARD_ITEM* item = GetObject( aCtx );
848 
849  if( !item )
850  return LIBEVAL::VALUE();
851 
852  auto it = m_matchingTypes.find( TYPE_HASH( *item ) );
853 
854  if( it == m_matchingTypes.end() )
855  {
856  // Don't force user to type "A.Type == 'via' && A.Via_Type == 'buried'" when the
857  // simplier "A.Via_Type == 'buried'" is perfectly clear. Instead, return an undefined
858  // value when the property doesn't appear on a particular object.
859 
860  return LIBEVAL::VALUE();
861  }
862  else
863  {
864  if( m_type == LIBEVAL::VT_NUMERIC )
865  return LIBEVAL::VALUE( (double) item->Get<int>( it->second ) );
866  else
867  {
868  wxString str;
869 
870  if( !m_isEnum )
871  {
872  str = item->Get<wxString>( it->second );
873  return LIBEVAL::VALUE( str );
874  }
875  else
876  {
877  const wxAny& any = item->Get( it->second );
878  bool valid = any.GetAs<wxString>( &str );
879 
880  if( valid )
881  return LIBEVAL::VALUE( str );
882  }
883 
884  return LIBEVAL::VALUE();
885  }
886  }
887 }
888 
889 
891 {
892  BOARD_ITEM* item = GetObject( aCtx );
893 
894  if( !item )
895  return LIBEVAL::VALUE();
896 
897  if( item->IsConnected() )
898  return LIBEVAL::VALUE( static_cast<BOARD_CONNECTED_ITEM*>( item )->GetNetClassName() );
899  else
900  return LIBEVAL::VALUE();
901 }
902 
903 
905 {
906  BOARD_ITEM* item = GetObject( aCtx );
907 
908  if( !item )
909  return LIBEVAL::VALUE();
910 
911  if( item->IsConnected() )
912  return LIBEVAL::VALUE( static_cast<BOARD_CONNECTED_ITEM*>( item )->GetNetname() );
913  else
914  return LIBEVAL::VALUE();
915 }
916 
917 
919 {
921 
922  return registry.Get( aName.Lower() );
923 }
924 
925 
926 std::unique_ptr<LIBEVAL::VAR_REF> PCB_EXPR_UCODE::CreateVarRef( const wxString& aVar,
927  const wxString& aField )
928 {
930  std::unique_ptr<PCB_EXPR_VAR_REF> vref;
931 
932  // Check for a couple of very common cases and compile them straight to "object code".
933 
934  if( aField.CmpNoCase( "NetClass" ) == 0 )
935  {
936  if( aVar == "A" )
937  return std::make_unique<PCB_EXPR_NETCLASS_REF>( 0 );
938  else if( aVar == "B" )
939  return std::make_unique<PCB_EXPR_NETCLASS_REF>( 1 );
940  else
941  return nullptr;
942  }
943  else if( aField.CmpNoCase( "NetName" ) == 0 )
944  {
945  if( aVar == "A" )
946  return std::make_unique<PCB_EXPR_NETNAME_REF>( 0 );
947  else if( aVar == "B" )
948  return std::make_unique<PCB_EXPR_NETNAME_REF>( 1 );
949  else
950  return nullptr;
951  }
952 
953  if( aVar == "A" || aVar == "AB" )
954  vref = std::make_unique<PCB_EXPR_VAR_REF>( 0 );
955  else if( aVar == "B" )
956  vref = std::make_unique<PCB_EXPR_VAR_REF>( 1 );
957  else if( aVar == "L" )
958  vref = std::make_unique<PCB_EXPR_VAR_REF>( 2 );
959  else
960  return nullptr;
961 
962  if( aField.length() == 0 ) // return reference to base object
963  {
964  return std::move( vref );
965  }
966 
967  wxString field( aField );
968  field.Replace( "_", " " );
969 
970  for( const PROPERTY_MANAGER::CLASS_INFO& cls : propMgr.GetAllClasses() )
971  {
972  if( propMgr.IsOfType( cls.type, TYPE_HASH( BOARD_ITEM ) ) )
973  {
974  PROPERTY_BASE* prop = propMgr.GetProperty( cls.type, field );
975 
976  if( prop )
977  {
978  vref->AddAllowedClass( cls.type, prop );
979 
980  if( prop->TypeHash() == TYPE_HASH( int ) )
981  {
982  vref->SetType( LIBEVAL::VT_NUMERIC );
983  }
984  else if( prop->TypeHash() == TYPE_HASH( wxString ) )
985  {
986  vref->SetType( LIBEVAL::VT_STRING );
987  }
988  else if ( prop->HasChoices() )
989  { // it's an enum, we treat it as string
990  vref->SetType( LIBEVAL::VT_STRING );
991  vref->SetIsEnum ( true );
992  }
993  else
994  {
995  wxFAIL_MSG( "PCB_EXPR_UCODE::createVarRef: Unknown property type." );
996  }
997  }
998  }
999  }
1000 
1001  if( vref->GetType() == LIBEVAL::VT_UNDEFINED )
1002  vref->SetType( LIBEVAL::VT_PARSE_ERROR );
1003 
1004  return std::move( vref );
1005 }
1006 
1007 
1009 {
1010 public:
1012  {
1013  }
1014 
1015  virtual const std::vector<wxString>& GetSupportedUnits() const override
1016  {
1017  static const std::vector<wxString> pcbUnits = { "mil", "mm", "in" };
1018 
1019  return pcbUnits;
1020  }
1021 
1022  virtual wxString GetSupportedUnitsMessage() const override
1023  {
1024  return _( "must be mm, in, or mil" );
1025  }
1026 
1027  virtual double Convert( const wxString& aString, int unitId ) const override
1028  {
1029  double v = wxAtof( aString );
1030 
1031  switch( unitId )
1032  {
1033  case 0: return DoubleValueFromString( EDA_UNITS::MILS, aString );
1034  case 1: return DoubleValueFromString( EDA_UNITS::MILLIMETRES, aString );
1035  case 2: return DoubleValueFromString( EDA_UNITS::INCHES, aString );
1036  default: return v;
1037  }
1038  };
1039 };
1040 
1041 
1043 {
1044  m_unitResolver = std::make_unique<PCB_UNIT_RESOLVER>();
1045 }
1046 
1047 
1049 {
1050  m_result = 0;
1051 }
1052 
1054 {
1055 }
1056 
1057 
1058 bool PCB_EXPR_EVALUATOR::Evaluate( const wxString& aExpr )
1059 {
1060  PCB_EXPR_UCODE ucode;
1061  PCB_EXPR_CONTEXT preflightContext( F_Cu );
1062 
1063  if( !m_compiler.Compile( aExpr.ToUTF8().data(), &ucode, &preflightContext ) )
1064  return false;
1065 
1066  PCB_EXPR_CONTEXT evaluationContext( F_Cu );
1067  LIBEVAL::VALUE* result = ucode.Run( &evaluationContext );
1068 
1069  if( result->GetType() == LIBEVAL::VT_NUMERIC )
1070  m_result = KiROUND( result->AsDouble() );
1071 
1072  return true;
1073 }
1074 
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:216
static PROPERTY_MANAGER & Instance()
Definition: property_mgr.h:65
const SHAPE_POLY_SET & GetPolyCourtyardFront() const
Used in DRC to test the courtyard area (a complex polygon).
Definition: footprint.h:651
#define TYPE_HASH(x)
Definition: property.h:59
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:1153
ZONES & Zones()
Definition: board.h:311
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:82
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
virtual LIBEVAL::FUNC_CALL_REF CreateFuncCall(const wxString &aName) override
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:50
void RegisterFunc(const wxString &funcSignature, LIBEVAL::FUNC_CALL_REF funcPtr)
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:510
static void isPlated(LIBEVAL::CONTEXT *aCtx, void *self)
bool exprFromTo(LIBEVAL::CONTEXT *aCtx, void *self)
static LIB_PART * dummy()
Used to draw a dummy shape when a LIB_PART is not found in library.
Definition: sch_symbol.cpp:69
class PAD, a pad in a footprint
Definition: typeinfo.h:89
bool IsFilled() const
Definition: zone.h:231
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.
static void insideFrontCourtyard(LIBEVAL::CONTEXT *aCtx, void *self)
virtual wxString GetSupportedUnitsMessage() const override
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
static LSET FrontMask()
Return a mask holding all technical layers and the external CU layer on front side.
Definition: lset.cpp:874
bool Contains(const wxPoint &aPoint) const
Definition: eda_rect.cpp:57
std::mutex m_CachesMutex
Definition: board.h:1152
bool Compile(const wxString &aString, UCODE *aCode, CONTEXT *aPreflightContext)
std::map< wxString, LIBEVAL::FUNC_CALL_REF > m_funcs
Plated through hole pad.
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)
static bool insideFootprintCourtyard(BOARD_ITEM *aItem, const EDA_RECT &aItemBBox, std::shared_ptr< SHAPE > aItemShape, PCB_EXPR_CONTEXT *aCtx, FOOTPRINT *aFootprint, PCB_LAYER_ID aSide=UNDEFINED_LAYER)
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:140
static void existsOnLayer(LIBEVAL::CONTEXT *aCtx, void *self)
FOOTPRINTS & Footprints()
Definition: board.h:305
wxAny Get(PROPERTY_BASE *aProperty)
Definition: inspectable.h:86
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
bool Set(PROPERTY_BASE *aProperty, wxAny &aValue)
Definition: inspectable.h:42
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:416
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:652
#define MALFORMED_COURTYARDS
Definition: eda_item.h:126
Handle 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)
static void insideBackCourtyard(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:129
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,...
bool IsFlipped() const
Definition: footprint.h:266
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
const EDA_RECT GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: footprint.cpp:631
virtual const wxString & AsString() const
class ZONE, managed by a footprint
Definition: typeinfo.h:94
static void insideCourtyard(LIBEVAL::CONTEXT *aCtx, void *self)
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
bool Intersects(const EDA_RECT &aRect) const
Test for a common area between rectangles.
Definition: eda_rect.cpp:150
std::map< ZONE *, std::unique_ptr< DRC_RTREE > > m_CopperZoneRTrees
Definition: board.h:1158
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:62
virtual bool EqualTo(const VALUE *b) const override
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
static void isCoupledDiffPair(LIBEVAL::CONTEXT *aCtx, void *self)
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:73
std::map< std::pair< BOARD_ITEM *, BOARD_ITEM * >, bool > m_InsideBCourtyardCache
Definition: board.h:1155
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:293
DRC_RTREE - Implements an R-tree for fast spatial and layer indexing of connectable items.
Definition: drc_rtree.h:43
STATUS_FLAGS GetFlags() const
Definition: eda_item.h:205
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:1156
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:163
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()
std::map< std::pair< BOARD_ITEM *, BOARD_ITEM * >, bool > m_InsideFCourtyardCache
Definition: board.h:1154