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.

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