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.

2056 lines
60 KiB

8 years ago
9 years ago
9 years ago
4 years ago
5 years 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
9 years ago
5 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
5 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 (C) 2016-2023 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 <pad.h>
  27. #include <pcb_track.h>
  28. #include <zone.h>
  29. #include <pcb_shape.h>
  30. #include <pcb_text.h>
  31. #include <board_commit.h>
  32. #include <layer_ids.h>
  33. #include <geometry/convex_hull.h>
  34. #include <confirm.h>
  35. #include <tools/pcb_tool_base.h>
  36. #include <tool/tool_manager.h>
  37. #include <settings/app_settings.h>
  38. #include <pcb_painter.h>
  39. #include <geometry/shape.h>
  40. #include <geometry/shape_line_chain.h>
  41. #include <geometry/shape_arc.h>
  42. #include <geometry/shape_simple.h>
  43. #include <drc/drc_rule.h>
  44. #include <drc/drc_engine.h>
  45. #include <connectivity/connectivity_data.h>
  46. #include <wx/log.h>
  47. #include <memory>
  48. #include <advanced_config.h>
  49. #include <pcbnew_settings.h>
  50. #include <macros.h>
  51. #include "pns_kicad_iface.h"
  52. #include "pns_arc.h"
  53. #include "pns_sizes_settings.h"
  54. #include "pns_item.h"
  55. #include "pns_line.h"
  56. #include "pns_solid.h"
  57. #include "pns_segment.h"
  58. #include "pns_node.h"
  59. #include "pns_router.h"
  60. #include "pns_debug_decorator.h"
  61. #include "router_preview_item.h"
  62. typedef VECTOR2I::extended_type ecoord;
  63. struct CLEARANCE_CACHE_KEY
  64. {
  65. const PNS::ITEM* A;
  66. const PNS::ITEM* B;
  67. bool Flag;
  68. bool operator==(const CLEARANCE_CACHE_KEY& other) const
  69. {
  70. return A == other.A && B == other.B && Flag == other.Flag;
  71. }
  72. };
  73. namespace std
  74. {
  75. template <>
  76. struct hash<CLEARANCE_CACHE_KEY>
  77. {
  78. std::size_t operator()( const CLEARANCE_CACHE_KEY& k ) const
  79. {
  80. return hash<const void*>()( k.A ) ^ hash<const void*>()( k.B ) ^ hash<int>()( k.Flag );
  81. }
  82. };
  83. }
  84. class PNS_PCBNEW_RULE_RESOLVER : public PNS::RULE_RESOLVER
  85. {
  86. public:
  87. PNS_PCBNEW_RULE_RESOLVER( BOARD* aBoard, PNS::ROUTER_IFACE* aRouterIface );
  88. virtual ~PNS_PCBNEW_RULE_RESOLVER();
  89. virtual int Clearance( const PNS::ITEM* aA, const PNS::ITEM* aB,
  90. bool aUseClearanceEpsilon = true ) override;
  91. virtual PNS::NET_HANDLE DpCoupledNet( PNS::NET_HANDLE aNet ) override;
  92. virtual int DpNetPolarity( PNS::NET_HANDLE aNet ) override;
  93. virtual bool DpNetPair( const PNS::ITEM* aItem, PNS::NET_HANDLE& aNetP,
  94. PNS::NET_HANDLE& aNetN ) override;
  95. virtual int NetCode( PNS::NET_HANDLE aNet ) override;
  96. virtual wxString NetName( PNS::NET_HANDLE aNet ) override;
  97. virtual bool IsInNetTie( const PNS::ITEM* aA ) override;
  98. virtual bool IsNetTieExclusion( const PNS::ITEM* aItem, const VECTOR2I& aCollisionPos,
  99. const PNS::ITEM* aCollidingItem ) override;
  100. virtual bool IsKeepout( const PNS::ITEM* aA, const PNS::ITEM* aB ) override;
  101. virtual bool QueryConstraint( PNS::CONSTRAINT_TYPE aType, const PNS::ITEM* aItemA,
  102. const PNS::ITEM* aItemB, int aLayer,
  103. PNS::CONSTRAINT* aConstraint ) override;
  104. int ClearanceEpsilon() const override { return m_clearanceEpsilon; }
  105. void ClearCacheForItems( std::vector<const PNS::ITEM*>& aItems ) override;
  106. void ClearCaches() override;
  107. private:
  108. /**
  109. * Checks for netnamed differential pairs.
  110. * This accepts nets named suffixed by 'P', 'N', '+', '-', as well as additional
  111. * numbers and underscores following the suffix. So NET_P_123 is a valid positive net
  112. * name matched to NET_N_123.
  113. * @param aNetName Input net name to check for DP naming
  114. * @param aComplementNet Generated net name for the pair
  115. * @return -1 if found the negative pair, +1 if found the positive pair, 0 otherwise
  116. */
  117. int matchDpSuffix( const wxString& aNetName, wxString& aComplementNet );
  118. private:
  119. PNS::ROUTER_IFACE* m_routerIface;
  120. BOARD* m_board;
  121. PCB_TRACK m_dummyTracks[2];
  122. PCB_ARC m_dummyArcs[2];
  123. PCB_VIA m_dummyVias[2];
  124. int m_clearanceEpsilon;
  125. std::unordered_map<CLEARANCE_CACHE_KEY, int> m_clearanceCache;
  126. };
  127. PNS_PCBNEW_RULE_RESOLVER::PNS_PCBNEW_RULE_RESOLVER( BOARD* aBoard,
  128. PNS::ROUTER_IFACE* aRouterIface ) :
  129. m_routerIface( aRouterIface ),
  130. m_board( aBoard ),
  131. m_dummyTracks{ { aBoard }, { aBoard } },
  132. m_dummyArcs{ { aBoard }, { aBoard } },
  133. m_dummyVias{ { aBoard }, { aBoard } }
  134. {
  135. for( PCB_TRACK& track : m_dummyTracks )
  136. track.SetFlags( ROUTER_TRANSIENT );
  137. for( PCB_ARC& arc : m_dummyArcs )
  138. arc.SetFlags( ROUTER_TRANSIENT );
  139. for ( PCB_VIA& via : m_dummyVias )
  140. via.SetFlags( ROUTER_TRANSIENT );
  141. if( aBoard )
  142. m_clearanceEpsilon = aBoard->GetDesignSettings().GetDRCEpsilon();
  143. else
  144. m_clearanceEpsilon = 0;
  145. }
  146. PNS_PCBNEW_RULE_RESOLVER::~PNS_PCBNEW_RULE_RESOLVER()
  147. {
  148. }
  149. bool PNS_PCBNEW_RULE_RESOLVER::IsInNetTie( const PNS::ITEM* aA )
  150. {
  151. BOARD_ITEM* item = aA->BoardItem();
  152. return item && item->GetParentFootprint() && item->GetParentFootprint()->IsNetTie();
  153. }
  154. bool PNS_PCBNEW_RULE_RESOLVER::IsNetTieExclusion( const PNS::ITEM* aItem,
  155. const VECTOR2I& aCollisionPos,
  156. const PNS::ITEM* aCollidingItem )
  157. {
  158. wxCHECK( aItem && aCollidingItem, false );
  159. std::shared_ptr<DRC_ENGINE> drcEngine = m_board->GetDesignSettings().m_DRCEngine;
  160. BOARD_ITEM* item = aItem->BoardItem();
  161. BOARD_ITEM* collidingItem = aCollidingItem->BoardItem();
  162. FOOTPRINT* collidingFp = collidingItem->GetParentFootprint();
  163. FOOTPRINT* itemFp = item ? item->GetParentFootprint() : nullptr;
  164. if( collidingFp && itemFp && ( collidingFp == itemFp ) && itemFp->IsNetTie() )
  165. {
  166. // Two items colliding from the same net tie footprint are not checked
  167. return true;
  168. }
  169. if( drcEngine )
  170. {
  171. return drcEngine->IsNetTieExclusion( NetCode( aItem->Net() ), ToLAYER_ID( aItem->Layer() ),
  172. aCollisionPos, collidingItem );
  173. }
  174. return false;
  175. }
  176. bool PNS_PCBNEW_RULE_RESOLVER::IsKeepout( const PNS::ITEM* aA, const PNS::ITEM* aB )
  177. {
  178. auto checkKeepout =
  179. []( const ZONE* aKeepout, const BOARD_ITEM* aOther )
  180. {
  181. if( aKeepout->GetDoNotAllowTracks() && aOther->IsType( { PCB_ARC_T, PCB_TRACE_T } ) )
  182. return true;
  183. if( aKeepout->GetDoNotAllowVias() && aOther->Type() == PCB_VIA_T )
  184. return true;
  185. if( aKeepout->GetDoNotAllowPads() && aOther->Type() == PCB_PAD_T )
  186. return true;
  187. // Incomplete test, but better than nothing:
  188. if( aKeepout->GetDoNotAllowFootprints() && aOther->Type() == PCB_PAD_T )
  189. {
  190. return !aKeepout->GetParentFootprint()
  191. || aKeepout->GetParentFootprint() != aOther->GetParentFootprint();
  192. }
  193. return false;
  194. };
  195. if( aA->Parent() && aA->Parent()->Type() == PCB_ZONE_T )
  196. {
  197. const ZONE* zoneA = static_cast<ZONE*>( aA->Parent() );
  198. if( zoneA->GetIsRuleArea() && aB->Parent() )
  199. return checkKeepout( zoneA, aB->Parent() );
  200. }
  201. if( aB->Parent() && aB->Parent()->Type() == PCB_ZONE_T )
  202. {
  203. const ZONE* zoneB = static_cast<ZONE*>( aB->Parent() );
  204. if( zoneB->GetIsRuleArea() && aA->Parent() )
  205. return checkKeepout( zoneB, aA->Parent() );
  206. }
  207. return false;
  208. }
  209. static bool isCopper( const PNS::ITEM* aItem )
  210. {
  211. if ( !aItem )
  212. return false;
  213. const BOARD_ITEM *parent = aItem->Parent();
  214. return !parent || parent->IsOnCopperLayer();
  215. }
  216. static bool isHole( const PNS::ITEM* aItem )
  217. {
  218. if ( !aItem )
  219. return false;
  220. return aItem->OfKind( PNS::ITEM::HOLE_T );
  221. }
  222. static bool isDrilledHole( const PNS::ITEM* aItem )
  223. {
  224. if( !isHole( aItem ) )
  225. return false;
  226. if( PAD* pad = dynamic_cast<PAD*>( aItem->Parent() ) )
  227. return pad->GetDrillSizeX() && pad->GetDrillSizeX() == pad->GetDrillSizeY();
  228. // Via holes are (currently) always round
  229. return true;
  230. }
  231. static bool isNonPlatedSlot( const PNS::ITEM* aItem )
  232. {
  233. if( !isHole( aItem ) )
  234. return false;
  235. if( PAD* pad = dynamic_cast<PAD*>( aItem->Parent() ) )
  236. return pad->GetAttribute() == PAD_ATTRIB::NPTH && pad->GetDrillSizeX() != pad->GetDrillSizeY();
  237. // Via holes are (currently) always round
  238. return false;
  239. }
  240. static bool isEdge( const PNS::ITEM* aItem )
  241. {
  242. if ( !aItem )
  243. return false;
  244. const PCB_SHAPE *parent = dynamic_cast<PCB_SHAPE*>( aItem->BoardItem() );
  245. return parent && ( parent->IsOnLayer( Edge_Cuts ) || parent->IsOnLayer( Margin ) );
  246. }
  247. bool PNS_PCBNEW_RULE_RESOLVER::QueryConstraint( PNS::CONSTRAINT_TYPE aType,
  248. const PNS::ITEM* aItemA, const PNS::ITEM* aItemB,
  249. int aLayer, PNS::CONSTRAINT* aConstraint )
  250. {
  251. std::shared_ptr<DRC_ENGINE> drcEngine = m_board->GetDesignSettings().m_DRCEngine;
  252. if( !drcEngine )
  253. return false;
  254. DRC_CONSTRAINT_T hostType;
  255. switch ( aType )
  256. {
  257. case PNS::CONSTRAINT_TYPE::CT_CLEARANCE: hostType = CLEARANCE_CONSTRAINT; break;
  258. case PNS::CONSTRAINT_TYPE::CT_WIDTH: hostType = TRACK_WIDTH_CONSTRAINT; break;
  259. case PNS::CONSTRAINT_TYPE::CT_DIFF_PAIR_GAP: hostType = DIFF_PAIR_GAP_CONSTRAINT; break;
  260. case PNS::CONSTRAINT_TYPE::CT_LENGTH: hostType = LENGTH_CONSTRAINT; break;
  261. case PNS::CONSTRAINT_TYPE::CT_VIA_DIAMETER: hostType = VIA_DIAMETER_CONSTRAINT; break;
  262. case PNS::CONSTRAINT_TYPE::CT_VIA_HOLE: hostType = HOLE_SIZE_CONSTRAINT; break;
  263. case PNS::CONSTRAINT_TYPE::CT_HOLE_CLEARANCE: hostType = HOLE_CLEARANCE_CONSTRAINT; break;
  264. case PNS::CONSTRAINT_TYPE::CT_EDGE_CLEARANCE: hostType = EDGE_CLEARANCE_CONSTRAINT; break;
  265. case PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE: hostType = HOLE_TO_HOLE_CONSTRAINT; break;
  266. default: return false; // should not happen
  267. }
  268. BOARD_ITEM* parentA = aItemA ? aItemA->BoardItem() : nullptr;
  269. BOARD_ITEM* parentB = aItemB ? aItemB->BoardItem() : nullptr;
  270. DRC_CONSTRAINT hostConstraint;
  271. // A track being routed may not have a BOARD_ITEM associated yet.
  272. if( aItemA && !parentA )
  273. {
  274. switch( aItemA->Kind() )
  275. {
  276. case PNS::ITEM::ARC_T:
  277. m_dummyArcs[0].SetLayer( ToLAYER_ID( aLayer ) );
  278. m_dummyArcs[0].SetNet( static_cast<NETINFO_ITEM*>( aItemA->Net() ) );
  279. m_dummyArcs[0].SetStart( aItemA->Anchor( 0 ) );
  280. m_dummyArcs[0].SetEnd( aItemA->Anchor( 1 ) );
  281. parentA = &m_dummyArcs[0];
  282. break;
  283. case PNS::ITEM::VIA_T:
  284. case PNS::ITEM::HOLE_T:
  285. m_dummyVias[0].SetLayer( ToLAYER_ID( aLayer ) );
  286. m_dummyVias[0].SetNet( static_cast<NETINFO_ITEM*>( aItemA->Net() ) );
  287. m_dummyVias[0].SetStart( aItemA->Anchor( 0 ) );
  288. parentA = &m_dummyVias[0];
  289. break;
  290. case PNS::ITEM::SEGMENT_T:
  291. case PNS::ITEM::LINE_T:
  292. m_dummyTracks[0].SetLayer( ToLAYER_ID( aLayer ) );
  293. m_dummyTracks[0].SetNet( static_cast<NETINFO_ITEM*>( aItemA->Net() ) );
  294. m_dummyTracks[0].SetStart( aItemA->Anchor( 0 ) );
  295. m_dummyTracks[0].SetEnd( aItemA->Anchor( 1 ) );
  296. parentA = &m_dummyTracks[0];
  297. break;
  298. default:
  299. break;
  300. }
  301. }
  302. if( aItemB && !parentB )
  303. {
  304. switch( aItemB->Kind() )
  305. {
  306. case PNS::ITEM::ARC_T:
  307. m_dummyArcs[1].SetLayer( ToLAYER_ID( aLayer ) );
  308. m_dummyArcs[1].SetNet( static_cast<NETINFO_ITEM*>( aItemB->Net() ) );
  309. m_dummyArcs[1].SetStart( aItemB->Anchor( 0 ) );
  310. m_dummyArcs[1].SetEnd( aItemB->Anchor( 1 ) );
  311. parentB = &m_dummyArcs[1];
  312. break;
  313. case PNS::ITEM::VIA_T:
  314. case PNS::ITEM::HOLE_T:
  315. m_dummyVias[1].SetLayer( ToLAYER_ID( aLayer ) );
  316. m_dummyVias[1].SetNet( static_cast<NETINFO_ITEM*>( aItemB->Net() ) );
  317. m_dummyVias[1].SetStart( aItemB->Anchor( 0 ) );
  318. parentB = &m_dummyVias[1];
  319. break;
  320. case PNS::ITEM::SEGMENT_T:
  321. case PNS::ITEM::LINE_T:
  322. m_dummyTracks[1].SetLayer( ToLAYER_ID( aLayer ) );
  323. m_dummyTracks[1].SetNet( static_cast<NETINFO_ITEM*>( aItemB->Net() ) );
  324. m_dummyTracks[1].SetStart( aItemB->Anchor( 0 ) );
  325. m_dummyTracks[1].SetEnd( aItemB->Anchor( 1 ) );
  326. parentB = &m_dummyTracks[1];
  327. break;
  328. default:
  329. break;
  330. }
  331. }
  332. if( parentA )
  333. hostConstraint = drcEngine->EvalRules( hostType, parentA, parentB, ToLAYER_ID( aLayer ) );
  334. if( hostConstraint.IsNull() )
  335. return false;
  336. if( hostConstraint.GetSeverity() == RPT_SEVERITY_IGNORE )
  337. {
  338. aConstraint->m_Value.SetMin( -1 );
  339. aConstraint->m_RuleName = hostConstraint.GetName();
  340. aConstraint->m_Type = aType;
  341. return true;
  342. }
  343. switch ( aType )
  344. {
  345. case PNS::CONSTRAINT_TYPE::CT_CLEARANCE:
  346. case PNS::CONSTRAINT_TYPE::CT_WIDTH:
  347. case PNS::CONSTRAINT_TYPE::CT_DIFF_PAIR_GAP:
  348. case PNS::CONSTRAINT_TYPE::CT_VIA_DIAMETER:
  349. case PNS::CONSTRAINT_TYPE::CT_VIA_HOLE:
  350. case PNS::CONSTRAINT_TYPE::CT_HOLE_CLEARANCE:
  351. case PNS::CONSTRAINT_TYPE::CT_EDGE_CLEARANCE:
  352. case PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE:
  353. aConstraint->m_Value = hostConstraint.GetValue();
  354. aConstraint->m_RuleName = hostConstraint.GetName();
  355. aConstraint->m_Type = aType;
  356. return true;
  357. default:
  358. return false;
  359. }
  360. }
  361. void PNS_PCBNEW_RULE_RESOLVER::ClearCacheForItems( std::vector<const PNS::ITEM*>& aItems )
  362. {
  363. int n_pruned = 0;
  364. std::set<const PNS::ITEM*> remainingItems( aItems.begin(), aItems.end() );
  365. /* We need to carefully check both A and B item pointers in the cache against dirty/invalidated
  366. items in the set, as the clearance relation is commutative ( CL[a,b] == CL[b,a] ). The code
  367. below is a bit ugly, but works in O(n*log(m)) and is run once or twice during ROUTER::Move() call
  368. - so I hope it still gets better performance than no cache at all */
  369. for( auto it = m_clearanceCache.begin(); it != m_clearanceCache.end(); )
  370. {
  371. bool dirty = remainingItems.find( it->first.A ) != remainingItems.end();
  372. dirty |= remainingItems.find( it->first.B) != remainingItems.end();
  373. if( dirty )
  374. {
  375. it = m_clearanceCache.erase( it );
  376. n_pruned++;
  377. } else
  378. it++;
  379. }
  380. #if 0
  381. printf("ClearCache : n_pruned %d\n", n_pruned );
  382. #endif
  383. }
  384. void PNS_PCBNEW_RULE_RESOLVER::ClearCaches()
  385. {
  386. m_clearanceCache.clear();
  387. }
  388. int PNS_PCBNEW_RULE_RESOLVER::Clearance( const PNS::ITEM* aA, const PNS::ITEM* aB,
  389. bool aUseClearanceEpsilon )
  390. {
  391. CLEARANCE_CACHE_KEY key = { aA, aB, aUseClearanceEpsilon };
  392. auto it = m_clearanceCache.find( key );
  393. if( it != m_clearanceCache.end() )
  394. return it->second;
  395. PNS::CONSTRAINT constraint;
  396. int rv = 0;
  397. LAYER_RANGE layers;
  398. if( !aB )
  399. layers = aA->Layers();
  400. else if( isEdge( aA ) )
  401. layers = aB->Layers();
  402. else if( isEdge( aB ) )
  403. layers = aA->Layers();
  404. else
  405. layers = aA->Layers().Intersection( aB->Layers() );
  406. // Normalize layer range (no -1 magic numbers)
  407. layers = layers.Intersection( LAYER_RANGE( PCBNEW_LAYER_ID_START, PCB_LAYER_ID_COUNT - 1 ) );
  408. for( int layer = layers.Start(); layer <= layers.End(); ++layer )
  409. {
  410. if( isDrilledHole( aA ) && isDrilledHole( aB) )
  411. {
  412. if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE, aA, aB, layer, &constraint ) )
  413. {
  414. if( constraint.m_Value.Min() > rv )
  415. rv = constraint.m_Value.Min();
  416. }
  417. }
  418. else if( isHole( aA ) || isHole( aB ) )
  419. {
  420. if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_CLEARANCE, aA, aB, layer, &constraint ) )
  421. {
  422. if( constraint.m_Value.Min() > rv )
  423. rv = constraint.m_Value.Min();
  424. }
  425. }
  426. else if( isCopper( aA ) && ( !aB || isCopper( aB ) ) )
  427. {
  428. if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_CLEARANCE, aA, aB, layer, &constraint ) )
  429. {
  430. if( constraint.m_Value.Min() > rv )
  431. rv = constraint.m_Value.Min();
  432. }
  433. }
  434. // No 'else'; non-plated milled holes get both HOLE_CLEARANCE and EDGE_CLEARANCE
  435. if( isEdge( aA ) || isNonPlatedSlot( aA ) || isEdge( aB ) || isNonPlatedSlot( aB ) )
  436. {
  437. if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_EDGE_CLEARANCE, aA, aB, layer, &constraint ) )
  438. {
  439. if( constraint.m_Value.Min() > rv )
  440. rv = constraint.m_Value.Min();
  441. }
  442. }
  443. }
  444. if( aUseClearanceEpsilon && rv > 0 )
  445. rv = std::max( 0, rv - m_clearanceEpsilon );
  446. /* It makes no sense to put items that have no owning NODE in the cache - they can be allocated on stack
  447. and we can't really invalidate them in the cache when they are destroyed. Probably a better idea would be
  448. to use a static unique counter in PNS::ITEM constructor to generate the cache keys. */
  449. if( aA && aB && aA->Owner() && aB->Owner() )
  450. {
  451. m_clearanceCache[ key ] = rv;
  452. }
  453. return rv;
  454. }
  455. bool PNS_KICAD_IFACE_BASE::inheritTrackWidth( PNS::ITEM* aItem, int* aInheritedWidth )
  456. {
  457. VECTOR2I p;
  458. assert( aItem->Owner() != nullptr );
  459. auto tryGetTrackWidth =
  460. []( PNS::ITEM* aPnsItem ) -> int
  461. {
  462. switch( aPnsItem->Kind() )
  463. {
  464. case PNS::ITEM::SEGMENT_T: return static_cast<PNS::SEGMENT*>( aPnsItem )->Width();
  465. case PNS::ITEM::ARC_T: return static_cast<PNS::ARC*>( aPnsItem )->Width();
  466. default: return -1;
  467. }
  468. };
  469. int itemTrackWidth = tryGetTrackWidth( aItem );
  470. if( itemTrackWidth > 0 )
  471. {
  472. *aInheritedWidth = itemTrackWidth;
  473. return true;
  474. }
  475. switch( aItem->Kind() )
  476. {
  477. case PNS::ITEM::VIA_T:
  478. p = static_cast<PNS::VIA*>( aItem )->Pos();
  479. break;
  480. case PNS::ITEM::SOLID_T:
  481. p = static_cast<PNS::SOLID*>( aItem )->Pos();
  482. break;
  483. default:
  484. return false;
  485. }
  486. const PNS::JOINT* jt = static_cast<const PNS::NODE*>( aItem->Owner() )->FindJoint( p, aItem );
  487. assert( jt != nullptr );
  488. int mval = INT_MAX;
  489. PNS::ITEM_SET linkedSegs( jt->CLinks() );
  490. linkedSegs.ExcludeItem( aItem ).FilterKinds( PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T );
  491. for( PNS::ITEM* item : linkedSegs.Items() )
  492. {
  493. int w = tryGetTrackWidth( item );
  494. assert( w > 0 );
  495. mval = std::min( w, mval );
  496. }
  497. if( mval == INT_MAX )
  498. return false;
  499. *aInheritedWidth = mval;
  500. return true;
  501. }
  502. bool PNS_KICAD_IFACE_BASE::ImportSizes( PNS::SIZES_SETTINGS& aSizes, PNS::ITEM* aStartItem,
  503. PNS::NET_HANDLE aNet )
  504. {
  505. BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
  506. PNS::CONSTRAINT constraint;
  507. if( aStartItem && m_startLayer < 0 )
  508. m_startLayer = aStartItem->Layer();
  509. aSizes.SetClearance( bds.m_MinClearance );
  510. aSizes.SetMinClearance( bds.m_MinClearance );
  511. aSizes.SetClearanceSource( _( "board minimum clearance" ) );
  512. if( m_ruleResolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_CLEARANCE, aStartItem, nullptr,
  513. m_startLayer, &constraint ) )
  514. {
  515. if( constraint.m_Value.Min() > bds.m_MinClearance )
  516. {
  517. aSizes.SetClearance( constraint.m_Value.Min() );
  518. aSizes.SetClearanceSource( constraint.m_RuleName );
  519. }
  520. }
  521. int trackWidth = bds.m_TrackMinWidth;
  522. bool found = false;
  523. aSizes.SetWidthSource( _( "board minimum track width" ) );
  524. if( bds.m_UseConnectedTrackWidth && !bds.m_TempOverrideTrackWidth && aStartItem != nullptr )
  525. {
  526. found = inheritTrackWidth( aStartItem, &trackWidth );
  527. if( found )
  528. aSizes.SetWidthSource( _( "existing track" ) );
  529. }
  530. if( !found && bds.UseNetClassTrack() && aStartItem )
  531. {
  532. if( m_ruleResolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_WIDTH, aStartItem, nullptr,
  533. m_startLayer, &constraint ) )
  534. {
  535. trackWidth = std::max( trackWidth, constraint.m_Value.Opt() );
  536. found = true;
  537. if( trackWidth == constraint.m_Value.Opt() )
  538. aSizes.SetWidthSource( constraint.m_RuleName );
  539. }
  540. }
  541. if( !found )
  542. {
  543. trackWidth = std::max( trackWidth, bds.GetCurrentTrackWidth() );
  544. if( bds.UseNetClassTrack() )
  545. aSizes.SetWidthSource( _( "netclass 'Default'" ) );
  546. else if( trackWidth == bds.GetCurrentTrackWidth() )
  547. aSizes.SetWidthSource( _( "user choice" ) );
  548. }
  549. aSizes.SetTrackWidth( trackWidth );
  550. aSizes.SetBoardMinTrackWidth( bds.m_TrackMinWidth );
  551. aSizes.SetTrackWidthIsExplicit( !bds.m_UseConnectedTrackWidth || bds.m_TempOverrideTrackWidth );
  552. int viaDiameter = bds.m_ViasMinSize;
  553. int viaDrill = bds.m_MinThroughDrill;
  554. PNS::VIA dummyVia, coupledVia;
  555. if( aStartItem )
  556. {
  557. dummyVia.SetNet( aStartItem->Net() );
  558. coupledVia.SetNet( m_ruleResolver->DpCoupledNet( aStartItem->Net() ) );
  559. }
  560. if( bds.UseNetClassVia() && aStartItem ) // netclass value
  561. {
  562. if( m_ruleResolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_VIA_DIAMETER, &dummyVia,
  563. nullptr, m_startLayer, &constraint ) )
  564. {
  565. viaDiameter = std::max( viaDiameter, constraint.m_Value.Opt() );
  566. }
  567. if( m_ruleResolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_VIA_HOLE, &dummyVia,
  568. nullptr, m_startLayer, &constraint ) )
  569. {
  570. viaDrill = std::max( viaDrill, constraint.m_Value.Opt() );
  571. }
  572. }
  573. else
  574. {
  575. viaDiameter = bds.GetCurrentViaSize();
  576. viaDrill = bds.GetCurrentViaDrill();
  577. }
  578. aSizes.SetViaDiameter( viaDiameter );
  579. aSizes.SetViaDrill( viaDrill );
  580. int diffPairWidth = bds.m_TrackMinWidth;
  581. int diffPairGap = bds.m_MinClearance;
  582. int diffPairViaGap = bds.m_MinClearance;
  583. aSizes.SetDiffPairWidthSource( _( "board minimum track width" ) );
  584. aSizes.SetDiffPairGapSource( _( "board minimum clearance" ) );
  585. found = false;
  586. // First try to pick up diff pair width from starting track, if enabled
  587. if( bds.m_UseConnectedTrackWidth && aStartItem )
  588. found = inheritTrackWidth( aStartItem, &diffPairWidth );
  589. // Next, pick up gap from netclass, and width also if we didn't get a starting width above
  590. if( bds.UseNetClassDiffPair() && aStartItem )
  591. {
  592. if( !found && m_ruleResolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_WIDTH, aStartItem,
  593. nullptr, m_startLayer, &constraint ) )
  594. {
  595. diffPairWidth = std::max( diffPairWidth, constraint.m_Value.Opt() );
  596. if( diffPairWidth == constraint.m_Value.Opt() )
  597. aSizes.SetDiffPairWidthSource( constraint.m_RuleName );
  598. }
  599. if( m_ruleResolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_DIFF_PAIR_GAP, aStartItem,
  600. nullptr, m_startLayer, &constraint ) )
  601. {
  602. diffPairGap = std::max( diffPairGap, constraint.m_Value.Opt() );
  603. diffPairViaGap = std::max( diffPairViaGap, constraint.m_Value.Opt() );
  604. if( diffPairGap == constraint.m_Value.Opt() )
  605. aSizes.SetDiffPairGapSource( constraint.m_RuleName );
  606. }
  607. }
  608. else
  609. {
  610. diffPairWidth = bds.GetCurrentDiffPairWidth();
  611. diffPairGap = bds.GetCurrentDiffPairGap();
  612. diffPairViaGap = bds.GetCurrentDiffPairViaGap();
  613. aSizes.SetDiffPairWidthSource( _( "user choice" ) );
  614. aSizes.SetDiffPairGapSource( _( "user choice" ) );
  615. }
  616. aSizes.SetDiffPairWidth( diffPairWidth );
  617. aSizes.SetDiffPairGap( diffPairGap );
  618. aSizes.SetDiffPairViaGap( diffPairViaGap );
  619. aSizes.SetDiffPairViaGapSameAsTraceGap( false );
  620. int holeToHoleMin = bds.m_HoleToHoleMin;
  621. if( m_ruleResolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE, &dummyVia,
  622. &dummyVia, UNDEFINED_LAYER, &constraint ) )
  623. {
  624. holeToHoleMin = constraint.m_Value.Min();
  625. }
  626. aSizes.SetHoleToHole( holeToHoleMin );
  627. if( m_ruleResolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE, &dummyVia,
  628. &coupledVia, UNDEFINED_LAYER, &constraint ) )
  629. {
  630. holeToHoleMin = constraint.m_Value.Min();
  631. }
  632. aSizes.SetDiffPairHoleToHole( holeToHoleMin );
  633. return true;
  634. }
  635. int PNS_KICAD_IFACE_BASE::StackupHeight( int aFirstLayer, int aSecondLayer ) const
  636. {
  637. if( !m_board || !m_board->GetDesignSettings().m_UseHeightForLengthCalcs )
  638. return 0;
  639. BOARD_STACKUP& stackup = m_board->GetDesignSettings().GetStackupDescriptor();
  640. return stackup.GetLayerDistance( ToLAYER_ID( aFirstLayer ), ToLAYER_ID( aSecondLayer ) );
  641. }
  642. int PNS_PCBNEW_RULE_RESOLVER::matchDpSuffix( const wxString& aNetName, wxString& aComplementNet )
  643. {
  644. int rv = 0;
  645. int count = 0;
  646. for( auto it = aNetName.rbegin(); it != aNetName.rend() && rv == 0; ++it, ++count )
  647. {
  648. int ch = *it;
  649. if( ( ch >= '0' && ch <= '9' ) || ch == '_' )
  650. {
  651. continue;
  652. }
  653. else if( ch == '+' )
  654. {
  655. aComplementNet = wxT( "-" );
  656. rv = 1;
  657. }
  658. else if( ch == '-' )
  659. {
  660. aComplementNet = wxT( "+" );
  661. rv = -1;
  662. }
  663. else if( ch == 'N' )
  664. {
  665. aComplementNet = wxT( "P" );
  666. rv = -1;
  667. }
  668. else if ( ch == 'P' )
  669. {
  670. aComplementNet = wxT( "N" );
  671. rv = 1;
  672. }
  673. else
  674. {
  675. break;
  676. }
  677. }
  678. if( rv != 0 && count >= 1 )
  679. {
  680. aComplementNet = aNetName.Left( aNetName.length() - count )
  681. + aComplementNet
  682. + aNetName.Right( count - 1 );
  683. }
  684. return rv;
  685. }
  686. PNS::NET_HANDLE PNS_PCBNEW_RULE_RESOLVER::DpCoupledNet( PNS::NET_HANDLE aNet )
  687. {
  688. if( NETINFO_ITEM* net = static_cast<NETINFO_ITEM*>( aNet ) )
  689. {
  690. wxString refName = net->GetNetname();
  691. wxString coupledNetName;
  692. if( matchDpSuffix( refName, coupledNetName ) )
  693. return m_board->FindNet( coupledNetName );
  694. }
  695. return nullptr;
  696. }
  697. int PNS_PCBNEW_RULE_RESOLVER::NetCode( PNS::NET_HANDLE aNet )
  698. {
  699. return m_routerIface->GetNetCode( aNet );
  700. }
  701. wxString PNS_PCBNEW_RULE_RESOLVER::NetName( PNS::NET_HANDLE aNet )
  702. {
  703. return m_routerIface->GetNetName( aNet );
  704. }
  705. int PNS_PCBNEW_RULE_RESOLVER::DpNetPolarity( PNS::NET_HANDLE aNet )
  706. {
  707. wxString refName;
  708. if( NETINFO_ITEM* net = static_cast<NETINFO_ITEM*>( aNet ) )
  709. refName = net->GetNetname();
  710. wxString dummy1;
  711. return matchDpSuffix( refName, dummy1 );
  712. }
  713. bool PNS_PCBNEW_RULE_RESOLVER::DpNetPair( const PNS::ITEM* aItem, PNS::NET_HANDLE& aNetP,
  714. PNS::NET_HANDLE& aNetN )
  715. {
  716. if( !aItem || !aItem->Net() )
  717. return false;
  718. wxString netNameP = static_cast<NETINFO_ITEM*>( aItem->Net() )->GetNetname();
  719. wxString netNameN, netNameCoupled;
  720. int r = matchDpSuffix( netNameP, netNameCoupled );
  721. if( r == 0 )
  722. {
  723. return false;
  724. }
  725. else if( r == 1 )
  726. {
  727. netNameN = netNameCoupled;
  728. }
  729. else
  730. {
  731. netNameN = netNameP;
  732. netNameP = netNameCoupled;
  733. }
  734. PNS::NET_HANDLE netInfoP = m_board->FindNet( netNameP );
  735. PNS::NET_HANDLE netInfoN = m_board->FindNet( netNameN );
  736. if( !netInfoP || !netInfoN )
  737. return false;
  738. aNetP = netInfoP;
  739. aNetN = netInfoN;
  740. return true;
  741. }
  742. class PNS_PCBNEW_DEBUG_DECORATOR: public PNS::DEBUG_DECORATOR
  743. {
  744. public:
  745. PNS_PCBNEW_DEBUG_DECORATOR( KIGFX::VIEW* aView = nullptr ) :
  746. PNS::DEBUG_DECORATOR(),
  747. m_view( nullptr ),
  748. m_items( nullptr ),
  749. m_depth( 0 )
  750. {
  751. SetView( aView );
  752. }
  753. ~PNS_PCBNEW_DEBUG_DECORATOR()
  754. {
  755. PNS_PCBNEW_DEBUG_DECORATOR::Clear();
  756. delete m_items;
  757. }
  758. void SetView( KIGFX::VIEW* aView )
  759. {
  760. Clear();
  761. delete m_items;
  762. m_items = nullptr;
  763. m_view = aView;
  764. if( m_view == nullptr )
  765. return;
  766. if( m_view->GetGAL() )
  767. m_depth = m_view->GetGAL()->GetMinDepth();
  768. m_items = new KIGFX::VIEW_GROUP( m_view );
  769. m_items->SetLayer( LAYER_SELECT_OVERLAY ) ;
  770. m_view->Add( m_items );
  771. }
  772. void AddPoint( const VECTOR2I& aP, const KIGFX::COLOR4D& aColor, int aSize,
  773. const wxString& aName = wxT( "" ),
  774. const SRC_LOCATION_INFO& aSrcLoc = SRC_LOCATION_INFO() ) override
  775. {
  776. SHAPE_LINE_CHAIN sh;
  777. sh.SetWidth( 10000 );
  778. sh.Append( aP.x - aSize, aP.y - aSize );
  779. sh.Append( aP.x + aSize, aP.y + aSize );
  780. sh.Append( aP.x, aP.y );
  781. sh.Append( aP.x - aSize, aP.y + aSize );
  782. sh.Append( aP.x + aSize, aP.y - aSize );
  783. AddShape( &sh, aColor, sh.Width(), aName, aSrcLoc );
  784. }
  785. void AddItem( const PNS::ITEM* aItem, const KIGFX::COLOR4D& aColor, int aOverrideWidth = 0,
  786. const wxString& aName = wxT( "" ),
  787. const SRC_LOCATION_INFO& aSrcLoc = SRC_LOCATION_INFO() ) override
  788. {
  789. if( !m_view || !aItem )
  790. return;
  791. ROUTER_PREVIEW_ITEM* pitem = new ROUTER_PREVIEW_ITEM( aItem, m_view );
  792. pitem->SetColor( aColor.WithAlpha( 0.5 ) );
  793. pitem->SetWidth( aOverrideWidth );
  794. pitem->SetDepth( nextDepth() );
  795. m_items->Add( pitem );
  796. m_view->Update( m_items );
  797. }
  798. void AddShape( const BOX2I& aBox, const KIGFX::COLOR4D& aColor, int aOverrideWidth = 0,
  799. const wxString& aName = wxT( "" ),
  800. const SRC_LOCATION_INFO& aSrcLoc = SRC_LOCATION_INFO() ) override
  801. {
  802. SHAPE_LINE_CHAIN l;
  803. l.SetWidth( aOverrideWidth );
  804. VECTOR2I o = aBox.GetOrigin();
  805. VECTOR2I s = aBox.GetSize();
  806. l.Append( o );
  807. l.Append( o.x + s.x, o.y );
  808. l.Append( o.x + s.x, o.y + s.y );
  809. l.Append( o.x, o.y + s.y );
  810. l.Append( o );
  811. AddShape( &l, aColor, aOverrideWidth, aName, aSrcLoc );
  812. }
  813. void AddShape( const SHAPE* aShape, const KIGFX::COLOR4D& aColor, int aOverrideWidth = 0,
  814. const wxString& aName = wxT( "" ),
  815. const SRC_LOCATION_INFO& aSrcLoc = SRC_LOCATION_INFO() ) override
  816. {
  817. if( !m_view || !aShape )
  818. return;
  819. ROUTER_PREVIEW_ITEM* pitem = new ROUTER_PREVIEW_ITEM( *aShape, m_view );
  820. pitem->SetColor( aColor.WithAlpha( 0.5 ) );
  821. pitem->SetWidth( aOverrideWidth );
  822. pitem->SetDepth( nextDepth() );
  823. m_items->Add( pitem );
  824. m_view->Update( m_items );
  825. }
  826. void Clear() override
  827. {
  828. if( m_view && m_items )
  829. {
  830. m_items->FreeItems();
  831. m_view->Update( m_items );
  832. if( m_view->GetGAL() )
  833. m_depth = m_view->GetGAL()->GetMinDepth();
  834. }
  835. }
  836. private:
  837. double nextDepth()
  838. {
  839. // Use different depths so that the transculent shapes won't overwrite each other.
  840. m_depth++;
  841. if( m_depth >= 0 && m_view->GetGAL() )
  842. m_depth = m_view->GetGAL()->GetMinDepth();
  843. return m_depth;
  844. }
  845. KIGFX::VIEW* m_view;
  846. KIGFX::VIEW_GROUP* m_items;
  847. double m_depth;
  848. };
  849. PNS::DEBUG_DECORATOR* PNS_KICAD_IFACE_BASE::GetDebugDecorator()
  850. {
  851. return m_debugDecorator;
  852. }
  853. PNS_KICAD_IFACE_BASE::PNS_KICAD_IFACE_BASE()
  854. {
  855. m_ruleResolver = nullptr;
  856. m_board = nullptr;
  857. m_world = nullptr;
  858. m_debugDecorator = nullptr;
  859. m_startLayer = -1;
  860. }
  861. PNS_KICAD_IFACE::PNS_KICAD_IFACE()
  862. {
  863. m_tool = nullptr;
  864. m_view = nullptr;
  865. m_previewItems = nullptr;
  866. m_commitFlags = 0;
  867. }
  868. PNS_KICAD_IFACE_BASE::~PNS_KICAD_IFACE_BASE()
  869. {
  870. }
  871. PNS_KICAD_IFACE::~PNS_KICAD_IFACE()
  872. {
  873. delete m_ruleResolver;
  874. delete m_debugDecorator;
  875. if( m_previewItems )
  876. {
  877. m_previewItems->FreeItems();
  878. delete m_previewItems;
  879. }
  880. }
  881. std::unique_ptr<PNS::SOLID> PNS_KICAD_IFACE_BASE::syncPad( PAD* aPad )
  882. {
  883. LAYER_RANGE layers( 0, MAX_CU_LAYERS - 1 );
  884. // ignore non-copper pads except for those with holes
  885. if( ( aPad->GetLayerSet() & LSET::AllCuMask() ).none() && aPad->GetDrillSize().x == 0 )
  886. return nullptr;
  887. switch( aPad->GetAttribute() )
  888. {
  889. case PAD_ATTRIB::PTH:
  890. case PAD_ATTRIB::NPTH:
  891. break;
  892. case PAD_ATTRIB::CONN:
  893. case PAD_ATTRIB::SMD:
  894. {
  895. LSET lmsk = aPad->GetLayerSet();
  896. bool is_copper = false;
  897. for( int i = 0; i < MAX_CU_LAYERS; i++ )
  898. {
  899. if( lmsk[i] )
  900. {
  901. is_copper = true;
  902. if( aPad->GetAttribute() != PAD_ATTRIB::NPTH )
  903. layers = LAYER_RANGE( i );
  904. break;
  905. }
  906. }
  907. if( !is_copper )
  908. return nullptr;
  909. break;
  910. }
  911. default:
  912. wxLogTrace( wxT( "PNS" ), wxT( "unsupported pad type 0x%x" ), aPad->GetAttribute() );
  913. return nullptr;
  914. }
  915. std::unique_ptr<PNS::SOLID> solid = std::make_unique<PNS::SOLID>();
  916. if( aPad->GetAttribute() == PAD_ATTRIB::NPTH )
  917. solid->SetRoutable( false );
  918. solid->SetLayers( layers );
  919. solid->SetNet( aPad->GetNet() );
  920. solid->SetParent( aPad );
  921. solid->SetPadToDie( aPad->GetPadToDieLength() );
  922. solid->SetOrientation( aPad->GetOrientation() );
  923. if( aPad->IsFreePad() )
  924. solid->SetIsFreePad();
  925. VECTOR2I wx_c = aPad->ShapePos();
  926. VECTOR2I offset = aPad->GetOffset();
  927. VECTOR2I c( wx_c.x, wx_c.y );
  928. RotatePoint( offset, aPad->GetOrientation() );
  929. solid->SetPos( VECTOR2I( c.x - offset.x, c.y - offset.y ) );
  930. solid->SetOffset( VECTOR2I( offset.x, offset.y ) );
  931. if( aPad->GetDrillSize().x > 0 )
  932. solid->SetHole( new PNS::HOLE( aPad->GetEffectiveHoleShape()->Clone() ) );
  933. std::shared_ptr<SHAPE_POLY_SET> shape = aPad->GetEffectivePolygon();
  934. if( shape->OutlineCount() )
  935. solid->SetShape( new SHAPE_SIMPLE( shape->Outline( 0 ) ) );
  936. return solid;
  937. }
  938. std::unique_ptr<PNS::SEGMENT> PNS_KICAD_IFACE_BASE::syncTrack( PCB_TRACK* aTrack )
  939. {
  940. auto segment = std::make_unique<PNS::SEGMENT>( SEG( aTrack->GetStart(), aTrack->GetEnd() ),
  941. aTrack->GetNet() );
  942. segment->SetWidth( aTrack->GetWidth() );
  943. segment->SetLayers( LAYER_RANGE( aTrack->GetLayer() ) );
  944. segment->SetParent( aTrack );
  945. if( aTrack->IsLocked() )
  946. segment->Mark( PNS::MK_LOCKED );
  947. return segment;
  948. }
  949. std::unique_ptr<PNS::ARC> PNS_KICAD_IFACE_BASE::syncArc( PCB_ARC* aArc )
  950. {
  951. auto arc = std::make_unique<PNS::ARC>( SHAPE_ARC( aArc->GetStart(), aArc->GetMid(),
  952. aArc->GetEnd(), aArc->GetWidth() ),
  953. aArc->GetNet() );
  954. arc->SetLayers( LAYER_RANGE( aArc->GetLayer() ) );
  955. arc->SetParent( aArc );
  956. if( aArc->IsLocked() )
  957. arc->Mark( PNS::MK_LOCKED );
  958. return arc;
  959. }
  960. std::unique_ptr<PNS::VIA> PNS_KICAD_IFACE_BASE::syncVia( PCB_VIA* aVia )
  961. {
  962. PCB_LAYER_ID top, bottom;
  963. aVia->LayerPair( &top, &bottom );
  964. auto via = std::make_unique<PNS::VIA>( aVia->GetPosition(),
  965. LAYER_RANGE( aVia->TopLayer(), aVia->BottomLayer() ),
  966. aVia->GetWidth(),
  967. aVia->GetDrillValue(),
  968. aVia->GetNet(),
  969. aVia->GetViaType() );
  970. via->SetParent( aVia );
  971. if( aVia->IsLocked() )
  972. via->Mark( PNS::MK_LOCKED );
  973. via->SetIsFree( aVia->GetIsFree() );
  974. via->SetHole( PNS::HOLE::MakeCircularHole( aVia->GetPosition(), aVia->GetDrillValue() / 2 ) );
  975. return via;
  976. }
  977. bool PNS_KICAD_IFACE_BASE::syncZone( PNS::NODE* aWorld, ZONE* aZone, SHAPE_POLY_SET* aBoardOutline )
  978. {
  979. SHAPE_POLY_SET* poly;
  980. // TODO handle aZone->GetDoNotAllowVias()
  981. // TODO handle rules which disallow tracks & vias
  982. if( !aZone->GetIsRuleArea() || !aZone->GetDoNotAllowTracks() )
  983. return false;
  984. LSET layers = aZone->GetLayerSet();
  985. poly = aZone->Outline();
  986. poly->CacheTriangulation( false );
  987. if( !poly->IsTriangulationUpToDate() )
  988. {
  989. UNITS_PROVIDER unitsProvider( pcbIUScale, GetUnits() );
  990. KIDIALOG dlg( nullptr, wxString::Format( _( "%s is malformed." ),
  991. aZone->GetItemDescription( &unitsProvider ) ),
  992. KIDIALOG::KD_WARNING );
  993. dlg.ShowDetailedText( wxString::Format( _( "This zone cannot be handled by the router.\n"
  994. "Please verify it is not a self-intersecting "
  995. "polygon." ) ) );
  996. dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
  997. dlg.ShowModal();
  998. return false;
  999. }
  1000. for( int layer = F_Cu; layer <= B_Cu; layer++ )
  1001. {
  1002. if( !layers[ layer ] )
  1003. continue;
  1004. for( int outline = 0; outline < poly->OutlineCount(); outline++ )
  1005. {
  1006. const SHAPE_POLY_SET::TRIANGULATED_POLYGON* tri = poly->TriangulatedPolygon( outline );
  1007. for( size_t i = 0; i < tri->GetTriangleCount(); i++)
  1008. {
  1009. VECTOR2I a, b, c;
  1010. tri->GetTriangle( i, a, b, c );
  1011. SHAPE_SIMPLE* triShape = new SHAPE_SIMPLE;
  1012. triShape->Append( a );
  1013. triShape->Append( b );
  1014. triShape->Append( c );
  1015. std::unique_ptr<PNS::SOLID> solid = std::make_unique<PNS::SOLID>();
  1016. solid->SetLayer( layer );
  1017. solid->SetNet( nullptr );
  1018. solid->SetParent( aZone );
  1019. solid->SetShape( triShape );
  1020. solid->SetIsCompoundShapePrimitive();
  1021. solid->SetRoutable( false );
  1022. aWorld->Add( std::move( solid ) );
  1023. }
  1024. }
  1025. }
  1026. return true;
  1027. }
  1028. bool PNS_KICAD_IFACE_BASE::syncTextItem( PNS::NODE* aWorld, PCB_TEXT* aText, PCB_LAYER_ID aLayer )
  1029. {
  1030. if( !IsCopperLayer( aLayer ) )
  1031. return false;
  1032. std::unique_ptr<PNS::SOLID> solid = std::make_unique<PNS::SOLID>();
  1033. SHAPE_SIMPLE* shape = new SHAPE_SIMPLE;
  1034. solid->SetLayer( aLayer );
  1035. solid->SetNet( nullptr );
  1036. solid->SetParent( aText );
  1037. solid->SetShape( shape ); // takes ownership
  1038. solid->SetRoutable( false );
  1039. SHAPE_POLY_SET cornerBuffer;
  1040. aText->TransformShapeToPolygon( cornerBuffer, aText->GetLayer(), 0,
  1041. m_board->GetDesignSettings().m_MaxError, ERROR_OUTSIDE );
  1042. if( !cornerBuffer.OutlineCount() )
  1043. return false;
  1044. for( const VECTOR2I& pt : cornerBuffer.Outline( 0 ).CPoints() )
  1045. shape->Append( pt );
  1046. aWorld->Add( std::move( solid ) );
  1047. return true;
  1048. }
  1049. bool PNS_KICAD_IFACE_BASE::syncGraphicalItem( PNS::NODE* aWorld, PCB_SHAPE* aItem )
  1050. {
  1051. if( aItem->GetLayer() == Edge_Cuts
  1052. || aItem->GetLayer() == Margin
  1053. || IsCopperLayer( aItem->GetLayer() ) )
  1054. {
  1055. std::vector<SHAPE*> shapes = aItem->MakeEffectiveShapes();
  1056. for( SHAPE* shape : shapes )
  1057. {
  1058. std::unique_ptr<PNS::SOLID> solid = std::make_unique<PNS::SOLID>();
  1059. if( aItem->GetLayer() == Edge_Cuts || aItem->GetLayer() == Margin )
  1060. solid->SetLayers( LAYER_RANGE( F_Cu, B_Cu ) );
  1061. else
  1062. solid->SetLayer( aItem->GetLayer() );
  1063. if( aItem->GetLayer() == Edge_Cuts )
  1064. {
  1065. switch( shape->Type() )
  1066. {
  1067. case SH_SEGMENT: static_cast<SHAPE_SEGMENT*>( shape )->SetWidth( 0 ); break;
  1068. case SH_ARC: static_cast<SHAPE_ARC*>( shape )->SetWidth( 0 ); break;
  1069. case SH_LINE_CHAIN: static_cast<SHAPE_LINE_CHAIN*>( shape )->SetWidth( 0 ); break;
  1070. default: /* remaining shapes don't have width */ break;
  1071. }
  1072. }
  1073. solid->SetAnchorPoints( aItem->GetConnectionPoints() );
  1074. solid->SetNet( aItem->GetNet() );
  1075. solid->SetParent( aItem );
  1076. solid->SetShape( shape ); // takes ownership
  1077. if( shapes.size() > 1 )
  1078. solid->SetIsCompoundShapePrimitive();
  1079. aWorld->Add( std::move( solid ) );
  1080. }
  1081. return true;
  1082. }
  1083. return false;
  1084. }
  1085. void PNS_KICAD_IFACE_BASE::SetBoard( BOARD* aBoard )
  1086. {
  1087. m_board = aBoard;
  1088. wxLogTrace( wxT( "PNS" ), wxT( "m_board = %p" ), m_board );
  1089. }
  1090. bool PNS_KICAD_IFACE::IsAnyLayerVisible( const LAYER_RANGE& aLayer ) const
  1091. {
  1092. if( !m_view )
  1093. return false;
  1094. for( int i = aLayer.Start(); i <= aLayer.End(); i++ )
  1095. {
  1096. if( m_view->IsLayerVisible( i ) )
  1097. return true;
  1098. }
  1099. return false;
  1100. }
  1101. bool PNS_KICAD_IFACE_BASE::IsFlashedOnLayer( const PNS::ITEM* aItem, int aLayer ) const
  1102. {
  1103. /// Default is all layers
  1104. if( aLayer < 0 )
  1105. return true;
  1106. if( aItem->Parent() )
  1107. {
  1108. switch( aItem->Parent()->Type() )
  1109. {
  1110. case PCB_VIA_T:
  1111. {
  1112. const PCB_VIA* via = static_cast<const PCB_VIA*>( aItem->Parent() );
  1113. return via->FlashLayer( ToLAYER_ID( aLayer ) );
  1114. }
  1115. case PCB_PAD_T:
  1116. {
  1117. const PAD* pad = static_cast<const PAD*>( aItem->Parent() );
  1118. return pad->FlashLayer( ToLAYER_ID( aLayer ) );
  1119. }
  1120. default:
  1121. break;
  1122. }
  1123. }
  1124. return aItem->Layers().Overlaps( aLayer );
  1125. }
  1126. bool PNS_KICAD_IFACE_BASE::IsFlashedOnLayer( const PNS::ITEM* aItem,
  1127. const LAYER_RANGE& aLayer ) const
  1128. {
  1129. LAYER_RANGE test = aItem->Layers().Intersection( aLayer );
  1130. if( aItem->Parent() )
  1131. {
  1132. switch( aItem->Parent()->Type() )
  1133. {
  1134. case PCB_VIA_T:
  1135. {
  1136. const PCB_VIA* via = static_cast<const PCB_VIA*>( aItem->Parent() );
  1137. for( int layer = test.Start(); layer <= test.End(); ++layer )
  1138. {
  1139. if( via->FlashLayer( ToLAYER_ID( layer ) ) )
  1140. return true;
  1141. }
  1142. return false;
  1143. }
  1144. case PCB_PAD_T:
  1145. {
  1146. const PAD* pad = static_cast<const PAD*>( aItem->Parent() );
  1147. for( int layer = test.Start(); layer <= test.End(); ++layer )
  1148. {
  1149. if( pad->FlashLayer( ToLAYER_ID( layer ) ) )
  1150. return true;
  1151. }
  1152. return false;
  1153. }
  1154. default:
  1155. break;
  1156. }
  1157. }
  1158. return test.Start() <= test.End();
  1159. }
  1160. bool PNS_KICAD_IFACE::IsItemVisible( const PNS::ITEM* aItem ) const
  1161. {
  1162. // by default, all items are visible (new ones created by the router have parent == NULL
  1163. // as they have not been committed yet to the BOARD)
  1164. if( !m_view || !aItem->Parent() )
  1165. return true;
  1166. BOARD_ITEM* item = aItem->Parent();
  1167. bool isOnVisibleLayer = true;
  1168. RENDER_SETTINGS* settings = m_view->GetPainter()->GetSettings();
  1169. if( settings->GetHighContrast() )
  1170. isOnVisibleLayer = item->IsOnLayer( settings->GetPrimaryHighContrastLayer() );
  1171. if( m_view->IsVisible( item ) && isOnVisibleLayer )
  1172. {
  1173. for( PCB_LAYER_ID layer : item->GetLayerSet().Seq() )
  1174. {
  1175. if( item->ViewGetLOD( layer, m_view ) < m_view->GetScale() )
  1176. return true;
  1177. }
  1178. }
  1179. // Items hidden in the router are not hidden on the board
  1180. if( m_hiddenItems.find( item ) != m_hiddenItems.end() )
  1181. return true;
  1182. return false;
  1183. }
  1184. void PNS_KICAD_IFACE_BASE::SyncWorld( PNS::NODE *aWorld )
  1185. {
  1186. if( !m_board )
  1187. {
  1188. wxLogTrace( wxT( "PNS" ), wxT( "No board attached, aborting sync." ) );
  1189. return;
  1190. }
  1191. int worstClearance = m_board->GetMaxClearanceValue();
  1192. m_world = aWorld;
  1193. for( BOARD_ITEM* gitem : m_board->Drawings() )
  1194. {
  1195. if ( gitem->Type() == PCB_SHAPE_T || gitem->Type() == PCB_TEXTBOX_T )
  1196. {
  1197. syncGraphicalItem( aWorld, static_cast<PCB_SHAPE*>( gitem ) );
  1198. }
  1199. else if( gitem->Type() == PCB_TEXT_T )
  1200. {
  1201. syncTextItem( aWorld, static_cast<PCB_TEXT*>( gitem ), gitem->GetLayer() );
  1202. }
  1203. }
  1204. SHAPE_POLY_SET buffer;
  1205. SHAPE_POLY_SET* boardOutline = nullptr;
  1206. if( m_board->GetBoardPolygonOutlines( buffer ) )
  1207. boardOutline = &buffer;
  1208. for( ZONE* zone : m_board->Zones() )
  1209. {
  1210. syncZone( aWorld, zone, boardOutline );
  1211. }
  1212. for( FOOTPRINT* footprint : m_board->Footprints() )
  1213. {
  1214. for( PAD* pad : footprint->Pads() )
  1215. {
  1216. if( std::unique_ptr<PNS::SOLID> solid = syncPad( pad ) )
  1217. aWorld->Add( std::move( solid ) );
  1218. worstClearance = std::max( worstClearance, pad->GetLocalClearance() );
  1219. if( pad->GetProperty() == PAD_PROP::CASTELLATED )
  1220. {
  1221. std::unique_ptr<SHAPE> hole;
  1222. hole.reset( pad->GetEffectiveHoleShape()->Clone() );
  1223. aWorld->AddEdgeExclusion( std::move( hole ) );
  1224. }
  1225. }
  1226. syncTextItem( aWorld, &footprint->Reference(), footprint->Reference().GetLayer() );
  1227. syncTextItem( aWorld, &footprint->Value(), footprint->Value().GetLayer() );
  1228. for( ZONE* zone : footprint->Zones() )
  1229. syncZone( aWorld, zone, boardOutline );
  1230. for( PCB_FIELD* field : footprint->Fields() )
  1231. syncTextItem( aWorld, static_cast<PCB_TEXT*>( field ), field->GetLayer() );
  1232. for( BOARD_ITEM* item : footprint->GraphicalItems() )
  1233. {
  1234. if( item->Type() == PCB_SHAPE_T || item->Type() == PCB_TEXTBOX_T )
  1235. {
  1236. syncGraphicalItem( aWorld, static_cast<PCB_SHAPE*>( item ) );
  1237. }
  1238. else if( item->Type() == PCB_TEXT_T )
  1239. {
  1240. syncTextItem( aWorld, static_cast<PCB_TEXT*>( item ), item->GetLayer() );
  1241. }
  1242. }
  1243. }
  1244. for( PCB_TRACK* t : m_board->Tracks() )
  1245. {
  1246. KICAD_T type = t->Type();
  1247. if( type == PCB_TRACE_T )
  1248. {
  1249. if( std::unique_ptr<PNS::SEGMENT> segment = syncTrack( t ) )
  1250. aWorld->Add( std::move( segment ) );
  1251. }
  1252. else if( type == PCB_ARC_T )
  1253. {
  1254. if( std::unique_ptr<PNS::ARC> arc = syncArc( static_cast<PCB_ARC*>( t ) ) )
  1255. aWorld->Add( std::move( arc ) );
  1256. }
  1257. else if( type == PCB_VIA_T )
  1258. {
  1259. if( std::unique_ptr<PNS::VIA> via = syncVia( static_cast<PCB_VIA*>( t ) ) )
  1260. aWorld->Add( std::move( via ) );
  1261. }
  1262. }
  1263. // NB: if this were ever to become a long-lived object we would need to dirty its
  1264. // clearance cache here....
  1265. delete m_ruleResolver;
  1266. m_ruleResolver = new PNS_PCBNEW_RULE_RESOLVER( m_board, this );
  1267. aWorld->SetRuleResolver( m_ruleResolver );
  1268. aWorld->SetMaxClearance( worstClearance + m_ruleResolver->ClearanceEpsilon() );
  1269. }
  1270. void PNS_KICAD_IFACE::EraseView()
  1271. {
  1272. for( BOARD_ITEM* item : m_hiddenItems )
  1273. m_view->SetVisible( item, true );
  1274. m_hiddenItems.clear();
  1275. if( m_previewItems )
  1276. {
  1277. m_previewItems->FreeItems();
  1278. m_view->Update( m_previewItems );
  1279. }
  1280. if( m_debugDecorator )
  1281. m_debugDecorator->Clear();
  1282. }
  1283. void PNS_KICAD_IFACE_BASE::SetDebugDecorator( PNS::DEBUG_DECORATOR *aDec )
  1284. {
  1285. m_debugDecorator = aDec;
  1286. }
  1287. void PNS_KICAD_IFACE::DisplayItem( const PNS::ITEM* aItem, int aClearance, bool aEdit, bool aIsHeadTrace )
  1288. {
  1289. if( aItem->IsVirtual() )
  1290. return;
  1291. ROUTER_PREVIEW_ITEM* pitem = new ROUTER_PREVIEW_ITEM( aItem, m_view );
  1292. // Note: SEGMENT_T is used for placed tracks; LINE_T is used for the routing head
  1293. static int tracks = PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T | PNS::ITEM::LINE_T;
  1294. static int tracksOrVias = tracks | PNS::ITEM::VIA_T;
  1295. if( aClearance >= 0 )
  1296. {
  1297. pitem->SetClearance( aClearance );
  1298. auto* settings = static_cast<PCBNEW_SETTINGS*>( m_tool->GetManager()->GetSettings() );
  1299. switch( settings->m_Display.m_TrackClearance )
  1300. {
  1301. case SHOW_WITH_VIA_ALWAYS:
  1302. case SHOW_WITH_VIA_WHILE_ROUTING_OR_DRAGGING:
  1303. pitem->ShowClearance( aItem->OfKind( tracksOrVias ) );
  1304. break;
  1305. case SHOW_WITH_VIA_WHILE_ROUTING:
  1306. pitem->ShowClearance( aItem->OfKind( tracksOrVias ) && !aEdit );
  1307. break;
  1308. case SHOW_WHILE_ROUTING:
  1309. pitem->ShowClearance( aItem->OfKind( tracks ) && !aEdit );
  1310. break;
  1311. default:
  1312. pitem->ShowClearance( false );
  1313. break;
  1314. }
  1315. }
  1316. if( aIsHeadTrace )
  1317. {
  1318. pitem->SetIsHeadTrace( true );
  1319. pitem->Update( aItem );
  1320. }
  1321. m_previewItems->Add( pitem );
  1322. m_view->Update( m_previewItems );
  1323. }
  1324. void PNS_KICAD_IFACE::DisplayPathLine( const SHAPE_LINE_CHAIN& aLine, int aImportance )
  1325. {
  1326. ROUTER_PREVIEW_ITEM* pitem = new ROUTER_PREVIEW_ITEM( aLine, m_view );
  1327. pitem->SetDepth( ROUTER_PREVIEW_ITEM::PathOverlayDepth );
  1328. COLOR4D color;
  1329. if( aImportance >= 1 )
  1330. color = COLOR4D( 1.0, 1.0, 0.0, 0.6 );
  1331. else if( aImportance == 0 )
  1332. color = COLOR4D( 0.7, 0.7, 0.7, 0.6 );
  1333. pitem->SetColor( color );
  1334. m_previewItems->Add( pitem );
  1335. m_view->Update( m_previewItems );
  1336. }
  1337. void PNS_KICAD_IFACE::DisplayRatline( const SHAPE_LINE_CHAIN& aRatline, PNS::NET_HANDLE aNet )
  1338. {
  1339. ROUTER_PREVIEW_ITEM* pitem = new ROUTER_PREVIEW_ITEM( aRatline, m_view );
  1340. KIGFX::RENDER_SETTINGS* renderSettings = m_view->GetPainter()->GetSettings();
  1341. KIGFX::PCB_RENDER_SETTINGS* rs = static_cast<KIGFX::PCB_RENDER_SETTINGS*>( renderSettings );
  1342. bool colorByNet = rs->GetNetColorMode() != NET_COLOR_MODE::OFF;
  1343. COLOR4D defaultColor = rs->GetColor( nullptr, LAYER_RATSNEST );
  1344. COLOR4D color = defaultColor;
  1345. std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
  1346. std::set<int> highlightedNets = rs->GetHighlightNetCodes();
  1347. std::map<int, KIGFX::COLOR4D>& netColors = rs->GetNetColorMap();
  1348. std::map<wxString, KIGFX::COLOR4D>& ncColors = rs->GetNetclassColorMap();
  1349. const std::map<int, wxString>& ncMap = connectivity->GetNetclassMap();
  1350. int netCode = -1;
  1351. if( NETINFO_ITEM* net = static_cast<NETINFO_ITEM*>( aNet ) )
  1352. netCode = net->GetNetCode();
  1353. if( colorByNet && netColors.count( netCode ) )
  1354. color = netColors.at( netCode );
  1355. else if( colorByNet && ncMap.count( netCode ) && ncColors.count( ncMap.at( netCode ) ) )
  1356. color = ncColors.at( ncMap.at( netCode ) );
  1357. else
  1358. color = defaultColor;
  1359. if( color == COLOR4D::UNSPECIFIED )
  1360. color = defaultColor;
  1361. pitem->SetColor( color.Brightened( 0.5 ).WithAlpha( std::min( 1.0, color.a + 0.4 ) ) );
  1362. m_previewItems->Add( pitem );
  1363. m_view->Update( m_previewItems );
  1364. }
  1365. void PNS_KICAD_IFACE::HideItem( PNS::ITEM* aItem )
  1366. {
  1367. BOARD_ITEM* parent = aItem->Parent();
  1368. if( parent )
  1369. {
  1370. if( m_view->IsVisible( parent ) )
  1371. m_hiddenItems.insert( parent );
  1372. m_view->SetVisible( parent, false );
  1373. m_view->Update( parent, KIGFX::APPEARANCE );
  1374. for( ZONE* td : m_board->Zones() )
  1375. {
  1376. if( td->IsTeardropArea()
  1377. && td->GetBoundingBox().Intersects( aItem->Parent()->GetBoundingBox() )
  1378. && td->Outline()->Collide( aItem->Shape() ) )
  1379. {
  1380. m_view->SetVisible( td, false );
  1381. m_view->Update( td, KIGFX::APPEARANCE );
  1382. }
  1383. }
  1384. }
  1385. }
  1386. void PNS_KICAD_IFACE_BASE::RemoveItem( PNS::ITEM* aItem )
  1387. {
  1388. }
  1389. void PNS_KICAD_IFACE::RemoveItem( PNS::ITEM* aItem )
  1390. {
  1391. BOARD_ITEM* parent = aItem->Parent();
  1392. if( aItem->OfKind( PNS::ITEM::SOLID_T ) )
  1393. {
  1394. PAD* pad = static_cast<PAD*>( parent );
  1395. VECTOR2I pos = static_cast<PNS::SOLID*>( aItem )->Pos();
  1396. m_fpOffsets[ pad ].p_old = pos;
  1397. return;
  1398. }
  1399. if( parent )
  1400. {
  1401. m_commit->Remove( parent );
  1402. }
  1403. }
  1404. void PNS_KICAD_IFACE_BASE::UpdateItem( PNS::ITEM* aItem )
  1405. {
  1406. }
  1407. void PNS_KICAD_IFACE::UpdateItem( PNS::ITEM* aItem )
  1408. {
  1409. BOARD_ITEM* board_item = aItem->Parent();
  1410. m_commit->Modify( board_item );
  1411. switch( aItem->Kind() )
  1412. {
  1413. case PNS::ITEM::ARC_T:
  1414. {
  1415. PNS::ARC* arc = static_cast<PNS::ARC*>( aItem );
  1416. PCB_ARC* arc_board = static_cast<PCB_ARC*>( board_item );
  1417. const SHAPE_ARC* arc_shape = static_cast<const SHAPE_ARC*>( arc->Shape() );
  1418. arc_board->SetStart( VECTOR2I( arc_shape->GetP0() ) );
  1419. arc_board->SetEnd( VECTOR2I( arc_shape->GetP1() ) );
  1420. arc_board->SetMid( VECTOR2I( arc_shape->GetArcMid() ) );
  1421. arc_board->SetWidth( arc->Width() );
  1422. break;
  1423. }
  1424. case PNS::ITEM::SEGMENT_T:
  1425. {
  1426. PNS::SEGMENT* seg = static_cast<PNS::SEGMENT*>( aItem );
  1427. PCB_TRACK* track = static_cast<PCB_TRACK*>( board_item );
  1428. const SEG& s = seg->Seg();
  1429. track->SetStart( VECTOR2I( s.A.x, s.A.y ) );
  1430. track->SetEnd( VECTOR2I( s.B.x, s.B.y ) );
  1431. track->SetWidth( seg->Width() );
  1432. break;
  1433. }
  1434. case PNS::ITEM::VIA_T:
  1435. {
  1436. PCB_VIA* via_board = static_cast<PCB_VIA*>( board_item );
  1437. PNS::VIA* via = static_cast<PNS::VIA*>( aItem );
  1438. via_board->SetPosition( VECTOR2I( via->Pos().x, via->Pos().y ) );
  1439. via_board->SetWidth( via->Diameter() );
  1440. via_board->SetDrill( via->Drill() );
  1441. via_board->SetNet( static_cast<NETINFO_ITEM*>( via->Net() ) );
  1442. via_board->SetViaType( via->ViaType() ); // MUST be before SetLayerPair()
  1443. via_board->SetIsFree( via->IsFree() );
  1444. via_board->SetLayerPair( ToLAYER_ID( via->Layers().Start() ),
  1445. ToLAYER_ID( via->Layers().End() ) );
  1446. break;
  1447. }
  1448. case PNS::ITEM::SOLID_T:
  1449. {
  1450. PAD* pad = static_cast<PAD*>( aItem->Parent() );
  1451. VECTOR2I pos = static_cast<PNS::SOLID*>( aItem )->Pos();
  1452. m_fpOffsets[ pad ].p_old = pad->GetPosition();
  1453. m_fpOffsets[ pad ].p_new = pos;
  1454. break;
  1455. }
  1456. default:
  1457. break;
  1458. }
  1459. }
  1460. void PNS_KICAD_IFACE_BASE::AddItem( PNS::ITEM* aItem )
  1461. {
  1462. }
  1463. void PNS_KICAD_IFACE::AddItem( PNS::ITEM* aItem )
  1464. {
  1465. BOARD_CONNECTED_ITEM* newBI = nullptr;
  1466. switch( aItem->Kind() )
  1467. {
  1468. case PNS::ITEM::ARC_T:
  1469. {
  1470. PNS::ARC* arc = static_cast<PNS::ARC*>( aItem );
  1471. PCB_ARC* new_arc = new PCB_ARC( m_board, static_cast<const SHAPE_ARC*>( arc->Shape() ) );
  1472. new_arc->SetWidth( arc->Width() );
  1473. new_arc->SetLayer( ToLAYER_ID( arc->Layers().Start() ) );
  1474. new_arc->SetNet( static_cast<NETINFO_ITEM*>( arc->Net() ) );
  1475. newBI = new_arc;
  1476. break;
  1477. }
  1478. case PNS::ITEM::SEGMENT_T:
  1479. {
  1480. PNS::SEGMENT* seg = static_cast<PNS::SEGMENT*>( aItem );
  1481. PCB_TRACK* track = new PCB_TRACK( m_board );
  1482. const SEG& s = seg->Seg();
  1483. track->SetStart( VECTOR2I( s.A.x, s.A.y ) );
  1484. track->SetEnd( VECTOR2I( s.B.x, s.B.y ) );
  1485. track->SetWidth( seg->Width() );
  1486. track->SetLayer( ToLAYER_ID( seg->Layers().Start() ) );
  1487. track->SetNet( static_cast<NETINFO_ITEM*>( seg->Net() ) );
  1488. newBI = track;
  1489. break;
  1490. }
  1491. case PNS::ITEM::VIA_T:
  1492. {
  1493. PCB_VIA* via_board = new PCB_VIA( m_board );
  1494. PNS::VIA* via = static_cast<PNS::VIA*>( aItem );
  1495. via_board->SetPosition( VECTOR2I( via->Pos().x, via->Pos().y ) );
  1496. via_board->SetWidth( via->Diameter() );
  1497. via_board->SetDrill( via->Drill() );
  1498. via_board->SetNet( static_cast<NETINFO_ITEM*>( via->Net() ) );
  1499. via_board->SetViaType( via->ViaType() ); // MUST be before SetLayerPair()
  1500. via_board->SetIsFree( via->IsFree() );
  1501. via_board->SetLayerPair( ToLAYER_ID( via->Layers().Start() ),
  1502. ToLAYER_ID( via->Layers().End() ) );
  1503. newBI = via_board;
  1504. break;
  1505. }
  1506. case PNS::ITEM::SOLID_T:
  1507. {
  1508. PAD* pad = static_cast<PAD*>( aItem->Parent() );
  1509. VECTOR2I pos = static_cast<PNS::SOLID*>( aItem )->Pos();
  1510. m_fpOffsets[ pad ].p_new = pos;
  1511. return;
  1512. }
  1513. default:
  1514. break;
  1515. }
  1516. if( newBI )
  1517. {
  1518. //newBI->SetLocalRatsnestVisible( m_dispOptions->m_ShowGlobalRatsnest );
  1519. aItem->SetParent( newBI );
  1520. newBI->ClearFlags();
  1521. m_commit->Add( newBI );
  1522. }
  1523. }
  1524. void PNS_KICAD_IFACE::Commit()
  1525. {
  1526. std::set<FOOTPRINT*> processedFootprints;
  1527. EraseView();
  1528. for( const auto& [ pad, fpOffset ] : m_fpOffsets )
  1529. {
  1530. VECTOR2I offset = fpOffset.p_new - fpOffset.p_old;
  1531. FOOTPRINT* footprint = pad->GetParentFootprint();
  1532. VECTOR2I p_orig = footprint->GetPosition();
  1533. VECTOR2I p_new = p_orig + offset;
  1534. if( processedFootprints.find( footprint ) != processedFootprints.end() )
  1535. continue;
  1536. processedFootprints.insert( footprint );
  1537. m_commit->Modify( footprint );
  1538. footprint->SetPosition( p_new );
  1539. }
  1540. m_fpOffsets.clear();
  1541. m_commit->Push( _( "Interactive Router" ), m_commitFlags );
  1542. m_commit = std::make_unique<BOARD_COMMIT>( m_tool );
  1543. }
  1544. EDA_UNITS PNS_KICAD_IFACE::GetUnits() const
  1545. {
  1546. return static_cast<EDA_UNITS>( m_tool->GetManager()->GetSettings()->m_System.units );
  1547. }
  1548. void PNS_KICAD_IFACE::SetView( KIGFX::VIEW* aView )
  1549. {
  1550. wxLogTrace( wxT( "PNS" ), wxT( "SetView %p" ), aView );
  1551. if( m_previewItems )
  1552. {
  1553. m_previewItems->FreeItems();
  1554. delete m_previewItems;
  1555. }
  1556. m_view = aView;
  1557. m_previewItems = new KIGFX::VIEW_GROUP( m_view );
  1558. m_previewItems->SetLayer( LAYER_SELECT_OVERLAY ) ;
  1559. if(m_view)
  1560. m_view->Add( m_previewItems );
  1561. delete m_debugDecorator;
  1562. auto dec = new PNS_PCBNEW_DEBUG_DECORATOR();
  1563. m_debugDecorator = dec;
  1564. dec->SetDebugEnabled( ADVANCED_CFG::GetCfg().m_ShowRouterDebugGraphics );
  1565. if( ADVANCED_CFG::GetCfg().m_ShowRouterDebugGraphics )
  1566. dec->SetView( m_view );
  1567. }
  1568. int PNS_KICAD_IFACE::GetNetCode( PNS::NET_HANDLE aNet ) const
  1569. {
  1570. if( aNet )
  1571. return static_cast<NETINFO_ITEM*>( aNet )->GetNetCode();
  1572. else
  1573. return -1;
  1574. }
  1575. wxString PNS_KICAD_IFACE::GetNetName( PNS::NET_HANDLE aNet ) const
  1576. {
  1577. if( aNet )
  1578. return static_cast<NETINFO_ITEM*>( aNet )->GetNetname();
  1579. else
  1580. return wxEmptyString;
  1581. }
  1582. void PNS_KICAD_IFACE::UpdateNet( PNS::NET_HANDLE aNet )
  1583. {
  1584. wxLogTrace( wxT( "PNS" ), wxT( "Update-net %s" ), GetNetName( aNet ) );
  1585. }
  1586. PNS::RULE_RESOLVER* PNS_KICAD_IFACE_BASE::GetRuleResolver()
  1587. {
  1588. return m_ruleResolver;
  1589. }
  1590. void PNS_KICAD_IFACE::SetHostTool( PCB_TOOL_BASE* aTool )
  1591. {
  1592. m_tool = aTool;
  1593. m_commit = std::make_unique<BOARD_COMMIT>( m_tool );
  1594. }