You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1073 lines
32 KiB

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