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