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.

2377 lines
73 KiB

8 years ago
9 years ago
9 years ago
5 years ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
9 years ago
9 years ago
5 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
5 years ago
9 years ago
9 years ago
9 years ago
4 years ago
4 years ago
4 years ago
4 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
5 years ago
9 years ago
2 years ago
9 years ago
3 years ago
3 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. /*
  2. * KiRouter - a push-and-(sometimes-)shove PCB router
  3. *
  4. * Copyright (C) 2013-2016 CERN
  5. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  6. * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  7. *
  8. * This program is free software: you can redistribute it and/or modify it
  9. * under the terms of the GNU General Public License as published by the
  10. * Free Software Foundation, either version 3 of the License, or (at your
  11. * option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License along
  19. * with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #include <board.h>
  22. #include <board_connected_item.h>
  23. #include <board_design_settings.h>
  24. #include <netinfo.h>
  25. #include <footprint.h>
  26. #include <layer_range.h>
  27. #include <pad.h>
  28. #include <pcb_track.h>
  29. #include <zone.h>
  30. #include <pcb_shape.h>
  31. #include <pcb_generator.h>
  32. #include <pcb_text.h>
  33. #include <pcb_table.h>
  34. #include <pcb_tablecell.h>
  35. #include <board_commit.h>
  36. #include <layer_ids.h>
  37. #include <kidialog.h>
  38. #include <tools/pcb_tool_base.h>
  39. #include <tool/tool_manager.h>
  40. #include <settings/app_settings.h>
  41. #include <gal/graphics_abstraction_layer.h>
  42. #include <pcb_painter.h>
  43. #include <geometry/shape.h>
  44. #include <geometry/shape_line_chain.h>
  45. #include <geometry/shape_arc.h>
  46. #include <geometry/shape_simple.h>
  47. #include <drc/drc_rule.h>
  48. #include <drc/drc_engine.h>
  49. #include <connectivity/connectivity_data.h>
  50. #include <wx/log.h>
  51. #include <memory>
  52. #include <advanced_config.h>
  53. #include <pcbnew_settings.h>
  54. #include <macros.h>
  55. #include "pns_kicad_iface.h"
  56. #include "pns_arc.h"
  57. #include "pns_sizes_settings.h"
  58. #include "pns_item.h"
  59. #include "pns_layerset.h"
  60. #include "pns_line.h"
  61. #include "pns_solid.h"
  62. #include "pns_segment.h"
  63. #include "pns_node.h"
  64. #include "pns_router.h"
  65. #include "pns_debug_decorator.h"
  66. #include "router_preview_item.h"
  67. typedef VECTOR2I::extended_type ecoord;
  68. struct CLEARANCE_CACHE_KEY
  69. {
  70. const PNS::ITEM* A;
  71. const PNS::ITEM* B;
  72. bool Flag;
  73. bool operator==(const CLEARANCE_CACHE_KEY& other) const
  74. {
  75. return A == other.A && B == other.B && Flag == other.Flag;
  76. }
  77. };
  78. namespace std
  79. {
  80. template <>
  81. struct hash<CLEARANCE_CACHE_KEY>
  82. {
  83. std::size_t operator()( const CLEARANCE_CACHE_KEY& k ) const
  84. {
  85. size_t retval = 0xBADC0FFEE0DDF00D;
  86. hash_combine( retval, hash<const void*>()( k.A ), hash<const void*>()( k.B ), hash<int>()( k.Flag ) );
  87. return retval;
  88. }
  89. };
  90. }
  91. class PNS_PCBNEW_RULE_RESOLVER : public PNS::RULE_RESOLVER
  92. {
  93. public:
  94. PNS_PCBNEW_RULE_RESOLVER( BOARD* aBoard, PNS::ROUTER_IFACE* aRouterIface );
  95. virtual ~PNS_PCBNEW_RULE_RESOLVER();
  96. int Clearance( const PNS::ITEM* aA, const PNS::ITEM* aB,
  97. bool aUseClearanceEpsilon = true ) override;
  98. PNS::NET_HANDLE DpCoupledNet( PNS::NET_HANDLE aNet ) override;
  99. int DpNetPolarity( PNS::NET_HANDLE aNet ) override;
  100. bool DpNetPair( const PNS::ITEM* aItem, PNS::NET_HANDLE& aNetP,
  101. PNS::NET_HANDLE& aNetN ) override;
  102. int NetCode( PNS::NET_HANDLE aNet ) override;
  103. wxString NetName( PNS::NET_HANDLE aNet ) override;
  104. bool IsInNetTie( const PNS::ITEM* aA ) override;
  105. bool IsNetTieExclusion( const PNS::ITEM* aItem, const VECTOR2I& aCollisionPos,
  106. const PNS::ITEM* aCollidingItem ) override;
  107. bool IsDrilledHole( const PNS::ITEM* aItem ) override;
  108. bool IsNonPlatedSlot( const PNS::ITEM* aItem ) override;
  109. /**
  110. * @return true if \a aObstacle is a keepout. Set \a aEnforce if said keepout's rules
  111. * exclude \a aItem.
  112. */
  113. bool IsKeepout( const PNS::ITEM* aObstacle, const PNS::ITEM* aItem, bool* aEnforce ) override;
  114. bool QueryConstraint( PNS::CONSTRAINT_TYPE aType, const PNS::ITEM* aItemA,
  115. const PNS::ITEM* aItemB, int aLayer,
  116. PNS::CONSTRAINT* aConstraint ) override;
  117. int ClearanceEpsilon() const override { return m_clearanceEpsilon; }
  118. void ClearCacheForItems( std::vector<const PNS::ITEM*>& aItems ) override;
  119. void ClearCaches() override;
  120. void ClearTemporaryCaches() override;
  121. private:
  122. BOARD_ITEM* getBoardItem( const PNS::ITEM* aItem, PCB_LAYER_ID aBoardLayer, int aIdx = 0 );
  123. private:
  124. PNS::ROUTER_IFACE* m_routerIface;
  125. BOARD* m_board;
  126. PCB_TRACK m_dummyTracks[2];
  127. PCB_ARC m_dummyArcs[2];
  128. PCB_VIA m_dummyVias[2];
  129. int m_clearanceEpsilon;
  130. std::unordered_map<CLEARANCE_CACHE_KEY, int> m_clearanceCache;
  131. std::unordered_map<CLEARANCE_CACHE_KEY, int> m_tempClearanceCache;
  132. };
  133. PNS_PCBNEW_RULE_RESOLVER::PNS_PCBNEW_RULE_RESOLVER( BOARD* aBoard,
  134. PNS::ROUTER_IFACE* aRouterIface ) :
  135. m_routerIface( aRouterIface ),
  136. m_board( aBoard ),
  137. m_dummyTracks{ { aBoard }, { aBoard } },
  138. m_dummyArcs{ { aBoard }, { aBoard } },
  139. m_dummyVias{ { aBoard }, { aBoard } }
  140. {
  141. for( PCB_TRACK& track : m_dummyTracks )
  142. track.SetFlags( ROUTER_TRANSIENT );
  143. for( PCB_ARC& arc : m_dummyArcs )
  144. arc.SetFlags( ROUTER_TRANSIENT );
  145. for ( PCB_VIA& via : m_dummyVias )
  146. via.SetFlags( ROUTER_TRANSIENT );
  147. if( aBoard )
  148. m_clearanceEpsilon = aBoard->GetDesignSettings().GetDRCEpsilon();
  149. else
  150. m_clearanceEpsilon = 0;
  151. }
  152. PNS_PCBNEW_RULE_RESOLVER::~PNS_PCBNEW_RULE_RESOLVER()
  153. {
  154. }
  155. bool PNS_PCBNEW_RULE_RESOLVER::IsInNetTie( const PNS::ITEM* aA )
  156. {
  157. BOARD_ITEM* item = aA->BoardItem();
  158. return item && item->GetParentFootprint() && item->GetParentFootprint()->IsNetTie();
  159. }
  160. bool PNS_PCBNEW_RULE_RESOLVER::IsNetTieExclusion( const PNS::ITEM* aItem,
  161. const VECTOR2I& aCollisionPos,
  162. const PNS::ITEM* aCollidingItem )
  163. {
  164. if( !aItem || !aCollidingItem )
  165. return false;
  166. std::shared_ptr<DRC_ENGINE> drcEngine = m_board->GetDesignSettings().m_DRCEngine;
  167. BOARD_ITEM* item = aItem->BoardItem();
  168. BOARD_ITEM* collidingItem = aCollidingItem->BoardItem();
  169. FOOTPRINT* collidingFp = collidingItem->GetParentFootprint();
  170. FOOTPRINT* itemFp = item ? item->GetParentFootprint() : nullptr;
  171. if( collidingFp && itemFp && ( collidingFp == itemFp ) && itemFp->IsNetTie() )
  172. {
  173. // Two items colliding from the same net tie footprint are not checked
  174. return true;
  175. }
  176. if( drcEngine )
  177. {
  178. return drcEngine->IsNetTieExclusion( NetCode( aItem->Net() ),
  179. m_routerIface->GetBoardLayerFromPNSLayer( aItem->Layer() ),
  180. aCollisionPos, collidingItem );
  181. }
  182. return false;
  183. }
  184. bool PNS_PCBNEW_RULE_RESOLVER::IsKeepout( const PNS::ITEM* aObstacle, const PNS::ITEM* aItem,
  185. bool* aEnforce )
  186. {
  187. auto checkKeepout =
  188. []( const ZONE* aKeepout, const BOARD_ITEM* aOther )
  189. {
  190. if( !aOther )
  191. return false;
  192. if( aKeepout->GetDoNotAllowTracks() && aOther->IsType( { PCB_ARC_T, PCB_TRACE_T } ) )
  193. return true;
  194. if( aKeepout->GetDoNotAllowVias() && aOther->Type() == PCB_VIA_T )
  195. return true;
  196. if( aKeepout->GetDoNotAllowPads() && aOther->Type() == PCB_PAD_T )
  197. return true;
  198. // Incomplete test, but better than nothing:
  199. if( aKeepout->GetDoNotAllowFootprints() && aOther->Type() == PCB_PAD_T )
  200. {
  201. return !aKeepout->GetParentFootprint()
  202. || aKeepout->GetParentFootprint() != aOther->GetParentFootprint();
  203. }
  204. return false;
  205. };
  206. if( aObstacle->Parent() && aObstacle->Parent()->Type() == PCB_ZONE_T )
  207. {
  208. const ZONE* zone = static_cast<ZONE*>( aObstacle->Parent() );
  209. if( zone->GetIsRuleArea() && zone->HasKeepoutParametersSet() )
  210. {
  211. *aEnforce = checkKeepout( zone,
  212. getBoardItem( aItem, m_routerIface->GetBoardLayerFromPNSLayer(
  213. aObstacle->Layer() ) ) );
  214. return true;
  215. }
  216. }
  217. return false;
  218. }
  219. static bool isCopper( const PNS::ITEM* aItem )
  220. {
  221. if ( !aItem )
  222. return false;
  223. const BOARD_ITEM *parent = aItem->Parent();
  224. return !parent || parent->IsOnCopperLayer();
  225. }
  226. static bool isHole( const PNS::ITEM* aItem )
  227. {
  228. if ( !aItem )
  229. return false;
  230. return aItem->OfKind( PNS::ITEM::HOLE_T );
  231. }
  232. static bool isEdge( const PNS::ITEM* aItem )
  233. {
  234. if ( !aItem )
  235. return false;
  236. const PCB_SHAPE *parent = dynamic_cast<PCB_SHAPE*>( aItem->BoardItem() );
  237. return parent && ( parent->IsOnLayer( Edge_Cuts ) || parent->IsOnLayer( Margin ) );
  238. }
  239. bool PNS_PCBNEW_RULE_RESOLVER::IsDrilledHole( const PNS::ITEM* aItem )
  240. {
  241. if( isHole( aItem ) && aItem->Parent() )
  242. return aItem->Parent()->HasDrilledHole();
  243. return false;
  244. }
  245. bool PNS_PCBNEW_RULE_RESOLVER::IsNonPlatedSlot( const PNS::ITEM* aItem )
  246. {
  247. if( !isHole( aItem ) )
  248. return false;
  249. BOARD_ITEM* parent = aItem->Parent();
  250. if( !parent && aItem->ParentPadVia() )
  251. parent = aItem->ParentPadVia()->Parent();
  252. if( parent )
  253. {
  254. if( parent->Type() == PCB_PAD_T )
  255. {
  256. PAD* pad = static_cast<PAD*>( parent );
  257. return pad->GetAttribute() == PAD_ATTRIB::NPTH
  258. && pad->GetDrillSizeX() != pad->GetDrillSizeY();
  259. }
  260. // Via holes are (currently) always round, and always plated
  261. }
  262. return false;
  263. }
  264. BOARD_ITEM* PNS_PCBNEW_RULE_RESOLVER::getBoardItem( const PNS::ITEM* aItem, PCB_LAYER_ID aBoardLayer, int aIdx )
  265. {
  266. switch( aItem->Kind() )
  267. {
  268. case PNS::ITEM::ARC_T:
  269. m_dummyArcs[aIdx].SetLayer( aBoardLayer );
  270. m_dummyArcs[aIdx].SetNet( static_cast<NETINFO_ITEM*>( aItem->Net() ) );
  271. m_dummyArcs[aIdx].SetStart( aItem->Anchor( 0 ) );
  272. m_dummyArcs[aIdx].SetEnd( aItem->Anchor( 1 ) );
  273. return &m_dummyArcs[aIdx];
  274. case PNS::ITEM::VIA_T:
  275. case PNS::ITEM::HOLE_T:
  276. m_dummyVias[aIdx].SetLayer( aBoardLayer );
  277. m_dummyVias[aIdx].SetNet( static_cast<NETINFO_ITEM*>( aItem->Net() ) );
  278. m_dummyVias[aIdx].SetStart( aItem->Anchor( 0 ) );
  279. return &m_dummyVias[aIdx];
  280. case PNS::ITEM::SEGMENT_T:
  281. case PNS::ITEM::LINE_T:
  282. m_dummyTracks[aIdx].SetLayer( aBoardLayer );
  283. m_dummyTracks[aIdx].SetNet( static_cast<NETINFO_ITEM*>( aItem->Net() ) );
  284. m_dummyTracks[aIdx].SetStart( aItem->Anchor( 0 ) );
  285. m_dummyTracks[aIdx].SetEnd( aItem->Anchor( 1 ) );
  286. return &m_dummyTracks[aIdx];
  287. default:
  288. return nullptr;
  289. }
  290. }
  291. bool PNS_PCBNEW_RULE_RESOLVER::QueryConstraint( PNS::CONSTRAINT_TYPE aType,
  292. const PNS::ITEM* aItemA, const PNS::ITEM* aItemB,
  293. int aPNSLayer, PNS::CONSTRAINT* aConstraint )
  294. {
  295. std::shared_ptr<DRC_ENGINE> drcEngine = m_board->GetDesignSettings().m_DRCEngine;
  296. if( !drcEngine )
  297. return false;
  298. DRC_CONSTRAINT_T hostType;
  299. switch ( aType )
  300. {
  301. case PNS::CONSTRAINT_TYPE::CT_CLEARANCE: hostType = CLEARANCE_CONSTRAINT; break;
  302. case PNS::CONSTRAINT_TYPE::CT_WIDTH: hostType = TRACK_WIDTH_CONSTRAINT; break;
  303. case PNS::CONSTRAINT_TYPE::CT_DIFF_PAIR_GAP: hostType = DIFF_PAIR_GAP_CONSTRAINT; break;
  304. case PNS::CONSTRAINT_TYPE::CT_LENGTH: hostType = LENGTH_CONSTRAINT; break;
  305. case PNS::CONSTRAINT_TYPE::CT_DIFF_PAIR_SKEW: hostType = SKEW_CONSTRAINT; break;
  306. case PNS::CONSTRAINT_TYPE::CT_MAX_UNCOUPLED: hostType = MAX_UNCOUPLED_CONSTRAINT; break;
  307. case PNS::CONSTRAINT_TYPE::CT_VIA_DIAMETER: hostType = VIA_DIAMETER_CONSTRAINT; break;
  308. case PNS::CONSTRAINT_TYPE::CT_VIA_HOLE: hostType = HOLE_SIZE_CONSTRAINT; break;
  309. case PNS::CONSTRAINT_TYPE::CT_HOLE_CLEARANCE: hostType = HOLE_CLEARANCE_CONSTRAINT; break;
  310. case PNS::CONSTRAINT_TYPE::CT_EDGE_CLEARANCE: hostType = EDGE_CLEARANCE_CONSTRAINT; break;
  311. case PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE: hostType = HOLE_TO_HOLE_CONSTRAINT; break;
  312. case PNS::CONSTRAINT_TYPE::CT_PHYSICAL_CLEARANCE: hostType = PHYSICAL_CLEARANCE_CONSTRAINT; break;
  313. default: return false; // should not happen
  314. }
  315. BOARD_ITEM* parentA = aItemA ? aItemA->BoardItem() : nullptr;
  316. BOARD_ITEM* parentB = aItemB ? aItemB->BoardItem() : nullptr;
  317. PCB_LAYER_ID board_layer = m_routerIface->GetBoardLayerFromPNSLayer( aPNSLayer );
  318. DRC_CONSTRAINT hostConstraint;
  319. // A track being routed may not have a BOARD_ITEM associated yet.
  320. if( aItemA && !parentA )
  321. parentA = getBoardItem( aItemA, board_layer, 0 );
  322. if( aItemB && !parentB )
  323. parentB = getBoardItem( aItemB, board_layer, 1 );
  324. if( parentA )
  325. hostConstraint = drcEngine->EvalRules( hostType, parentA, parentB, board_layer );
  326. if( hostConstraint.IsNull() )
  327. return false;
  328. if( hostConstraint.GetSeverity() == RPT_SEVERITY_IGNORE )
  329. {
  330. aConstraint->m_Value.SetMin( -1 );
  331. aConstraint->m_RuleName = hostConstraint.GetName();
  332. aConstraint->m_Type = aType;
  333. return true;
  334. }
  335. switch ( aType )
  336. {
  337. case PNS::CONSTRAINT_TYPE::CT_CLEARANCE:
  338. case PNS::CONSTRAINT_TYPE::CT_WIDTH:
  339. case PNS::CONSTRAINT_TYPE::CT_DIFF_PAIR_GAP:
  340. case PNS::CONSTRAINT_TYPE::CT_VIA_DIAMETER:
  341. case PNS::CONSTRAINT_TYPE::CT_VIA_HOLE:
  342. case PNS::CONSTRAINT_TYPE::CT_HOLE_CLEARANCE:
  343. case PNS::CONSTRAINT_TYPE::CT_EDGE_CLEARANCE:
  344. case PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE:
  345. case PNS::CONSTRAINT_TYPE::CT_LENGTH:
  346. case PNS::CONSTRAINT_TYPE::CT_DIFF_PAIR_SKEW:
  347. case PNS::CONSTRAINT_TYPE::CT_MAX_UNCOUPLED:
  348. case PNS::CONSTRAINT_TYPE::CT_PHYSICAL_CLEARANCE:
  349. aConstraint->m_Value = hostConstraint.GetValue();
  350. aConstraint->m_RuleName = hostConstraint.GetName();
  351. aConstraint->m_Type = aType;
  352. return true;
  353. default:
  354. return false;
  355. }
  356. }
  357. void PNS_PCBNEW_RULE_RESOLVER::ClearCacheForItems( std::vector<const PNS::ITEM*>& aItems )
  358. {
  359. int n_pruned = 0;
  360. std::set<const PNS::ITEM*> remainingItems( aItems.begin(), aItems.end() );
  361. /* We need to carefully check both A and B item pointers in the cache against dirty/invalidated
  362. items in the set, as the clearance relation is commutative ( CL[a,b] == CL[b,a] ). The code
  363. below is a bit ugly, but works in O(n*log(m)) and is run once or twice during ROUTER::Move() call
  364. - so I hope it still gets better performance than no cache at all */
  365. for( auto it = m_clearanceCache.begin(); it != m_clearanceCache.end(); )
  366. {
  367. bool dirty = remainingItems.find( it->first.A ) != remainingItems.end();
  368. dirty |= remainingItems.find( it->first.B) != remainingItems.end();
  369. if( dirty )
  370. {
  371. it = m_clearanceCache.erase( it );
  372. n_pruned++;
  373. } else
  374. it++;
  375. }
  376. #if 0
  377. printf("ClearCache : n_pruned %d\n", n_pruned );
  378. #endif
  379. }
  380. void PNS_PCBNEW_RULE_RESOLVER::ClearCaches()
  381. {
  382. m_clearanceCache.clear();
  383. m_tempClearanceCache.clear();
  384. }
  385. void PNS_PCBNEW_RULE_RESOLVER::ClearTemporaryCaches()
  386. {
  387. m_tempClearanceCache.clear();
  388. }
  389. int PNS_PCBNEW_RULE_RESOLVER::Clearance( const PNS::ITEM* aA, const PNS::ITEM* aB,
  390. bool aUseClearanceEpsilon )
  391. {
  392. CLEARANCE_CACHE_KEY key = { aA, aB, aUseClearanceEpsilon };
  393. // Search cache (used for actual board items)
  394. auto it = m_clearanceCache.find( key );
  395. if( it != m_clearanceCache.end() )
  396. return it->second;
  397. // Search cache (used for temporary items within an algorithm)
  398. it = m_tempClearanceCache.find( key );
  399. if( it != m_tempClearanceCache.end() )
  400. return it->second;
  401. PNS::CONSTRAINT constraint;
  402. int rv = 0;
  403. PNS_LAYER_RANGE layers;
  404. if( !aB )
  405. layers = aA->Layers();
  406. else if( isEdge( aA ) )
  407. layers = aB->Layers();
  408. else if( isEdge( aB ) )
  409. layers = aA->Layers();
  410. else
  411. layers = aA->Layers().Intersection( aB->Layers() );
  412. // Normalize layer range (no -1 magic numbers)
  413. layers = layers.Intersection( PNS_LAYER_RANGE( PCBNEW_LAYER_ID_START, PCB_LAYER_ID_COUNT - 1 ) );
  414. for( int layer = layers.Start(); layer <= layers.End(); ++layer )
  415. {
  416. if( IsDrilledHole( aA ) && IsDrilledHole( aB ) )
  417. {
  418. if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE, aA, aB, layer, &constraint ) )
  419. {
  420. if( constraint.m_Value.Min() > rv )
  421. rv = constraint.m_Value.Min();
  422. }
  423. }
  424. else if( isHole( aA ) || isHole( aB ) )
  425. {
  426. if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_CLEARANCE, aA, aB, layer, &constraint ) )
  427. {
  428. if( constraint.m_Value.Min() > rv )
  429. rv = constraint.m_Value.Min();
  430. }
  431. }
  432. // No 'else'; plated holes get both HOLE_CLEARANCE and CLEARANCE
  433. if( isCopper( aA ) && ( !aB || isCopper( aB ) ) )
  434. {
  435. if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_CLEARANCE, aA, aB, layer, &constraint ) )
  436. {
  437. if( constraint.m_Value.Min() > rv )
  438. rv = constraint.m_Value.Min();
  439. }
  440. }
  441. // No 'else'; non-plated milled holes get both HOLE_CLEARANCE and EDGE_CLEARANCE
  442. if( isEdge( aA ) || IsNonPlatedSlot( aA ) || isEdge( aB ) || IsNonPlatedSlot( aB ) )
  443. {
  444. if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_EDGE_CLEARANCE, aA, aB, layer, &constraint ) )
  445. {
  446. if( constraint.m_Value.Min() > rv )
  447. rv = constraint.m_Value.Min();
  448. }
  449. }
  450. if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_PHYSICAL_CLEARANCE, aA, aB, layer, &constraint ) )
  451. {
  452. if( constraint.m_Value.Min() > rv )
  453. rv = constraint.m_Value.Min();
  454. }
  455. }
  456. if( aUseClearanceEpsilon && rv > 0 )
  457. rv = std::max( 0, rv - m_clearanceEpsilon );
  458. /*
  459. * It makes no sense to put items that have no owning NODE in the cache - they can be
  460. * allocated on stack and we can't really invalidate them in the cache when they are
  461. * destroyed. Probably a better idea would be to use a static unique counter in PNS::ITEM
  462. * constructor to generate the cache keys.
  463. *
  464. * However, algorithms DO greatly benefit from using the cache, so ownerless items need to be
  465. * cached. In order to easily clear those only, a temporary cache is created. If this doesn't
  466. * seem nice, an alternative is clearing the full cache once it reaches a certain size. Also
  467. * not pretty, but VERY effective to keep things interactive.
  468. */
  469. if( aA && aB )
  470. {
  471. if ( aA->Owner() && aB->Owner() )
  472. m_clearanceCache[ key ] = rv;
  473. else
  474. m_tempClearanceCache[ key ] = rv;
  475. }
  476. return rv;
  477. }
  478. bool PNS_KICAD_IFACE_BASE::inheritTrackWidth( PNS::ITEM* aItem, int* aInheritedWidth )
  479. {
  480. VECTOR2I p;
  481. assert( aItem->Owner() != nullptr );
  482. auto tryGetTrackWidth =
  483. []( PNS::ITEM* aPnsItem ) -> int
  484. {
  485. switch( aPnsItem->Kind() )
  486. {
  487. case PNS::ITEM::SEGMENT_T: return static_cast<PNS::SEGMENT*>( aPnsItem )->Width();
  488. case PNS::ITEM::ARC_T: return static_cast<PNS::ARC*>( aPnsItem )->Width();
  489. default: return -1;
  490. }
  491. };
  492. int itemTrackWidth = tryGetTrackWidth( aItem );
  493. if( itemTrackWidth > 0 )
  494. {
  495. *aInheritedWidth = itemTrackWidth;
  496. return true;
  497. }
  498. switch( aItem->Kind() )
  499. {
  500. case PNS::ITEM::VIA_T: p = static_cast<PNS::VIA*>( aItem )->Pos(); break;
  501. case PNS::ITEM::SOLID_T: p = static_cast<PNS::SOLID*>( aItem )->Pos(); break;
  502. default: return false;
  503. }
  504. const PNS::JOINT* jt = static_cast<const PNS::NODE*>( aItem->Owner() )->FindJoint( p, aItem );
  505. assert( jt != nullptr );
  506. int mval = INT_MAX;
  507. PNS::ITEM_SET linkedSegs( jt->CLinks() );
  508. linkedSegs.ExcludeItem( aItem ).FilterKinds( PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T );
  509. for( PNS::ITEM* item : linkedSegs.Items() )
  510. {
  511. int w = tryGetTrackWidth( item );
  512. if( w > 0 )
  513. mval = std::min( w, mval );
  514. }
  515. if( mval == INT_MAX )
  516. return false;
  517. *aInheritedWidth = mval;
  518. return true;
  519. }
  520. bool PNS_KICAD_IFACE_BASE::ImportSizes( PNS::SIZES_SETTINGS& aSizes, PNS::ITEM* aStartItem,
  521. PNS::NET_HANDLE aNet, VECTOR2D aStartPosition )
  522. {
  523. BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
  524. PNS::CONSTRAINT constraint;
  525. if( aStartItem && m_startLayer < 0 )
  526. m_startLayer = aStartItem->Layer();
  527. aSizes.SetClearance( bds.m_MinClearance );
  528. aSizes.SetMinClearance( bds.m_MinClearance );
  529. aSizes.SetClearanceSource( _( "board minimum clearance" ) );
  530. int startAnchor = 0;
  531. VECTOR2I startPosInt( aStartPosition.x, aStartPosition.y );
  532. if( aStartItem && aStartItem->Kind() == PNS::ITEM::SEGMENT_T )
  533. {
  534. // Find the start anchor which is closest to the start mouse location
  535. double anchor0Distance = startPosInt.Distance( aStartItem->Anchor( 0 ) );
  536. double anchor1Distance = startPosInt.Distance( aStartItem->Anchor( 1 ) );
  537. if( anchor1Distance < anchor0Distance )
  538. startAnchor = 1;
  539. }
  540. if( aStartItem )
  541. {
  542. PNS::SEGMENT dummyTrack;
  543. dummyTrack.SetEnds( aStartItem->Anchor( startAnchor ), aStartItem->Anchor( startAnchor ) );
  544. dummyTrack.SetLayer( m_startLayer );
  545. dummyTrack.SetNet( static_cast<NETINFO_ITEM*>( aStartItem->Net() ) );
  546. if( m_ruleResolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_CLEARANCE, &dummyTrack,
  547. nullptr, m_startLayer, &constraint ) )
  548. {
  549. if( constraint.m_Value.Min() >= bds.m_MinClearance )
  550. {
  551. aSizes.SetClearance( constraint.m_Value.Min() );
  552. aSizes.SetClearanceSource( constraint.m_RuleName );
  553. }
  554. }
  555. }
  556. int trackWidth = bds.m_TrackMinWidth;
  557. bool found = false;
  558. aSizes.SetWidthSource( _( "board minimum track width" ) );
  559. if( bds.m_UseConnectedTrackWidth && !bds.m_TempOverrideTrackWidth && aStartItem != nullptr )
  560. {
  561. found = inheritTrackWidth( aStartItem, &trackWidth );
  562. if( found )
  563. aSizes.SetWidthSource( _( "existing track" ) );
  564. }
  565. if( !found && bds.UseNetClassTrack() && aStartItem )
  566. {
  567. PNS::SEGMENT dummyTrack;
  568. dummyTrack.SetEnds( aStartItem->Anchor( startAnchor ), aStartItem->Anchor( startAnchor ) );
  569. dummyTrack.SetLayer( m_startLayer );
  570. dummyTrack.SetNet( static_cast<NETINFO_ITEM*>( aStartItem->Net() ) );
  571. if( m_ruleResolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_WIDTH, &dummyTrack, nullptr,
  572. m_startLayer, &constraint ) )
  573. {
  574. trackWidth = std::max( trackWidth, constraint.m_Value.Opt() );
  575. found = true;
  576. if( trackWidth == constraint.m_Value.Opt() )
  577. aSizes.SetWidthSource( constraint.m_RuleName );
  578. }
  579. }
  580. if( !found )
  581. {
  582. trackWidth = std::max( trackWidth, bds.GetCurrentTrackWidth() );
  583. if( bds.UseNetClassTrack() )
  584. aSizes.SetWidthSource( _( "netclass 'Default'" ) );
  585. else if( trackWidth == bds.GetCurrentTrackWidth() )
  586. aSizes.SetWidthSource( _( "user choice" ) );
  587. }
  588. aSizes.SetTrackWidth( trackWidth );
  589. aSizes.SetBoardMinTrackWidth( bds.m_TrackMinWidth );
  590. aSizes.SetTrackWidthIsExplicit( !bds.m_UseConnectedTrackWidth || bds.m_TempOverrideTrackWidth );
  591. int viaDiameter = bds.m_ViasMinSize;
  592. int viaDrill = bds.m_MinThroughDrill;
  593. PNS::VIA dummyVia, coupledVia;
  594. if( aStartItem )
  595. {
  596. dummyVia.SetNet( aStartItem->Net() );
  597. coupledVia.SetNet( m_ruleResolver->DpCoupledNet( aStartItem->Net() ) );
  598. }
  599. if( bds.UseNetClassVia() && aStartItem ) // netclass value
  600. {
  601. if( m_ruleResolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_VIA_DIAMETER, &dummyVia,
  602. nullptr, m_startLayer, &constraint ) )
  603. {
  604. viaDiameter = std::max( viaDiameter, constraint.m_Value.Opt() );
  605. }
  606. if( m_ruleResolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_VIA_HOLE, &dummyVia,
  607. nullptr, m_startLayer, &constraint ) )
  608. {
  609. viaDrill = std::max( viaDrill, constraint.m_Value.Opt() );
  610. }
  611. }
  612. else
  613. {
  614. viaDiameter = bds.GetCurrentViaSize();
  615. viaDrill = bds.GetCurrentViaDrill();
  616. }
  617. aSizes.SetViaDiameter( viaDiameter );
  618. aSizes.SetViaDrill( viaDrill );
  619. int diffPairWidth = bds.m_TrackMinWidth;
  620. int diffPairGap = bds.m_MinClearance;
  621. int diffPairViaGap = bds.m_MinClearance;
  622. aSizes.SetDiffPairWidthSource( _( "board minimum track width" ) );
  623. aSizes.SetDiffPairGapSource( _( "board minimum clearance" ) );
  624. found = false;
  625. // First try to pick up diff pair width from starting track, if enabled
  626. if( bds.m_UseConnectedTrackWidth && aStartItem )
  627. found = inheritTrackWidth( aStartItem, &diffPairWidth );
  628. // Next, pick up gap from netclass, and width also if we didn't get a starting width above
  629. if( bds.UseNetClassDiffPair() && aStartItem )
  630. {
  631. PNS::NET_HANDLE coupledNet = m_ruleResolver->DpCoupledNet( aStartItem->Net() );
  632. PNS::SEGMENT dummyTrack;
  633. dummyTrack.SetEnds( aStartItem->Anchor( 0 ), aStartItem->Anchor( 0 ) );
  634. dummyTrack.SetLayer( m_startLayer );
  635. dummyTrack.SetNet( static_cast<NETINFO_ITEM*>( aStartItem->Net() ) );
  636. PNS::SEGMENT coupledTrack;
  637. dummyTrack.SetEnds( aStartItem->Anchor( 0 ), aStartItem->Anchor( 0 ) );
  638. dummyTrack.SetLayer( m_startLayer );
  639. dummyTrack.SetNet( static_cast<NETINFO_ITEM*>( coupledNet ) );
  640. if( !found
  641. && m_ruleResolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_WIDTH, &dummyTrack,
  642. &coupledTrack, m_startLayer, &constraint ) )
  643. {
  644. diffPairWidth = std::max( diffPairWidth, constraint.m_Value.Opt() );
  645. if( diffPairWidth == constraint.m_Value.Opt() )
  646. aSizes.SetDiffPairWidthSource( constraint.m_RuleName );
  647. }
  648. if( m_ruleResolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_DIFF_PAIR_GAP, &dummyTrack,
  649. &coupledTrack, m_startLayer, &constraint ) )
  650. {
  651. diffPairGap = std::max( diffPairGap, constraint.m_Value.Opt() );
  652. diffPairViaGap = std::max( diffPairViaGap, constraint.m_Value.Opt() );
  653. if( diffPairGap == constraint.m_Value.Opt() )
  654. aSizes.SetDiffPairGapSource( constraint.m_RuleName );
  655. }
  656. }
  657. else
  658. {
  659. diffPairWidth = bds.GetCurrentDiffPairWidth();
  660. diffPairGap = bds.GetCurrentDiffPairGap();
  661. diffPairViaGap = bds.GetCurrentDiffPairViaGap();
  662. aSizes.SetDiffPairWidthSource( _( "user choice" ) );
  663. aSizes.SetDiffPairGapSource( _( "user choice" ) );
  664. }
  665. aSizes.SetDiffPairWidth( diffPairWidth );
  666. aSizes.SetDiffPairGap( diffPairGap );
  667. aSizes.SetDiffPairViaGap( diffPairViaGap );
  668. aSizes.SetDiffPairViaGapSameAsTraceGap( false );
  669. int holeToHoleMin = bds.m_HoleToHoleMin;
  670. if( m_ruleResolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE, &dummyVia,
  671. &dummyVia, UNDEFINED_LAYER, &constraint ) )
  672. {
  673. holeToHoleMin = constraint.m_Value.Min();
  674. }
  675. aSizes.SetHoleToHole( holeToHoleMin );
  676. if( m_ruleResolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE, &dummyVia,
  677. &coupledVia, UNDEFINED_LAYER, &constraint ) )
  678. {
  679. holeToHoleMin = constraint.m_Value.Min();
  680. }
  681. aSizes.SetDiffPairHoleToHole( holeToHoleMin );
  682. return true;
  683. }
  684. int PNS_KICAD_IFACE_BASE::StackupHeight( int aFirstLayer, int aSecondLayer ) const
  685. {
  686. if( !m_board || !m_board->GetDesignSettings().m_UseHeightForLengthCalcs )
  687. return 0;
  688. BOARD_STACKUP& stackup = m_board->GetDesignSettings().GetStackupDescriptor();
  689. return stackup.GetLayerDistance( GetBoardLayerFromPNSLayer( aFirstLayer ),
  690. GetBoardLayerFromPNSLayer( aSecondLayer ) );
  691. }
  692. PNS::NET_HANDLE PNS_PCBNEW_RULE_RESOLVER::DpCoupledNet( PNS::NET_HANDLE aNet )
  693. {
  694. return m_board->DpCoupledNet( static_cast<NETINFO_ITEM*>( aNet ) );
  695. }
  696. int PNS_PCBNEW_RULE_RESOLVER::NetCode( PNS::NET_HANDLE aNet )
  697. {
  698. return m_routerIface->GetNetCode( aNet );
  699. }
  700. wxString PNS_PCBNEW_RULE_RESOLVER::NetName( PNS::NET_HANDLE aNet )
  701. {
  702. return m_routerIface->GetNetName( aNet );
  703. }
  704. int PNS_PCBNEW_RULE_RESOLVER::DpNetPolarity( PNS::NET_HANDLE aNet )
  705. {
  706. wxString refName;
  707. if( NETINFO_ITEM* net = static_cast<NETINFO_ITEM*>( aNet ) )
  708. refName = net->GetNetname();
  709. wxString dummy1;
  710. return m_board->MatchDpSuffix( refName, dummy1 );
  711. }
  712. bool PNS_PCBNEW_RULE_RESOLVER::DpNetPair( const PNS::ITEM* aItem, PNS::NET_HANDLE& aNetP,
  713. PNS::NET_HANDLE& aNetN )
  714. {
  715. if( !aItem || !aItem->Net() )
  716. return false;
  717. wxString netNameP = static_cast<NETINFO_ITEM*>( aItem->Net() )->GetNetname();
  718. wxString netNameN, netNameCoupled;
  719. int r = m_board->MatchDpSuffix( netNameP, netNameCoupled );
  720. if( r == 0 )
  721. {
  722. return false;
  723. }
  724. else if( r == 1 )
  725. {
  726. netNameN = netNameCoupled;
  727. }
  728. else
  729. {
  730. netNameN = netNameP;
  731. netNameP = netNameCoupled;
  732. }
  733. PNS::NET_HANDLE netInfoP = m_board->FindNet( netNameP );
  734. PNS::NET_HANDLE netInfoN = m_board->FindNet( netNameN );
  735. if( !netInfoP || !netInfoN )
  736. return false;
  737. aNetP = netInfoP;
  738. aNetN = netInfoN;
  739. return true;
  740. }
  741. class PNS_PCBNEW_DEBUG_DECORATOR: public PNS::DEBUG_DECORATOR
  742. {
  743. public:
  744. PNS_PCBNEW_DEBUG_DECORATOR( PNS::ROUTER_IFACE* aIface ) :
  745. PNS::DEBUG_DECORATOR(),
  746. m_iface( aIface ),
  747. m_view( nullptr ),
  748. m_items( nullptr ),
  749. m_depth( 0 )
  750. {}
  751. ~PNS_PCBNEW_DEBUG_DECORATOR()
  752. {
  753. PNS_PCBNEW_DEBUG_DECORATOR::Clear();
  754. delete m_items;
  755. }
  756. void SetView( KIGFX::VIEW* aView )
  757. {
  758. Clear();
  759. delete m_items;
  760. m_items = nullptr;
  761. m_view = aView;
  762. if( m_view == nullptr )
  763. return;
  764. if( m_view->GetGAL() )
  765. m_depth = m_view->GetGAL()->GetMinDepth();
  766. m_items = new KIGFX::VIEW_GROUP( m_view );
  767. m_items->SetLayer( LAYER_SELECT_OVERLAY ) ;
  768. m_view->Add( m_items );
  769. }
  770. void AddPoint( const VECTOR2I& aP, const KIGFX::COLOR4D& aColor, int aSize,
  771. const wxString& aName = wxT( "" ),
  772. const SRC_LOCATION_INFO& aSrcLoc = SRC_LOCATION_INFO() ) override
  773. {
  774. SHAPE_LINE_CHAIN sh;
  775. sh.SetWidth( 10000 );
  776. sh.Append( aP.x - aSize, aP.y - aSize );
  777. sh.Append( aP.x + aSize, aP.y + aSize );
  778. sh.Append( aP.x, aP.y );
  779. sh.Append( aP.x - aSize, aP.y + aSize );
  780. sh.Append( aP.x + aSize, aP.y - aSize );
  781. AddShape( &sh, aColor, sh.Width(), aName, aSrcLoc );
  782. }
  783. void AddItem( const PNS::ITEM* aItem, const KIGFX::COLOR4D& aColor, int aOverrideWidth = 0,
  784. const wxString& aName = wxT( "" ),
  785. const SRC_LOCATION_INFO& aSrcLoc = SRC_LOCATION_INFO() ) override
  786. {
  787. if( !m_view || !aItem )
  788. return;
  789. ROUTER_PREVIEW_ITEM* pitem = new ROUTER_PREVIEW_ITEM( aItem, m_iface, m_view );
  790. pitem->SetColor( aColor.WithAlpha( 0.5 ) );
  791. pitem->SetWidth( aOverrideWidth );
  792. pitem->SetDepth( nextDepth() );
  793. m_items->Add( pitem );
  794. m_view->Update( m_items );
  795. }
  796. void AddShape( const BOX2I& aBox, const KIGFX::COLOR4D& aColor, int aOverrideWidth = 0,
  797. const wxString& aName = wxT( "" ),
  798. const SRC_LOCATION_INFO& aSrcLoc = SRC_LOCATION_INFO() ) override
  799. {
  800. SHAPE_LINE_CHAIN l;
  801. l.SetWidth( aOverrideWidth );
  802. VECTOR2I o = aBox.GetOrigin();
  803. VECTOR2I s = aBox.GetSize();
  804. l.Append( o );
  805. l.Append( o.x + s.x, o.y );
  806. l.Append( o.x + s.x, o.y + s.y );
  807. l.Append( o.x, o.y + s.y );
  808. l.Append( o );
  809. AddShape( &l, aColor, aOverrideWidth, aName, aSrcLoc );
  810. }
  811. void AddShape( const SHAPE* aShape, const KIGFX::COLOR4D& aColor, int aOverrideWidth = 0,
  812. const wxString& aName = wxT( "" ),
  813. const SRC_LOCATION_INFO& aSrcLoc = SRC_LOCATION_INFO() ) override
  814. {
  815. if( !m_view || !aShape )
  816. return;
  817. ROUTER_PREVIEW_ITEM* pitem = new ROUTER_PREVIEW_ITEM( *aShape, m_iface, m_view );
  818. pitem->SetColor( aColor.WithAlpha( 0.5 ) );
  819. pitem->SetWidth( aOverrideWidth );
  820. pitem->SetDepth( nextDepth() );
  821. m_items->Add( pitem );
  822. m_view->Update( m_items );
  823. }
  824. void Clear() override
  825. {
  826. if( m_view && m_items )
  827. {
  828. m_items->FreeItems();
  829. m_view->Update( m_items );
  830. if( m_view->GetGAL() )
  831. m_depth = m_view->GetGAL()->GetMinDepth();
  832. }
  833. }
  834. virtual void Message( const wxString& msg,
  835. const SRC_LOCATION_INFO& aSrcLoc = SRC_LOCATION_INFO() ) override
  836. {
  837. printf("PNS: %s\n", msg.c_str().AsChar() );
  838. }
  839. private:
  840. double nextDepth()
  841. {
  842. // Use different depths so that the transculent shapes won't overwrite each other.
  843. m_depth++;
  844. if( m_depth >= 0 && m_view->GetGAL() )
  845. m_depth = m_view->GetGAL()->GetMinDepth();
  846. return m_depth;
  847. }
  848. PNS::ROUTER_IFACE* m_iface;
  849. KIGFX::VIEW* m_view;
  850. KIGFX::VIEW_GROUP* m_items;
  851. double m_depth;
  852. };
  853. PNS::DEBUG_DECORATOR* PNS_KICAD_IFACE_BASE::GetDebugDecorator()
  854. {
  855. return m_debugDecorator;
  856. }
  857. PNS_KICAD_IFACE_BASE::PNS_KICAD_IFACE_BASE()
  858. {
  859. m_ruleResolver = nullptr;
  860. m_board = nullptr;
  861. m_world = nullptr;
  862. m_debugDecorator = nullptr;
  863. m_startLayer = -1;
  864. }
  865. PNS_KICAD_IFACE::PNS_KICAD_IFACE()
  866. {
  867. m_tool = nullptr;
  868. m_view = nullptr;
  869. m_previewItems = nullptr;
  870. m_commitFlags = 0;
  871. }
  872. PNS_KICAD_IFACE_BASE::~PNS_KICAD_IFACE_BASE()
  873. {
  874. delete m_ruleResolver;
  875. delete m_debugDecorator;
  876. }
  877. PNS_KICAD_IFACE::~PNS_KICAD_IFACE()
  878. {
  879. if( m_previewItems )
  880. {
  881. m_previewItems->FreeItems();
  882. delete m_previewItems;
  883. }
  884. }
  885. std::vector<std::unique_ptr<PNS::SOLID>> PNS_KICAD_IFACE_BASE::syncPad( PAD* aPad )
  886. {
  887. std::vector<std::unique_ptr<PNS::SOLID>> solids;
  888. PNS_LAYER_RANGE layers( 0, aPad->BoardCopperLayerCount() - 1 );
  889. LSEQ lmsk = aPad->GetLayerSet().CuStack();
  890. // ignore non-copper pads except for those with holes
  891. if( lmsk.empty() && aPad->GetDrillSize().x == 0 )
  892. return solids;
  893. switch( aPad->GetAttribute() )
  894. {
  895. case PAD_ATTRIB::PTH:
  896. case PAD_ATTRIB::NPTH:
  897. break;
  898. case PAD_ATTRIB::CONN:
  899. case PAD_ATTRIB::SMD:
  900. {
  901. bool is_copper = false;
  902. if( !lmsk.empty() && aPad->GetAttribute() != PAD_ATTRIB::NPTH )
  903. {
  904. layers = SetLayersFromPCBNew( lmsk.front(), lmsk.front() );
  905. is_copper = true;
  906. }
  907. if( !is_copper )
  908. return solids;
  909. break;
  910. }
  911. default:
  912. wxLogTrace( wxT( "PNS" ), wxT( "unsupported pad type 0x%x" ), aPad->GetAttribute() );
  913. return solids;
  914. }
  915. auto makeSolidFromPadLayer =
  916. [&]( PCB_LAYER_ID aLayer )
  917. {
  918. std::unique_ptr<PNS::SOLID> solid = std::make_unique<PNS::SOLID>();
  919. if( aPad->GetAttribute() == PAD_ATTRIB::NPTH )
  920. solid->SetRoutable( false );
  921. if( aPad->Padstack().Mode() == PADSTACK::MODE::CUSTOM )
  922. {
  923. solid->SetLayer( GetPNSLayerFromBoardLayer( aLayer ) );
  924. }
  925. else if( aPad->Padstack().Mode() == PADSTACK::MODE::FRONT_INNER_BACK )
  926. {
  927. if( aLayer == F_Cu || aLayer == B_Cu )
  928. solid->SetLayer( GetPNSLayerFromBoardLayer( aLayer ) );
  929. else
  930. solid->SetLayers( PNS_LAYER_RANGE( 1, aPad->BoardCopperLayerCount() - 2 ) );
  931. }
  932. else
  933. {
  934. solid->SetLayers( layers );
  935. }
  936. solid->SetNet( aPad->GetNet() );
  937. solid->SetParent( aPad );
  938. solid->SetPadToDie( aPad->GetPadToDieLength() );
  939. solid->SetOrientation( aPad->GetOrientation() );
  940. if( aPad->IsFreePad() )
  941. solid->SetIsFreePad();
  942. VECTOR2I wx_c = aPad->ShapePos( aLayer );
  943. VECTOR2I offset = aPad->GetOffset( aLayer );
  944. VECTOR2I c( wx_c.x, wx_c.y );
  945. RotatePoint( offset, aPad->GetOrientation() );
  946. solid->SetPos( VECTOR2I( c.x - offset.x, c.y - offset.y ) );
  947. solid->SetOffset( VECTOR2I( offset.x, offset.y ) );
  948. if( aPad->GetDrillSize().x > 0 )
  949. {
  950. solid->SetHole( new PNS::HOLE( aPad->GetEffectiveHoleShape()->Clone() ) );
  951. solid->Hole()->SetLayers( PNS_LAYER_RANGE( 0, aPad->BoardCopperLayerCount() - 1 ) );
  952. }
  953. // We generate a single SOLID for a pad, so we have to treat it as ALWAYS_FLASHED and
  954. // then perform layer-specific flashing tests internally.
  955. const std::shared_ptr<SHAPE>& shape =
  956. aPad->GetEffectiveShape( aLayer, FLASHING::ALWAYS_FLASHED );
  957. if( shape->HasIndexableSubshapes() && shape->GetIndexableSubshapeCount() == 1 )
  958. {
  959. std::vector<const SHAPE*> subshapes;
  960. shape->GetIndexableSubshapes( subshapes );
  961. solid->SetShape( subshapes[0]->Clone() );
  962. }
  963. // For anything that's not a single shape we use a polygon. Multiple shapes have a tendency
  964. // to confuse the hull generator. https://gitlab.com/kicad/code/kicad/-/issues/15553
  965. else
  966. {
  967. const std::shared_ptr<SHAPE_POLY_SET>& poly =
  968. aPad->GetEffectivePolygon( aLayer, ERROR_OUTSIDE );
  969. if( poly->OutlineCount() )
  970. solid->SetShape( new SHAPE_SIMPLE( poly->Outline( 0 ) ) );
  971. }
  972. solids.emplace_back( std::move( solid ) );
  973. };
  974. aPad->Padstack().ForEachUniqueLayer( makeSolidFromPadLayer );
  975. return solids;
  976. }
  977. std::unique_ptr<PNS::SEGMENT> PNS_KICAD_IFACE_BASE::syncTrack( PCB_TRACK* aTrack )
  978. {
  979. auto segment = std::make_unique<PNS::SEGMENT>( SEG( aTrack->GetStart(), aTrack->GetEnd() ),
  980. aTrack->GetNet() );
  981. segment->SetWidth( aTrack->GetWidth() );
  982. segment->SetLayer( GetPNSLayerFromBoardLayer( aTrack->GetLayer() ) );
  983. segment->SetParent( aTrack );
  984. if( aTrack->IsLocked() )
  985. segment->Mark( PNS::MK_LOCKED );
  986. if( PCB_GENERATOR* generator = dynamic_cast<PCB_GENERATOR*>( aTrack->GetParentGroup() ) )
  987. {
  988. if( !generator->HasFlag( IN_EDIT ) )
  989. segment->Mark( PNS::MK_LOCKED );
  990. }
  991. return segment;
  992. }
  993. std::unique_ptr<PNS::ARC> PNS_KICAD_IFACE_BASE::syncArc( PCB_ARC* aArc )
  994. {
  995. auto arc = std::make_unique<PNS::ARC>( SHAPE_ARC( aArc->GetStart(), aArc->GetMid(),
  996. aArc->GetEnd(), aArc->GetWidth() ),
  997. aArc->GetNet() );
  998. arc->SetLayer( GetPNSLayerFromBoardLayer( aArc->GetLayer() ) );
  999. arc->SetParent( aArc );
  1000. if( aArc->IsLocked() )
  1001. arc->Mark( PNS::MK_LOCKED );
  1002. if( PCB_GENERATOR* generator = dynamic_cast<PCB_GENERATOR*>( aArc->GetParentGroup() ) )
  1003. {
  1004. if( !generator->HasFlag( IN_EDIT ) )
  1005. arc->Mark( PNS::MK_LOCKED );
  1006. }
  1007. return arc;
  1008. }
  1009. std::unique_ptr<PNS::VIA> PNS_KICAD_IFACE_BASE::syncVia( PCB_VIA* aVia )
  1010. {
  1011. PCB_LAYER_ID top, bottom;
  1012. aVia->LayerPair( &top, &bottom );
  1013. /*
  1014. * NOTE about PNS via padstacks:
  1015. *
  1016. * PNS::VIA has no knowledge about how many layers are in the board, and there is no fixed
  1017. * reference to the "back layer" in the PNS. That means that there is no way for a VIA to know
  1018. * the difference between its bottom layer and the bottom layer of the overall board (i.e. if
  1019. * the via is a blind/buried via). For this reason, PNS::VIA::STACK_MODE::FRONT_INNER_BACK
  1020. * cannot be used for blind/buried vias. This mode will always assume that the via's top layer
  1021. * is the "front" layer and the via's bottom layer is the "back" layer, but from KiCad's point
  1022. * of view, at least at the moment, front/inner/back padstack mode is board-scoped, not
  1023. * via-scoped, so a buried via would only use the inner layer size even if its padstack mode is
  1024. * set to PADSTACK::MODE::FRONT_INNER_BACK and different sizes are defined for front or back.
  1025. * For this kind of via, the PNS VIA stack mode will be set to NORMAL because effectively it has
  1026. * the same size on every layer it exists on.
  1027. */
  1028. auto via = std::make_unique<PNS::VIA>( aVia->GetPosition(),
  1029. SetLayersFromPCBNew( aVia->TopLayer(), aVia->BottomLayer() ),
  1030. 0,
  1031. aVia->GetDrillValue(),
  1032. aVia->GetNet(),
  1033. aVia->GetViaType() );
  1034. auto syncDiameter =
  1035. [&]( PCB_LAYER_ID aLayer )
  1036. {
  1037. via->SetDiameter( GetPNSLayerFromBoardLayer( aLayer ), aVia->GetWidth( aLayer ) );
  1038. };
  1039. switch( aVia->Padstack().Mode() )
  1040. {
  1041. case PADSTACK::MODE::NORMAL:
  1042. via->SetDiameter( 0, aVia->GetWidth( PADSTACK::ALL_LAYERS ) );
  1043. break;
  1044. case PADSTACK::MODE::FRONT_INNER_BACK:
  1045. if( aVia->GetViaType() == VIATYPE::BLIND_BURIED )
  1046. {
  1047. via->SetDiameter( 0, aVia->GetWidth( PADSTACK::INNER_LAYERS ) );
  1048. }
  1049. else
  1050. {
  1051. via->SetStackMode( PNS::VIA::STACK_MODE::FRONT_INNER_BACK );
  1052. aVia->Padstack().ForEachUniqueLayer( syncDiameter );
  1053. }
  1054. break;
  1055. case PADSTACK::MODE::CUSTOM:
  1056. via->SetStackMode( PNS::VIA::STACK_MODE::CUSTOM );
  1057. aVia->Padstack().ForEachUniqueLayer( syncDiameter );
  1058. }
  1059. via->SetParent( aVia );
  1060. if( aVia->IsLocked() )
  1061. via->Mark( PNS::MK_LOCKED );
  1062. if( PCB_GENERATOR* generator = dynamic_cast<PCB_GENERATOR*>( aVia->GetParentGroup() ) )
  1063. {
  1064. if( !generator->HasFlag( IN_EDIT ) )
  1065. via->Mark( PNS::MK_LOCKED );
  1066. }
  1067. via->SetIsFree( aVia->GetIsFree() );
  1068. via->SetHole( PNS::HOLE::MakeCircularHole( aVia->GetPosition(),
  1069. aVia->GetDrillValue() / 2,
  1070. SetLayersFromPCBNew( aVia->TopLayer(), aVia->BottomLayer() ) ) );
  1071. return via;
  1072. }
  1073. bool PNS_KICAD_IFACE_BASE::syncZone( PNS::NODE* aWorld, ZONE* aZone, SHAPE_POLY_SET* aBoardOutline )
  1074. {
  1075. static wxString msg;
  1076. SHAPE_POLY_SET* poly;
  1077. if( !aZone->GetIsRuleArea() || !aZone->HasKeepoutParametersSet() )
  1078. return false;
  1079. LSET layers = aZone->GetLayerSet();
  1080. poly = aZone->Outline();
  1081. poly->CacheTriangulation( false );
  1082. if( !poly->IsTriangulationUpToDate() )
  1083. {
  1084. UNITS_PROVIDER unitsProvider( pcbIUScale, GetUnits() );
  1085. msg.Printf( _( "%s is malformed." ), aZone->GetItemDescription( &unitsProvider, true ) );
  1086. KIDIALOG dlg( nullptr, msg, KIDIALOG::KD_WARNING );
  1087. dlg.ShowDetailedText( _( "This zone cannot be handled by the router.\n"
  1088. "Please verify it is not a self-intersecting polygon." ) );
  1089. dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
  1090. dlg.ShowModal();
  1091. return false;
  1092. }
  1093. for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, m_board->GetCopperLayerCount() ) )
  1094. {
  1095. if( !layers[ layer ] )
  1096. continue;
  1097. for( int polyId = 0; polyId < poly->TriangulatedPolyCount(); polyId++ )
  1098. {
  1099. const SHAPE_POLY_SET::TRIANGULATED_POLYGON* tri = poly->TriangulatedPolygon( polyId );
  1100. for( size_t i = 0; i < tri->GetTriangleCount(); i++)
  1101. {
  1102. VECTOR2I a, b, c;
  1103. tri->GetTriangle( i, a, b, c );
  1104. SHAPE_SIMPLE* triShape = new SHAPE_SIMPLE;
  1105. triShape->Append( a );
  1106. triShape->Append( b );
  1107. triShape->Append( c );
  1108. std::unique_ptr<PNS::SOLID> solid = std::make_unique<PNS::SOLID>();
  1109. solid->SetLayer( GetPNSLayerFromBoardLayer( layer ) );
  1110. solid->SetNet( nullptr );
  1111. solid->SetParent( aZone );
  1112. solid->SetShape( triShape );
  1113. solid->SetIsCompoundShapePrimitive();
  1114. solid->SetRoutable( false );
  1115. aWorld->Add( std::move( solid ) );
  1116. }
  1117. }
  1118. }
  1119. return true;
  1120. }
  1121. bool PNS_KICAD_IFACE_BASE::syncTextItem( PNS::NODE* aWorld, BOARD_ITEM* aItem, PCB_LAYER_ID aLayer )
  1122. {
  1123. if( !IsKicadCopperLayer( aLayer ) )
  1124. return false;
  1125. if( aItem->Type() == PCB_FIELD_T && !static_cast<PCB_FIELD*>( aItem )->IsVisible() )
  1126. return false;
  1127. std::unique_ptr<PNS::SOLID> solid = std::make_unique<PNS::SOLID>();
  1128. SHAPE_SIMPLE* shape = new SHAPE_SIMPLE;
  1129. solid->SetLayer( GetPNSLayerFromBoardLayer( aLayer ) );
  1130. solid->SetNet( nullptr );
  1131. solid->SetParent( aItem );
  1132. solid->SetShape( shape ); // takes ownership
  1133. solid->SetRoutable( false );
  1134. SHAPE_POLY_SET cornerBuffer;
  1135. aItem->TransformShapeToPolygon( cornerBuffer, aItem->GetLayer(), 0,
  1136. m_board->GetDesignSettings().m_MaxError, ERROR_OUTSIDE );
  1137. cornerBuffer.Simplify();
  1138. if( !cornerBuffer.OutlineCount() )
  1139. return false;
  1140. for( const VECTOR2I& pt : cornerBuffer.Outline( 0 ).CPoints() )
  1141. shape->Append( pt );
  1142. aWorld->Add( std::move( solid ) );
  1143. return true;
  1144. }
  1145. bool PNS_KICAD_IFACE_BASE::syncGraphicalItem( PNS::NODE* aWorld, PCB_SHAPE* aItem )
  1146. {
  1147. if( aItem->GetLayer() == Edge_Cuts
  1148. || aItem->GetLayer() == Margin
  1149. || IsKicadCopperLayer( aItem->GetLayer() ) )
  1150. {
  1151. std::vector<SHAPE*> shapes = aItem->MakeEffectiveShapes();
  1152. for( SHAPE* shape : shapes )
  1153. {
  1154. std::unique_ptr<PNS::SOLID> solid = std::make_unique<PNS::SOLID>();
  1155. if( aItem->GetLayer() == Edge_Cuts || aItem->GetLayer() == Margin )
  1156. {
  1157. solid->SetLayers( PNS_LAYER_RANGE( 0, m_board->GetCopperLayerCount() - 1 ) );
  1158. solid->SetRoutable( false );
  1159. }
  1160. else
  1161. {
  1162. solid->SetLayer( GetPNSLayerFromBoardLayer( aItem->GetLayer() ) );
  1163. solid->SetRoutable( aItem->Type() != PCB_TABLECELL_T );
  1164. }
  1165. if( aItem->GetLayer() == Edge_Cuts )
  1166. {
  1167. switch( shape->Type() )
  1168. {
  1169. case SH_SEGMENT: static_cast<SHAPE_SEGMENT*>( shape )->SetWidth( 0 ); break;
  1170. case SH_ARC: static_cast<SHAPE_ARC*>( shape )->SetWidth( 0 ); break;
  1171. case SH_LINE_CHAIN: static_cast<SHAPE_LINE_CHAIN*>( shape )->SetWidth( 0 ); break;
  1172. default: /* remaining shapes don't have width */ break;
  1173. }
  1174. }
  1175. solid->SetAnchorPoints( aItem->GetConnectionPoints() );
  1176. solid->SetNet( aItem->GetNet() );
  1177. solid->SetParent( aItem );
  1178. solid->SetShape( shape ); // takes ownership
  1179. if( shapes.size() > 1 )
  1180. solid->SetIsCompoundShapePrimitive();
  1181. aWorld->Add( std::move( solid ) );
  1182. }
  1183. return true;
  1184. }
  1185. return false;
  1186. }
  1187. void PNS_KICAD_IFACE_BASE::SetBoard( BOARD* aBoard )
  1188. {
  1189. m_board = aBoard;
  1190. wxLogTrace( wxT( "PNS" ), wxT( "m_board = %p" ), m_board );
  1191. }
  1192. bool PNS_KICAD_IFACE_BASE::IsPNSCopperLayer( int aPNSLayer ) const
  1193. {
  1194. return ::IsCopperLayer( GetBoardLayerFromPNSLayer( aPNSLayer ) );
  1195. }
  1196. bool PNS_KICAD_IFACE_BASE::IsKicadCopperLayer( PCB_LAYER_ID aKicadLayer ) const
  1197. {
  1198. return ::IsCopperLayer( aKicadLayer );
  1199. }
  1200. bool PNS_KICAD_IFACE::IsAnyLayerVisible( const PNS_LAYER_RANGE& aLayer ) const
  1201. {
  1202. if( !m_view )
  1203. return false;
  1204. for( int i = aLayer.Start(); i <= aLayer.End(); i++ )
  1205. {
  1206. if( m_view->IsLayerVisible( GetBoardLayerFromPNSLayer( i ) ) )
  1207. return true;
  1208. }
  1209. return false;
  1210. }
  1211. bool PNS_KICAD_IFACE_BASE::IsFlashedOnLayer( const PNS::ITEM* aItem, int aLayer ) const
  1212. {
  1213. /// Default is all layers
  1214. if( aLayer < 0 )
  1215. return true;
  1216. if( aItem->Parent() )
  1217. {
  1218. switch( aItem->Parent()->Type() )
  1219. {
  1220. case PCB_VIA_T:
  1221. {
  1222. const PCB_VIA* via = static_cast<const PCB_VIA*>( aItem->Parent() );
  1223. return via->FlashLayer( GetBoardLayerFromPNSLayer( aLayer ) );
  1224. }
  1225. case PCB_PAD_T:
  1226. {
  1227. const PAD* pad = static_cast<const PAD*>( aItem->Parent() );
  1228. return pad->FlashLayer( GetBoardLayerFromPNSLayer( aLayer ) );
  1229. }
  1230. default:
  1231. break;
  1232. }
  1233. }
  1234. return aItem->Layers().Overlaps( aLayer );
  1235. }
  1236. bool PNS_KICAD_IFACE_BASE::IsFlashedOnLayer( const PNS::ITEM* aItem,
  1237. const PNS_LAYER_RANGE& aLayer ) const
  1238. {
  1239. PNS_LAYER_RANGE test = aItem->Layers().Intersection( aLayer );
  1240. if( aItem->Parent() )
  1241. {
  1242. switch( aItem->Parent()->Type() )
  1243. {
  1244. case PCB_VIA_T:
  1245. {
  1246. const PCB_VIA* via = static_cast<const PCB_VIA*>( aItem->Parent() );
  1247. for( int layer = test.Start(); layer <= test.End(); ++layer )
  1248. {
  1249. if( via->FlashLayer( GetBoardLayerFromPNSLayer( layer ) ) )
  1250. return true;
  1251. }
  1252. return false;
  1253. }
  1254. case PCB_PAD_T:
  1255. {
  1256. const PAD* pad = static_cast<const PAD*>( aItem->Parent() );
  1257. for( int layer = test.Start(); layer <= test.End(); ++layer )
  1258. {
  1259. if( pad->FlashLayer( GetBoardLayerFromPNSLayer( layer ) ) )
  1260. return true;
  1261. }
  1262. return false;
  1263. }
  1264. default:
  1265. break;
  1266. }
  1267. }
  1268. return test.Start() <= test.End();
  1269. }
  1270. bool PNS_KICAD_IFACE::IsItemVisible( const PNS::ITEM* aItem ) const
  1271. {
  1272. // by default, all items are visible (new ones created by the router have parent == NULL
  1273. // as they have not been committed yet to the BOARD)
  1274. if( !m_view || !aItem->Parent() )
  1275. return true;
  1276. BOARD_ITEM* item = aItem->Parent();
  1277. bool isOnVisibleLayer = true;
  1278. RENDER_SETTINGS* settings = m_view->GetPainter()->GetSettings();
  1279. if( settings->GetHighContrast() )
  1280. isOnVisibleLayer = item->IsOnLayer( settings->GetPrimaryHighContrastLayer() );
  1281. if( m_view->IsVisible( item ) && isOnVisibleLayer )
  1282. {
  1283. for( PCB_LAYER_ID layer : item->GetLayerSet().Seq() )
  1284. {
  1285. if( item->ViewGetLOD( layer, m_view ) < m_view->GetScale() )
  1286. return true;
  1287. }
  1288. }
  1289. // Items hidden in the router are not hidden on the board
  1290. if( m_hiddenItems.find( item ) != m_hiddenItems.end() )
  1291. return true;
  1292. return false;
  1293. }
  1294. void PNS_KICAD_IFACE_BASE::SyncWorld( PNS::NODE *aWorld )
  1295. {
  1296. if( !m_board )
  1297. {
  1298. wxLogTrace( wxT( "PNS" ), wxT( "No board attached, aborting sync." ) );
  1299. return;
  1300. }
  1301. int worstClearance = m_board->GetMaxClearanceValue();
  1302. m_world = aWorld;
  1303. for( BOARD_ITEM* gitem : m_board->Drawings() )
  1304. {
  1305. if ( gitem->Type() == PCB_SHAPE_T || gitem->Type() == PCB_TEXTBOX_T )
  1306. {
  1307. syncGraphicalItem( aWorld, static_cast<PCB_SHAPE*>( gitem ) );
  1308. }
  1309. else if( gitem->Type() == PCB_TEXT_T )
  1310. {
  1311. syncTextItem( aWorld, static_cast<PCB_TEXT*>( gitem ), gitem->GetLayer() );
  1312. }
  1313. else if( gitem->Type() == PCB_TABLE_T )
  1314. {
  1315. syncTextItem( aWorld, static_cast<PCB_TABLE*>( gitem ), gitem->GetLayer() );
  1316. }
  1317. }
  1318. SHAPE_POLY_SET buffer;
  1319. SHAPE_POLY_SET* boardOutline = nullptr;
  1320. if( m_board->GetBoardPolygonOutlines( buffer ) )
  1321. boardOutline = &buffer;
  1322. for( ZONE* zone : m_board->Zones() )
  1323. {
  1324. syncZone( aWorld, zone, boardOutline );
  1325. }
  1326. for( FOOTPRINT* footprint : m_board->Footprints() )
  1327. {
  1328. for( PAD* pad : footprint->Pads() )
  1329. {
  1330. std::vector<std::unique_ptr<PNS::SOLID>> solids = syncPad( pad );
  1331. for( std::unique_ptr<PNS::SOLID>& solid : solids )
  1332. aWorld->Add( std::move( solid ) );
  1333. std::optional<int> clearanceOverride = pad->GetClearanceOverrides( nullptr );
  1334. if( clearanceOverride.has_value() )
  1335. worstClearance = std::max( worstClearance, clearanceOverride.value() );
  1336. if( pad->GetProperty() == PAD_PROP::CASTELLATED )
  1337. {
  1338. std::unique_ptr<SHAPE> hole;
  1339. hole.reset( pad->GetEffectiveHoleShape()->Clone() );
  1340. aWorld->AddEdgeExclusion( std::move( hole ) );
  1341. }
  1342. }
  1343. syncTextItem( aWorld, &footprint->Reference(), footprint->Reference().GetLayer() );
  1344. syncTextItem( aWorld, &footprint->Value(), footprint->Value().GetLayer() );
  1345. for( ZONE* zone : footprint->Zones() )
  1346. syncZone( aWorld, zone, boardOutline );
  1347. for( PCB_FIELD* field : footprint->GetFields() )
  1348. syncTextItem( aWorld, static_cast<PCB_TEXT*>( field ), field->GetLayer() );
  1349. for( BOARD_ITEM* item : footprint->GraphicalItems() )
  1350. {
  1351. if( item->Type() == PCB_SHAPE_T || item->Type() == PCB_TEXTBOX_T )
  1352. {
  1353. syncGraphicalItem( aWorld, static_cast<PCB_SHAPE*>( item ) );
  1354. }
  1355. else if( item->Type() == PCB_TEXT_T )
  1356. {
  1357. syncTextItem( aWorld, static_cast<PCB_TEXT*>( item ), item->GetLayer() );
  1358. }
  1359. else if( item->Type() == PCB_TABLE_T )
  1360. {
  1361. syncTextItem( aWorld, static_cast<PCB_TABLE*>( item ), item->GetLayer() );
  1362. }
  1363. }
  1364. }
  1365. for( PCB_TRACK* t : m_board->Tracks() )
  1366. {
  1367. KICAD_T type = t->Type();
  1368. if( type == PCB_TRACE_T )
  1369. {
  1370. if( std::unique_ptr<PNS::SEGMENT> segment = syncTrack( t ) )
  1371. aWorld->Add( std::move( segment ), true );
  1372. }
  1373. else if( type == PCB_ARC_T )
  1374. {
  1375. if( std::unique_ptr<PNS::ARC> arc = syncArc( static_cast<PCB_ARC*>( t ) ) )
  1376. aWorld->Add( std::move( arc ), true );
  1377. }
  1378. else if( type == PCB_VIA_T )
  1379. {
  1380. if( std::unique_ptr<PNS::VIA> via = syncVia( static_cast<PCB_VIA*>( t ) ) )
  1381. aWorld->Add( std::move( via ) );
  1382. }
  1383. }
  1384. // NB: if this were ever to become a long-lived object we would need to dirty its
  1385. // clearance cache here....
  1386. delete m_ruleResolver;
  1387. m_ruleResolver = new PNS_PCBNEW_RULE_RESOLVER( m_board, this );
  1388. aWorld->SetRuleResolver( m_ruleResolver );
  1389. aWorld->SetMaxClearance( worstClearance + m_ruleResolver->ClearanceEpsilon() );
  1390. }
  1391. void PNS_KICAD_IFACE::EraseView()
  1392. {
  1393. for( BOARD_ITEM* item : m_hiddenItems )
  1394. m_view->SetVisible( item, true );
  1395. m_hiddenItems.clear();
  1396. if( m_previewItems )
  1397. {
  1398. m_previewItems->FreeItems();
  1399. m_view->Update( m_previewItems );
  1400. }
  1401. if( m_debugDecorator )
  1402. m_debugDecorator->Clear();
  1403. }
  1404. void PNS_KICAD_IFACE_BASE::SetDebugDecorator( PNS::DEBUG_DECORATOR *aDec )
  1405. {
  1406. m_debugDecorator = aDec;
  1407. }
  1408. void PNS_KICAD_IFACE::DisplayItem( const PNS::ITEM* aItem, int aClearance, bool aEdit, int aFlags )
  1409. {
  1410. if( aItem->IsVirtual() )
  1411. return;
  1412. if( ZONE* zone = dynamic_cast<ZONE*>( aItem->Parent() ) )
  1413. {
  1414. if( zone->GetIsRuleArea() )
  1415. aFlags |= PNS_SEMI_SOLID;
  1416. }
  1417. ROUTER_PREVIEW_ITEM* pitem = new ROUTER_PREVIEW_ITEM( aItem, this, m_view, aFlags );
  1418. // Note: SEGMENT_T is used for placed tracks; LINE_T is used for the routing head
  1419. static int tracks = PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T | PNS::ITEM::LINE_T;
  1420. static int tracksOrVias = tracks | PNS::ITEM::VIA_T;
  1421. if( aClearance >= 0 )
  1422. {
  1423. pitem->SetClearance( aClearance );
  1424. auto* settings = static_cast<PCBNEW_SETTINGS*>( m_tool->GetManager()->GetSettings() );
  1425. switch( settings->m_Display.m_TrackClearance )
  1426. {
  1427. case SHOW_WITH_VIA_ALWAYS:
  1428. case SHOW_WITH_VIA_WHILE_ROUTING_OR_DRAGGING:
  1429. pitem->ShowClearance( aItem->OfKind( tracksOrVias ) );
  1430. break;
  1431. case SHOW_WITH_VIA_WHILE_ROUTING:
  1432. pitem->ShowClearance( aItem->OfKind( tracksOrVias ) && !aEdit );
  1433. break;
  1434. case SHOW_WHILE_ROUTING:
  1435. pitem->ShowClearance( aItem->OfKind( tracks ) && !aEdit );
  1436. break;
  1437. default:
  1438. pitem->ShowClearance( false );
  1439. break;
  1440. }
  1441. }
  1442. m_previewItems->Add( pitem );
  1443. m_view->Update( m_previewItems );
  1444. }
  1445. void PNS_KICAD_IFACE::DisplayPathLine( const SHAPE_LINE_CHAIN& aLine, int aImportance )
  1446. {
  1447. ROUTER_PREVIEW_ITEM* pitem = new ROUTER_PREVIEW_ITEM( aLine, this, m_view );
  1448. pitem->SetDepth( pitem->GetOriginDepth() - ROUTER_PREVIEW_ITEM::PathOverlayDepth );
  1449. COLOR4D color;
  1450. if( aImportance >= 1 )
  1451. color = COLOR4D( 1.0, 1.0, 0.0, 0.6 );
  1452. else if( aImportance == 0 )
  1453. color = COLOR4D( 0.7, 0.7, 0.7, 0.6 );
  1454. pitem->SetColor( color );
  1455. m_previewItems->Add( pitem );
  1456. m_view->Update( m_previewItems );
  1457. }
  1458. void PNS_KICAD_IFACE::DisplayRatline( const SHAPE_LINE_CHAIN& aRatline, PNS::NET_HANDLE aNet )
  1459. {
  1460. ROUTER_PREVIEW_ITEM* pitem = new ROUTER_PREVIEW_ITEM( aRatline, this, m_view );
  1461. KIGFX::RENDER_SETTINGS* renderSettings = m_view->GetPainter()->GetSettings();
  1462. KIGFX::PCB_RENDER_SETTINGS* rs = static_cast<KIGFX::PCB_RENDER_SETTINGS*>( renderSettings );
  1463. bool colorByNet = rs->GetNetColorMode() != NET_COLOR_MODE::OFF;
  1464. COLOR4D defaultColor = rs->GetColor( nullptr, LAYER_RATSNEST );
  1465. COLOR4D color = defaultColor;
  1466. std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
  1467. std::set<int> highlightedNets = rs->GetHighlightNetCodes();
  1468. std::map<int, KIGFX::COLOR4D>& netColors = rs->GetNetColorMap();
  1469. int netCode = -1;
  1470. if( NETINFO_ITEM* net = static_cast<NETINFO_ITEM*>( aNet ) )
  1471. netCode = net->GetNetCode();
  1472. const NETCLASS* nc = nullptr;
  1473. const NET_SETTINGS* netSettings = connectivity->GetNetSettings();
  1474. if( connectivity->HasNetNameForNetCode( netCode ) )
  1475. {
  1476. const wxString& netName = connectivity->GetNetNameForNetCode( netCode );
  1477. if( netSettings && netSettings->HasEffectiveNetClass( netName ) )
  1478. nc = netSettings->GetCachedEffectiveNetClass( netName ).get();
  1479. }
  1480. if( colorByNet && netColors.count( netCode ) )
  1481. color = netColors.at( netCode );
  1482. else if( colorByNet && nc && nc->HasPcbColor() )
  1483. color = nc->GetPcbColor();
  1484. else
  1485. color = defaultColor;
  1486. if( color == COLOR4D::UNSPECIFIED )
  1487. color = defaultColor;
  1488. pitem->SetColor( color.Brightened( 0.5 ).WithAlpha( std::min( 1.0, color.a + 0.4 ) ) );
  1489. m_previewItems->Add( pitem );
  1490. m_view->Update( m_previewItems );
  1491. }
  1492. void PNS_KICAD_IFACE::HideItem( PNS::ITEM* aItem )
  1493. {
  1494. BOARD_ITEM* parent = aItem->Parent();
  1495. if( parent )
  1496. {
  1497. if( m_view->IsVisible( parent ) )
  1498. m_hiddenItems.insert( parent );
  1499. m_view->SetVisible( parent, false );
  1500. m_view->Update( parent, KIGFX::APPEARANCE );
  1501. for( ZONE* td : m_board->Zones() )
  1502. {
  1503. if( td->IsTeardropArea()
  1504. && td->GetBoundingBox().Intersects( aItem->Parent()->GetBoundingBox() )
  1505. && td->Outline()->Collide( aItem->Shape( td->GetLayer() ) ) )
  1506. {
  1507. m_view->SetVisible( td, false );
  1508. m_view->Update( td, KIGFX::APPEARANCE );
  1509. }
  1510. }
  1511. }
  1512. }
  1513. void PNS_KICAD_IFACE_BASE::RemoveItem( PNS::ITEM* aItem )
  1514. {
  1515. }
  1516. void PNS_KICAD_IFACE::RemoveItem( PNS::ITEM* aItem )
  1517. {
  1518. BOARD_ITEM* parent = aItem->Parent();
  1519. if( aItem->OfKind( PNS::ITEM::SOLID_T ) && parent->Type() == PCB_PAD_T )
  1520. {
  1521. PAD* pad = static_cast<PAD*>( parent );
  1522. VECTOR2I pos = static_cast<PNS::SOLID*>( aItem )->Pos();
  1523. m_fpOffsets[ pad ].p_old = pos;
  1524. return;
  1525. }
  1526. if( parent )
  1527. {
  1528. m_commit->Remove( parent );
  1529. }
  1530. }
  1531. void PNS_KICAD_IFACE_BASE::UpdateItem( PNS::ITEM* aItem )
  1532. {
  1533. }
  1534. void PNS_KICAD_IFACE::modifyBoardItem( PNS::ITEM* aItem )
  1535. {
  1536. BOARD_ITEM* board_item = aItem->Parent();
  1537. switch( aItem->Kind() )
  1538. {
  1539. case PNS::ITEM::ARC_T:
  1540. {
  1541. PNS::ARC* arc = static_cast<PNS::ARC*>( aItem );
  1542. PCB_ARC* arc_board = static_cast<PCB_ARC*>( board_item );
  1543. const SHAPE_ARC* arc_shape = static_cast<const SHAPE_ARC*>( arc->Shape( -1 ) );
  1544. m_commit->Modify( arc_board );
  1545. arc_board->SetStart( VECTOR2I( arc_shape->GetP0() ) );
  1546. arc_board->SetEnd( VECTOR2I( arc_shape->GetP1() ) );
  1547. arc_board->SetMid( VECTOR2I( arc_shape->GetArcMid() ) );
  1548. arc_board->SetWidth( arc->Width() );
  1549. break;
  1550. }
  1551. case PNS::ITEM::SEGMENT_T:
  1552. {
  1553. PNS::SEGMENT* seg = static_cast<PNS::SEGMENT*>( aItem );
  1554. PCB_TRACK* track = static_cast<PCB_TRACK*>( board_item );
  1555. const SEG& s = seg->Seg();
  1556. m_commit->Modify( track );
  1557. track->SetStart( VECTOR2I( s.A.x, s.A.y ) );
  1558. track->SetEnd( VECTOR2I( s.B.x, s.B.y ) );
  1559. track->SetWidth( seg->Width() );
  1560. break;
  1561. }
  1562. case PNS::ITEM::VIA_T:
  1563. {
  1564. PCB_VIA* via_board = static_cast<PCB_VIA*>( board_item );
  1565. PNS::VIA* via = static_cast<PNS::VIA*>( aItem );
  1566. m_commit->Modify( via_board );
  1567. via_board->SetPosition( VECTOR2I( via->Pos().x, via->Pos().y ) );
  1568. via_board->SetWidth( PADSTACK::ALL_LAYERS, via->Diameter( 0 ) );
  1569. via_board->SetDrill( via->Drill() );
  1570. via_board->SetNet( static_cast<NETINFO_ITEM*>( via->Net() ) );
  1571. via_board->SetViaType( via->ViaType() ); // MUST be before SetLayerPair()
  1572. via_board->SetIsFree( via->IsFree() );
  1573. via_board->SetLayerPair( GetBoardLayerFromPNSLayer( via->Layers().Start() ),
  1574. GetBoardLayerFromPNSLayer( via->Layers().End() ) );
  1575. break;
  1576. }
  1577. case PNS::ITEM::SOLID_T:
  1578. {
  1579. if( aItem->Parent()->Type() == PCB_PAD_T )
  1580. {
  1581. PAD* pad = static_cast<PAD*>( aItem->Parent() );
  1582. VECTOR2I pos = static_cast<PNS::SOLID*>( aItem )->Pos();
  1583. // Don't add to commit; we'll add the parent footprints when processing the m_fpOffsets
  1584. m_fpOffsets[pad].p_old = pad->GetPosition();
  1585. m_fpOffsets[pad].p_new = pos;
  1586. }
  1587. break;
  1588. }
  1589. default:
  1590. m_commit->Modify( aItem->Parent() );
  1591. break;
  1592. }
  1593. }
  1594. void PNS_KICAD_IFACE::UpdateItem( PNS::ITEM* aItem )
  1595. {
  1596. modifyBoardItem( aItem );
  1597. }
  1598. void PNS_KICAD_IFACE_BASE::AddItem( PNS::ITEM* aItem )
  1599. {
  1600. }
  1601. BOARD_CONNECTED_ITEM* PNS_KICAD_IFACE::createBoardItem( PNS::ITEM* aItem )
  1602. {
  1603. BOARD_CONNECTED_ITEM* newBoardItem = nullptr;
  1604. NETINFO_ITEM* net = static_cast<NETINFO_ITEM*>( aItem->Net() );
  1605. if( !net )
  1606. net = NETINFO_LIST::OrphanedItem();
  1607. switch( aItem->Kind() )
  1608. {
  1609. case PNS::ITEM::ARC_T:
  1610. {
  1611. PNS::ARC* arc = static_cast<PNS::ARC*>( aItem );
  1612. PCB_ARC* new_arc = new PCB_ARC( m_board, static_cast<const SHAPE_ARC*>( arc->Shape( -1 ) ) );
  1613. new_arc->SetWidth( arc->Width() );
  1614. new_arc->SetLayer( GetBoardLayerFromPNSLayer( arc->Layers().Start() ) );
  1615. new_arc->SetNet( net );
  1616. if( aItem->GetSourceItem() && aItem->GetSourceItem()->IsType( { PCB_TRACE_T, PCB_ARC_T } ) )
  1617. {
  1618. PCB_TRACK* sourceTrack = static_cast<PCB_TRACK*>( aItem->GetSourceItem() );
  1619. new_arc->SetHasSolderMask( sourceTrack->HasSolderMask() );
  1620. new_arc->SetLocalSolderMaskMargin( sourceTrack->GetLocalSolderMaskMargin() );
  1621. }
  1622. newBoardItem = new_arc;
  1623. break;
  1624. }
  1625. case PNS::ITEM::SEGMENT_T:
  1626. {
  1627. PNS::SEGMENT* seg = static_cast<PNS::SEGMENT*>( aItem );
  1628. PCB_TRACK* track = new PCB_TRACK( m_board );
  1629. const SEG& s = seg->Seg();
  1630. track->SetStart( VECTOR2I( s.A.x, s.A.y ) );
  1631. track->SetEnd( VECTOR2I( s.B.x, s.B.y ) );
  1632. track->SetWidth( seg->Width() );
  1633. track->SetLayer( GetBoardLayerFromPNSLayer( seg->Layers().Start() ) );
  1634. track->SetNet( net );
  1635. if( aItem->GetSourceItem() && aItem->GetSourceItem()->IsType( { PCB_TRACE_T, PCB_ARC_T } ) )
  1636. {
  1637. PCB_TRACK* sourceTrack = static_cast<PCB_TRACK*>( aItem->GetSourceItem() );
  1638. track->SetHasSolderMask( sourceTrack->HasSolderMask() );
  1639. track->SetLocalSolderMaskMargin( sourceTrack->GetLocalSolderMaskMargin() );
  1640. }
  1641. newBoardItem = track;
  1642. break;
  1643. }
  1644. case PNS::ITEM::VIA_T:
  1645. {
  1646. PCB_VIA* via_board = new PCB_VIA( m_board );
  1647. PNS::VIA* via = static_cast<PNS::VIA*>( aItem );
  1648. via_board->SetPosition( VECTOR2I( via->Pos().x, via->Pos().y ) );
  1649. via_board->SetWidth( PADSTACK::ALL_LAYERS, via->Diameter( 0 ) );
  1650. via_board->SetDrill( via->Drill() );
  1651. via_board->SetNet( net );
  1652. via_board->SetViaType( via->ViaType() ); // MUST be before SetLayerPair()
  1653. via_board->SetIsFree( via->IsFree() );
  1654. via_board->SetLayerPair( GetBoardLayerFromPNSLayer( via->Layers().Start() ),
  1655. GetBoardLayerFromPNSLayer( via->Layers().End() ) );
  1656. if( aItem->GetSourceItem() && aItem->GetSourceItem()->Type() == PCB_VIA_T )
  1657. {
  1658. PCB_VIA* sourceVia = static_cast<PCB_VIA*>( aItem->GetSourceItem() );
  1659. via_board->SetFrontTentingMode( sourceVia->GetFrontTentingMode() );
  1660. via_board->SetBackTentingMode( sourceVia->GetBackTentingMode() );
  1661. }
  1662. newBoardItem = via_board;
  1663. break;
  1664. }
  1665. case PNS::ITEM::SOLID_T:
  1666. {
  1667. PAD* pad = static_cast<PAD*>( aItem->Parent() );
  1668. VECTOR2I pos = static_cast<PNS::SOLID*>( aItem )->Pos();
  1669. m_fpOffsets[pad].p_new = pos;
  1670. return nullptr;
  1671. }
  1672. default:
  1673. return nullptr;
  1674. }
  1675. if( net->GetNetCode() <= 0 )
  1676. {
  1677. NETINFO_ITEM* newNetInfo = newBoardItem->GetNet();
  1678. newNetInfo->SetParent( m_board );
  1679. newNetInfo->SetNetClass( m_board->GetDesignSettings().m_NetSettings->GetDefaultNetclass() );
  1680. }
  1681. return newBoardItem;
  1682. }
  1683. void PNS_KICAD_IFACE::AddItem( PNS::ITEM* aItem )
  1684. {
  1685. BOARD_CONNECTED_ITEM* boardItem = createBoardItem( aItem );
  1686. if( boardItem )
  1687. {
  1688. aItem->SetParent( boardItem );
  1689. boardItem->ClearFlags();
  1690. m_commit->Add( boardItem );
  1691. }
  1692. }
  1693. void PNS_KICAD_IFACE::Commit()
  1694. {
  1695. std::set<FOOTPRINT*> processedFootprints;
  1696. EraseView();
  1697. for( const auto& [ pad, fpOffset ] : m_fpOffsets )
  1698. {
  1699. VECTOR2I offset = fpOffset.p_new - fpOffset.p_old;
  1700. FOOTPRINT* footprint = pad->GetParentFootprint();
  1701. VECTOR2I p_orig = footprint->GetPosition();
  1702. VECTOR2I p_new = p_orig + offset;
  1703. if( processedFootprints.find( footprint ) != processedFootprints.end() )
  1704. continue;
  1705. processedFootprints.insert( footprint );
  1706. m_commit->Modify( footprint );
  1707. footprint->SetPosition( p_new );
  1708. }
  1709. m_fpOffsets.clear();
  1710. m_commit->Push( _( "Routing" ), m_commitFlags );
  1711. m_commit = std::make_unique<BOARD_COMMIT>( m_tool );
  1712. }
  1713. EDA_UNITS PNS_KICAD_IFACE::GetUnits() const
  1714. {
  1715. return static_cast<EDA_UNITS>( m_tool->GetManager()->GetSettings()->m_System.units );
  1716. }
  1717. void PNS_KICAD_IFACE::SetView( KIGFX::VIEW* aView )
  1718. {
  1719. wxLogTrace( wxT( "PNS" ), wxT( "SetView %p" ), aView );
  1720. if( m_previewItems )
  1721. {
  1722. m_previewItems->FreeItems();
  1723. delete m_previewItems;
  1724. }
  1725. m_view = aView;
  1726. m_previewItems = new KIGFX::VIEW_GROUP( m_view );
  1727. m_previewItems->SetLayer( LAYER_SELECT_OVERLAY ) ;
  1728. if(m_view)
  1729. m_view->Add( m_previewItems );
  1730. delete m_debugDecorator;
  1731. auto dec = new PNS_PCBNEW_DEBUG_DECORATOR( this );
  1732. m_debugDecorator = dec;
  1733. dec->SetDebugEnabled( ADVANCED_CFG::GetCfg().m_ShowRouterDebugGraphics );
  1734. if( ADVANCED_CFG::GetCfg().m_ShowRouterDebugGraphics )
  1735. dec->SetView( m_view );
  1736. }
  1737. int PNS_KICAD_IFACE::GetNetCode( PNS::NET_HANDLE aNet ) const
  1738. {
  1739. if( aNet )
  1740. return static_cast<NETINFO_ITEM*>( aNet )->GetNetCode();
  1741. else
  1742. return -1;
  1743. }
  1744. wxString PNS_KICAD_IFACE::GetNetName( PNS::NET_HANDLE aNet ) const
  1745. {
  1746. if( aNet )
  1747. return static_cast<NETINFO_ITEM*>( aNet )->GetNetname();
  1748. else
  1749. return wxEmptyString;
  1750. }
  1751. void PNS_KICAD_IFACE::UpdateNet( PNS::NET_HANDLE aNet )
  1752. {
  1753. wxLogTrace( wxT( "PNS" ), wxT( "Update-net %s" ), GetNetName( aNet ) );
  1754. }
  1755. PNS::NET_HANDLE PNS_KICAD_IFACE_BASE::GetOrphanedNetHandle()
  1756. {
  1757. return NETINFO_LIST::OrphanedItem();
  1758. }
  1759. PNS::RULE_RESOLVER* PNS_KICAD_IFACE_BASE::GetRuleResolver()
  1760. {
  1761. return m_ruleResolver;
  1762. }
  1763. void PNS_KICAD_IFACE::SetHostTool( PCB_TOOL_BASE* aTool )
  1764. {
  1765. m_tool = aTool;
  1766. m_commit = std::make_unique<BOARD_COMMIT>( m_tool );
  1767. }
  1768. PCB_LAYER_ID PNS_KICAD_IFACE_BASE::GetBoardLayerFromPNSLayer( int aLayer ) const
  1769. {
  1770. if( aLayer < 0 )
  1771. return PCB_LAYER_ID::UNDEFINED_LAYER;
  1772. if( aLayer == 0 )
  1773. return F_Cu;
  1774. if( aLayer == m_board->GetCopperLayerCount() - 1 )
  1775. return B_Cu;
  1776. return static_cast<PCB_LAYER_ID>( ( aLayer + 1 ) * 2 );
  1777. }
  1778. int PNS_KICAD_IFACE_BASE::GetPNSLayerFromBoardLayer( PCB_LAYER_ID aLayer ) const
  1779. {
  1780. if( aLayer < 0 )
  1781. return -1;
  1782. if( aLayer == F_Cu )
  1783. return 0;
  1784. if( aLayer == B_Cu )
  1785. return m_board->GetCopperLayerCount() - 1;
  1786. return ( aLayer / 2 ) - 1;
  1787. }
  1788. void PNS_KICAD_IFACE_BASE::SetStartLayerFromPCBNew( PCB_LAYER_ID aLayer )
  1789. {
  1790. m_startLayer = GetPNSLayerFromBoardLayer( aLayer );
  1791. }
  1792. PNS_LAYER_RANGE PNS_KICAD_IFACE_BASE::SetLayersFromPCBNew( PCB_LAYER_ID aStartLayer, PCB_LAYER_ID aEndLayer )
  1793. {
  1794. return PNS_LAYER_RANGE( GetPNSLayerFromBoardLayer( aStartLayer ),
  1795. GetPNSLayerFromBoardLayer( aEndLayer ) );
  1796. }
  1797. long long int PNS_KICAD_IFACE_BASE::CalculateRoutedPathLength( const PNS::ITEM_SET& aLine, const PNS::SOLID* aStartPad,
  1798. const PNS::SOLID* aEndPad )
  1799. {
  1800. std::vector<LENGTH_CALCULATION_ITEM> lengthItems;
  1801. for( int idx = 0; idx < aLine.Size(); idx++ )
  1802. {
  1803. const PNS::ITEM* lineItem = aLine[idx];
  1804. if( const PNS::LINE* l = dyn_cast<const PNS::LINE*>( lineItem ) )
  1805. {
  1806. LENGTH_CALCULATION_ITEM item;
  1807. item.SetLine( l->CLine() );
  1808. const PCB_LAYER_ID layer = GetBoardLayerFromPNSLayer( lineItem->Layer() );
  1809. item.SetLayers( layer );
  1810. lengthItems.emplace_back( std::move( item ) );
  1811. }
  1812. else if( lineItem->OfKind( PNS::ITEM::VIA_T ) && idx > 0 && idx < aLine.Size() - 1 )
  1813. {
  1814. const int layerPrev = aLine[idx - 1]->Layer();
  1815. const int layerNext = aLine[idx + 1]->Layer();
  1816. const PCB_LAYER_ID pcbLayerPrev = GetBoardLayerFromPNSLayer( layerPrev );
  1817. const PCB_LAYER_ID pcbLayerNext = GetBoardLayerFromPNSLayer( layerNext );
  1818. if( layerPrev != layerNext )
  1819. {
  1820. LENGTH_CALCULATION_ITEM item;
  1821. item.SetVia( static_cast<PCB_VIA*>( lineItem->GetSourceItem() ) );
  1822. item.SetLayers( pcbLayerPrev, pcbLayerNext );
  1823. lengthItems.emplace_back( std::move( item ) );
  1824. }
  1825. }
  1826. }
  1827. const PAD* startPad = nullptr;
  1828. const PAD* endPad = nullptr;
  1829. if( aStartPad )
  1830. startPad = static_cast<PAD*>( aStartPad->Parent() );
  1831. if( aEndPad )
  1832. endPad = static_cast<PAD*>( aEndPad->Parent() );
  1833. constexpr PATH_OPTIMISATIONS opts = {
  1834. .OptimiseViaLayers = false, .MergeTracks = false, .OptimiseTracesInPads = false, .InferViaInPad = true
  1835. };
  1836. const BOARD* board = GetBoard();
  1837. return board->GetLengthCalculation()->CalculateLength( lengthItems, opts, startPad, endPad );
  1838. }