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.

1070 lines
27 KiB

9 years ago
9 years ago
5 years ago
5 years ago
9 years ago
11 years ago
11 years ago
5 years ago
11 years ago
10 years ago
10 years ago
10 years ago
9 years ago
5 years ago
5 years ago
9 years ago
11 years ago
11 years ago
5 years ago
5 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
9 years ago
  1. /*
  2. * KiRouter - a push-and-(sometimes-)shove PCB router
  3. *
  4. * Copyright (C) 2013-2014 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 <cstdio>
  22. #include <memory>
  23. #include <vector>
  24. #include <gal/graphics_abstraction_layer.h>
  25. #include <advanced_config.h>
  26. #include <settings/settings_manager.h>
  27. #include <pcb_painter.h>
  28. #include <pad.h>
  29. #include <zone.h>
  30. #include <geometry/shape.h>
  31. #include "pns_node.h"
  32. #include "pns_line_placer.h"
  33. #include "pns_line.h"
  34. #include "pns_solid.h"
  35. #include "pns_utils.h"
  36. #include "pns_router.h"
  37. #include "pns_shove.h"
  38. #include "pns_dragger.h"
  39. #include "pns_component_dragger.h"
  40. #include "pns_topology.h"
  41. #include "pns_diff_pair_placer.h"
  42. #include "pns_meander_placer.h"
  43. #include "pns_meander_skew_placer.h"
  44. #include "pns_dp_meander_placer.h"
  45. #include "router_preview_item.h"
  46. namespace PNS {
  47. // an ugly singleton for drawing debug items within the router context.
  48. // To be fixed sometime in the future.
  49. static ROUTER* theRouter;
  50. ROUTER::ROUTER()
  51. {
  52. theRouter = this;
  53. m_state = IDLE;
  54. m_mode = PNS_MODE_ROUTE_SINGLE;
  55. m_logger = nullptr;
  56. if( ADVANCED_CFG::GetCfg().m_EnableRouterDump )
  57. m_logger = new LOGGER;
  58. // Initialize all other variables:
  59. m_lastNode = nullptr;
  60. m_iterLimit = 0;
  61. m_settings = nullptr;
  62. m_iface = nullptr;
  63. m_visibleViewArea.SetMaximum();
  64. }
  65. ROUTER* ROUTER::GetInstance()
  66. {
  67. return theRouter;
  68. }
  69. ROUTER::~ROUTER()
  70. {
  71. ClearWorld();
  72. theRouter = nullptr;
  73. delete m_logger;
  74. }
  75. void ROUTER::SyncWorld()
  76. {
  77. ClearWorld();
  78. m_world = std::make_unique<NODE>( );
  79. m_iface->SyncWorld( m_world.get() );
  80. m_world->FixupVirtualVias();
  81. }
  82. void ROUTER::ClearWorld()
  83. {
  84. if( m_world )
  85. {
  86. m_world->SetRuleResolver( nullptr );
  87. m_world->KillChildren();
  88. m_world.reset();
  89. }
  90. m_placer.reset();
  91. }
  92. bool ROUTER::RoutingInProgress() const
  93. {
  94. return m_state != IDLE;
  95. }
  96. const ITEM_SET ROUTER::QueryHoverItems( const VECTOR2I& aP, int aSlopRadius )
  97. {
  98. NODE* node = m_placer ? m_placer->CurrentNode() : m_world.get();
  99. PNS::ITEM_SET ret;
  100. wxCHECK( node, ret );
  101. if( aSlopRadius > 0 )
  102. {
  103. NODE::OBSTACLES obs;
  104. SEGMENT test( SEG( aP, aP ), nullptr );
  105. COLLISION_SEARCH_OPTIONS opts;
  106. test.SetWidth( 1 );
  107. test.SetLayers( PNS_LAYER_RANGE::All() );
  108. opts.m_differentNetsOnly = false;
  109. opts.m_overrideClearance = aSlopRadius;
  110. node->QueryColliding( &test, obs, opts );
  111. for( const OBSTACLE& obstacle : obs )
  112. ret.Add( obstacle.m_item, false );
  113. return ret;
  114. }
  115. else
  116. {
  117. return node->HitTest( aP );
  118. }
  119. }
  120. bool ROUTER::StartDragging( const VECTOR2I& aP, ITEM* aItem, int aDragMode )
  121. {
  122. return StartDragging( aP, ITEM_SET( aItem ), aDragMode );
  123. }
  124. bool ROUTER::StartDragging( const VECTOR2I& aP, ITEM_SET aStartItems, int aDragMode )
  125. {
  126. if( aStartItems.Empty() )
  127. return false;
  128. GetRuleResolver()->ClearCaches();
  129. if( aStartItems.Count( ITEM::SOLID_T ) == aStartItems.Size() )
  130. {
  131. m_dragger = std::make_unique<COMPONENT_DRAGGER>( this );
  132. m_state = DRAG_COMPONENT;
  133. }
  134. else
  135. {
  136. m_dragger = std::make_unique<DRAGGER>( this );
  137. m_state = DRAG_SEGMENT;
  138. }
  139. m_dragger->SetMode( static_cast<PNS::DRAG_MODE>( aDragMode ) );
  140. m_dragger->SetWorld( m_world.get() );
  141. m_dragger->SetLogger( m_logger );
  142. m_dragger->SetDebugDecorator( m_iface->GetDebugDecorator() );
  143. if( m_logger )
  144. m_logger->Clear();
  145. if( m_logger && aStartItems.Size() )
  146. m_logger->Log( LOGGER::EVT_START_DRAG, aP, aStartItems[0] );
  147. if( m_dragger->Start( aP, aStartItems ) )
  148. {
  149. return true;
  150. }
  151. else
  152. {
  153. m_dragger.reset();
  154. m_state = IDLE;
  155. return false;
  156. }
  157. }
  158. bool ROUTER::isStartingPointRoutable( const VECTOR2I& aWhere, ITEM* aStartItem, int aLayer )
  159. {
  160. if( Settings().AllowDRCViolations() )
  161. return true;
  162. if( m_mode == PNS_MODE_ROUTE_DIFF_PAIR )
  163. {
  164. if( m_sizes.DiffPairGap() < m_sizes.MinClearance() )
  165. {
  166. SetFailureReason( _( "Diff pair gap is less than board minimum clearance." ) );
  167. return false;
  168. }
  169. }
  170. ITEM_SET candidates = QueryHoverItems( aWhere );
  171. wxString failureReason;
  172. for( ITEM* item : candidates.Items() )
  173. {
  174. // Edge cuts are put on all layers, but they're not *really* on all layers
  175. if( item->BoardItem() && item->BoardItem()->GetLayer() == Edge_Cuts )
  176. continue;
  177. if( !item->Layers().Overlaps( aLayer ) )
  178. continue;
  179. if( item->IsRoutable() )
  180. {
  181. failureReason = wxEmptyString;
  182. break;
  183. }
  184. else
  185. {
  186. BOARD_ITEM* parent = item->BoardItem();
  187. switch( parent->Type() )
  188. {
  189. case PCB_PAD_T:
  190. {
  191. PAD* pad = static_cast<PAD*>( parent );
  192. if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
  193. failureReason = _( "Cannot start routing from a non-plated hole." );
  194. }
  195. break;
  196. case PCB_ZONE_T:
  197. {
  198. ZONE* zone = static_cast<ZONE*>( parent );
  199. if( !zone->HasKeepoutParametersSet() )
  200. break;
  201. if( !zone->GetZoneName().IsEmpty() )
  202. {
  203. failureReason = wxString::Format( _( "Rule area '%s' disallows tracks." ),
  204. zone->GetZoneName() );
  205. }
  206. else
  207. {
  208. failureReason = _( "Rule area disallows tracks." );
  209. }
  210. }
  211. break;
  212. case PCB_FIELD_T:
  213. case PCB_TEXT_T:
  214. case PCB_TEXTBOX_T:
  215. failureReason = _( "Cannot start routing from a text item." );
  216. break;
  217. default:
  218. break;
  219. }
  220. }
  221. }
  222. if( !failureReason.IsEmpty() )
  223. {
  224. SetFailureReason( failureReason );
  225. return false;
  226. }
  227. VECTOR2I startPoint = aWhere;
  228. if( m_mode == PNS_MODE_ROUTE_SINGLE )
  229. {
  230. SHAPE_LINE_CHAIN dummyStartSeg;
  231. LINE dummyStartLine;
  232. dummyStartSeg.Append( startPoint );
  233. dummyStartSeg.Append( startPoint, true );
  234. dummyStartLine.SetShape( dummyStartSeg );
  235. dummyStartLine.SetLayer( aLayer );
  236. dummyStartLine.SetNet( aStartItem ? aStartItem->Net() : 0 );
  237. dummyStartLine.SetWidth( m_sizes.TrackWidth() );
  238. if( m_world->CheckColliding( &dummyStartLine, ITEM::ANY_T ) )
  239. {
  240. // If the only reason we collide is track width; it's better to allow the user to start
  241. // anyway and just highlight the resulting collisions, so they can change width later.
  242. dummyStartLine.SetWidth( m_sizes.BoardMinTrackWidth() );
  243. if( m_world->CheckColliding( &dummyStartLine, ITEM::ANY_T ) )
  244. {
  245. ITEM_SET dummyStartSet( &dummyStartLine );
  246. NODE::ITEM_VECTOR highlightedItems;
  247. markViolations( m_world.get(), dummyStartSet, highlightedItems );
  248. for( ITEM* item : highlightedItems )
  249. m_iface->HideItem( item );
  250. SetFailureReason( _( "The routing start point violates DRC." ) );
  251. return false;
  252. }
  253. }
  254. }
  255. else if( m_mode == PNS_MODE_ROUTE_DIFF_PAIR )
  256. {
  257. if( !aStartItem )
  258. {
  259. SetFailureReason( _( "Cannot start a differential pair in the middle of nowhere." ) );
  260. return false;
  261. }
  262. DP_PRIMITIVE_PAIR dpPair;
  263. wxString errorMsg;
  264. if( !DIFF_PAIR_PLACER::FindDpPrimitivePair( m_world.get(), startPoint, aStartItem, dpPair,
  265. &errorMsg ) )
  266. {
  267. SetFailureReason( errorMsg );
  268. return false;
  269. }
  270. SHAPE_LINE_CHAIN dummyStartSegA;
  271. SHAPE_LINE_CHAIN dummyStartSegB;
  272. LINE dummyStartLineA;
  273. LINE dummyStartLineB;
  274. dummyStartSegA.Append( dpPair.AnchorN() );
  275. dummyStartSegA.Append( dpPair.AnchorN(), true );
  276. dummyStartSegB.Append( dpPair.AnchorP() );
  277. dummyStartSegB.Append( dpPair.AnchorP(), true );
  278. dummyStartLineA.SetShape( dummyStartSegA );
  279. dummyStartLineA.SetLayer( aLayer );
  280. dummyStartLineA.SetNet( dpPair.PrimN()->Net() );
  281. dummyStartLineA.SetWidth( m_sizes.DiffPairWidth() );
  282. dummyStartLineB.SetShape( dummyStartSegB );
  283. dummyStartLineB.SetLayer( aLayer );
  284. dummyStartLineB.SetNet( dpPair.PrimP()->Net() );
  285. dummyStartLineB.SetWidth( m_sizes.DiffPairWidth() );
  286. if( m_world->CheckColliding( &dummyStartLineA, ITEM::ANY_T )
  287. || m_world->CheckColliding( &dummyStartLineB, ITEM::ANY_T ) )
  288. {
  289. // If the only reason we collide is track width; it's better to allow the user to start
  290. // anyway and just highlight the resulting collisions, so they can change width later.
  291. dummyStartLineA.SetWidth( m_sizes.BoardMinTrackWidth() );
  292. dummyStartLineB.SetWidth( m_sizes.BoardMinTrackWidth() );
  293. if( m_world->CheckColliding( &dummyStartLineA, ITEM::ANY_T )
  294. || m_world->CheckColliding( &dummyStartLineB, ITEM::ANY_T ) )
  295. {
  296. ITEM_SET dummyStartSet;
  297. NODE::ITEM_VECTOR highlightedItems;
  298. dummyStartSet.Add( dummyStartLineA );
  299. dummyStartSet.Add( dummyStartLineB );
  300. markViolations( m_world.get(), dummyStartSet, highlightedItems );
  301. for( ITEM* item : highlightedItems )
  302. m_iface->HideItem( item );
  303. SetFailureReason( _( "The routing start point violates DRC." ) );
  304. return false;
  305. }
  306. }
  307. }
  308. return true;
  309. }
  310. bool ROUTER::StartRouting( const VECTOR2I& aP, ITEM* aStartItem, int aLayer )
  311. {
  312. GetRuleResolver()->ClearCaches();
  313. if( !isStartingPointRoutable( aP, aStartItem, aLayer ) )
  314. return false;
  315. switch( m_mode )
  316. {
  317. case PNS_MODE_ROUTE_SINGLE:
  318. m_placer = std::make_unique<LINE_PLACER>( this );
  319. break;
  320. case PNS_MODE_ROUTE_DIFF_PAIR:
  321. m_placer = std::make_unique<DIFF_PAIR_PLACER>( this );
  322. break;
  323. case PNS_MODE_TUNE_SINGLE:
  324. m_placer = std::make_unique<MEANDER_PLACER>( this );
  325. break;
  326. case PNS_MODE_TUNE_DIFF_PAIR:
  327. m_placer = std::make_unique<DP_MEANDER_PLACER>( this );
  328. break;
  329. case PNS_MODE_TUNE_DIFF_PAIR_SKEW:
  330. m_placer = std::make_unique<MEANDER_SKEW_PLACER>( this );
  331. break;
  332. default:
  333. return false;
  334. }
  335. m_placer->UpdateSizes( m_sizes );
  336. m_placer->SetLayer( aLayer );
  337. m_placer->SetDebugDecorator( m_iface->GetDebugDecorator() );
  338. m_placer->SetLogger( m_logger );
  339. if( m_placer->Start( aP, aStartItem ) )
  340. {
  341. m_state = ROUTE_TRACK;
  342. if( m_logger )
  343. {
  344. m_logger->Clear();
  345. m_logger->Log( LOGGER::EVT_START_ROUTE, aP, aStartItem, &m_sizes );
  346. }
  347. return true;
  348. }
  349. else
  350. {
  351. m_state = IDLE;
  352. m_placer.reset();
  353. return false;
  354. }
  355. }
  356. bool ROUTER::Move( const VECTOR2I& aP, ITEM* endItem )
  357. {
  358. if( m_logger )
  359. m_logger->Log( LOGGER::EVT_MOVE, aP, endItem );
  360. switch( m_state )
  361. {
  362. case ROUTE_TRACK:
  363. return movePlacing( aP, endItem );
  364. case DRAG_SEGMENT:
  365. case DRAG_COMPONENT:
  366. return moveDragging( aP, endItem );
  367. default:
  368. break;
  369. }
  370. GetRuleResolver()->ClearTemporaryCaches();
  371. return false;
  372. }
  373. bool ROUTER::getNearestRatnestAnchor( VECTOR2I& aOtherEnd, PNS_LAYER_RANGE& aOtherEndLayers,
  374. ITEM*& aOtherEndItem )
  375. {
  376. // Can't finish something with no connections
  377. if( GetCurrentNets().empty() )
  378. return false;
  379. PLACEMENT_ALGO* placer = Placer();
  380. if( placer == nullptr || placer->Traces().Size() == 0 )
  381. return false;
  382. LINE* trace = dynamic_cast<LINE*>( placer->Traces()[0] );
  383. if( trace == nullptr )
  384. return false;
  385. PNS::NODE* lastNode = placer->CurrentNode( true );
  386. PNS::TOPOLOGY topo( lastNode );
  387. // If the user has drawn a line, get the anchor nearest to the line end
  388. if( trace->SegmentCount() > 0 )
  389. {
  390. return topo.NearestUnconnectedAnchorPoint( trace, aOtherEnd, aOtherEndLayers,
  391. aOtherEndItem );
  392. }
  393. // Otherwise, find the closest anchor to our start point
  394. // Get joint from placer start item
  395. const JOINT* jt = lastNode->FindJoint( placer->CurrentStart(), placer->CurrentLayer(),
  396. placer->CurrentNets()[0] );
  397. if( !jt )
  398. return false;
  399. // Get unconnected item from joint
  400. int anchor;
  401. PNS::ITEM* it = topo.NearestUnconnectedItem( jt, &anchor );
  402. if( !it )
  403. return false;
  404. aOtherEnd = it->Anchor( anchor );
  405. aOtherEndLayers = it->Layers();
  406. aOtherEndItem = it;
  407. return true;
  408. }
  409. bool ROUTER::Finish()
  410. {
  411. if( m_state != ROUTE_TRACK )
  412. return false;
  413. PLACEMENT_ALGO* placer = Placer();
  414. if( placer == nullptr || placer->Traces().Size() == 0 )
  415. return false;
  416. LINE* current = dynamic_cast<LINE*>( placer->Traces()[0] );
  417. if( current == nullptr )
  418. return false;
  419. // Get our current line and position and nearest ratsnest to them if it exists
  420. VECTOR2I otherEnd;
  421. PNS_LAYER_RANGE otherEndLayers;
  422. ITEM* otherEndItem = nullptr;
  423. // Get the anchor nearest to the end of the trace the user is routing
  424. if( !getNearestRatnestAnchor( otherEnd, otherEndLayers, otherEndItem ) )
  425. return false;
  426. // Keep moving until we don't change position or hit the limit
  427. int triesLeft = 5;
  428. VECTOR2I moveResultPoint;
  429. do
  430. {
  431. moveResultPoint = placer->CurrentEnd();
  432. Move( otherEnd, otherEndItem );
  433. triesLeft--;
  434. } while( placer->CurrentEnd() != moveResultPoint && triesLeft );
  435. // If we've made it, fix the route and we're done
  436. if( moveResultPoint == otherEnd && otherEndLayers.Overlaps( GetCurrentLayer() ) )
  437. {
  438. bool forceFinish = false;
  439. bool allowViolations = false;
  440. return FixRoute( otherEnd, otherEndItem, forceFinish, allowViolations );
  441. }
  442. return false;
  443. }
  444. bool ROUTER::ContinueFromEnd( ITEM** aNewStartItem )
  445. {
  446. PLACEMENT_ALGO* placer = Placer();
  447. if( placer == nullptr || placer->Traces().Size() == 0 )
  448. return false;
  449. LINE* current = dynamic_cast<LINE*>( placer->Traces()[0] );
  450. if( current == nullptr )
  451. return false;
  452. int currentLayer = GetCurrentLayer();
  453. VECTOR2I currentEnd = placer->CurrentEnd();
  454. VECTOR2I otherEnd;
  455. PNS_LAYER_RANGE otherEndLayers;
  456. ITEM* otherEndItem = nullptr;
  457. // Get the anchor nearest to the end of the trace the user is routing
  458. if( !getNearestRatnestAnchor( otherEnd, otherEndLayers, otherEndItem ) )
  459. return false;
  460. CommitRouting();
  461. // Commit whatever we've fixed and restart routing from the other end
  462. int nextLayer = otherEndLayers.Overlaps( currentLayer ) ? currentLayer : otherEndLayers.Start();
  463. if( !StartRouting( otherEnd, otherEndItem, nextLayer ) )
  464. return false;
  465. // Attempt to route to our current position
  466. Move( currentEnd, nullptr );
  467. *aNewStartItem = otherEndItem;
  468. return true;
  469. }
  470. bool ROUTER::moveDragging( const VECTOR2I& aP, ITEM* aEndItem )
  471. {
  472. m_iface->EraseView();
  473. bool ret = m_dragger->Drag( aP );
  474. ITEM_SET dragged = m_dragger->Traces();
  475. updateView( m_dragger->CurrentNode(), dragged, true );
  476. return ret;
  477. }
  478. void ROUTER::markViolations( NODE* aNode, ITEM_SET& aCurrent, NODE::ITEM_VECTOR& aRemoved )
  479. {
  480. auto updateItem =
  481. [&]( ITEM* currentItem, ITEM* itemToMark )
  482. {
  483. std::unique_ptr<ITEM> tmp( itemToMark->Clone() );
  484. int clearance;
  485. bool removeOriginal = true;
  486. clearance = aNode->GetClearance( currentItem, itemToMark );
  487. if( itemToMark->Layers().IsMultilayer() && !currentItem->Layers().IsMultilayer() )
  488. tmp->SetLayer( currentItem->Layer() );
  489. if( itemToMark->IsCompoundShapePrimitive() )
  490. {
  491. // We're only highlighting one (or more) of several primitives so we don't
  492. // want all the other parts of the object to disappear
  493. removeOriginal = false;
  494. }
  495. m_iface->DisplayItem( tmp.get(), clearance );
  496. if( removeOriginal )
  497. aRemoved.push_back( itemToMark );
  498. };
  499. for( ITEM* item : aCurrent.Items() )
  500. {
  501. NODE::OBSTACLES obstacles;
  502. aNode->QueryColliding( item, obstacles );
  503. if( item->OfKind( ITEM::LINE_T ) )
  504. {
  505. LINE* l = static_cast<LINE*>( item );
  506. if( l->EndsWithVia() )
  507. {
  508. VIA v( l->Via() );
  509. aNode->QueryColliding( &v, obstacles );
  510. }
  511. }
  512. ITEM_SET draggedItems;
  513. if( GetDragger() )
  514. draggedItems = GetDragger()->Traces();
  515. for( const OBSTACLE& obs : obstacles )
  516. {
  517. // Don't mark items being dragged; only board items they collide with
  518. if( draggedItems.Contains( obs.m_item ) )
  519. continue;
  520. obs.m_item->Mark( obs.m_item->Marker() | MK_VIOLATION );
  521. updateItem( item, obs.m_item );
  522. }
  523. if( item->Kind() == ITEM::LINE_T )
  524. {
  525. LINE* line = static_cast<LINE*>( item );
  526. // Show clearance on any blocking obstacles
  527. if( line->GetBlockingObstacle() )
  528. updateItem( item, line->GetBlockingObstacle() );
  529. }
  530. }
  531. }
  532. void ROUTER::updateView( NODE* aNode, ITEM_SET& aCurrent, bool aDragging )
  533. {
  534. NODE::ITEM_VECTOR removed, added;
  535. NODE::OBSTACLES obstacles;
  536. if( !aNode )
  537. return;
  538. markViolations( aNode, aCurrent, removed );
  539. aNode->GetUpdatedItems( removed, added );
  540. std::vector<const PNS::ITEM*> cacheCheckItems( added.begin(), added.end() );
  541. GetRuleResolver()->ClearCacheForItems( cacheCheckItems );
  542. for( ITEM* item : added )
  543. {
  544. int clearance = GetRuleResolver()->Clearance( item, nullptr );
  545. m_iface->DisplayItem( item, clearance, aDragging );
  546. }
  547. for( ITEM* item : removed )
  548. m_iface->HideItem( item );
  549. }
  550. void ROUTER::UpdateSizes( const SIZES_SETTINGS& aSizes )
  551. {
  552. m_sizes = aSizes;
  553. // Change track/via size settings
  554. if( m_state == ROUTE_TRACK )
  555. m_placer->UpdateSizes( m_sizes );
  556. }
  557. bool ROUTER::movePlacing( const VECTOR2I& aP, ITEM* aEndItem )
  558. {
  559. m_iface->EraseView();
  560. bool ret = m_placer->Move( aP, aEndItem );
  561. ITEM_SET current = m_placer->Traces();
  562. for( const ITEM* item : current.CItems() )
  563. {
  564. if( !item->OfKind( ITEM::LINE_T ) )
  565. continue;
  566. const LINE* l = static_cast<const LINE*>( item );
  567. int clearance = GetRuleResolver()->Clearance( item, nullptr );
  568. m_iface->DisplayItem( l, clearance, false, PNS_HEAD_TRACE );
  569. if( l->EndsWithVia() )
  570. {
  571. const VIA& via = l->Via();
  572. clearance = GetRuleResolver()->Clearance( &via, nullptr );
  573. if( via.HasHole() )
  574. {
  575. int holeClearance = GetRuleResolver()->Clearance( via.Hole(), nullptr );
  576. int annularWidth = std::max( 0, via.Diameter() - via.Drill() ) / 2;
  577. int excessHoleClearance = holeClearance - annularWidth;
  578. if( excessHoleClearance > clearance )
  579. clearance = excessHoleClearance;
  580. }
  581. m_iface->DisplayItem( &l->Via(), clearance, false, PNS_HEAD_TRACE );
  582. }
  583. }
  584. //ITEM_SET tmp( &current );
  585. updateView( m_placer->CurrentNode( true ), current );
  586. return ret;
  587. }
  588. void ROUTER::GetUpdatedItems( std::vector<PNS::ITEM*>& aRemoved, std::vector<PNS::ITEM*>& aAdded,
  589. std::vector<PNS::ITEM*>& aHeads )
  590. {
  591. NODE *node = nullptr;
  592. ITEM_SET current;
  593. if( m_state == ROUTE_TRACK )
  594. {
  595. node = m_placer->CurrentNode( true );
  596. current = m_placer->Traces();
  597. }
  598. else if ( m_state == DRAG_SEGMENT )
  599. {
  600. node = m_dragger->CurrentNode();
  601. current = m_dragger->Traces();
  602. }
  603. // There probably should be a debugging assertion and possibly a PNS_LOGGER call here but
  604. // I'm not sure how to be proceed WLS.
  605. if( !node )
  606. return;
  607. node->GetUpdatedItems( aRemoved, aAdded );
  608. for( const ITEM* item : current.CItems() )
  609. aHeads.push_back( item->Clone() );
  610. }
  611. void ROUTER::CommitRouting( NODE* aNode )
  612. {
  613. if( m_state == ROUTE_TRACK && !m_placer->HasPlacedAnything() )
  614. return;
  615. NODE::ITEM_VECTOR removed;
  616. NODE::ITEM_VECTOR added;
  617. NODE::ITEM_VECTOR changed;
  618. aNode->GetUpdatedItems( removed, added );
  619. for( ITEM* item : removed )
  620. {
  621. bool is_changed = false;
  622. // Items in remove/add that share the same parent are just updated versions
  623. // We move them to the updated vector to preserve attributes such as UUID and pad data
  624. if( item->Parent() )
  625. {
  626. for( NODE::ITEM_VECTOR::iterator added_it = added.begin();
  627. added_it != added.end(); ++added_it )
  628. {
  629. if( ( *added_it )->Parent() && ( *added_it )->Parent() == item->Parent() )
  630. {
  631. changed.push_back( *added_it );
  632. added.erase( added_it );
  633. is_changed = true;
  634. break;
  635. }
  636. }
  637. }
  638. if( !is_changed && !item->IsVirtual() )
  639. m_iface->RemoveItem( item );
  640. }
  641. for( ITEM* item : added )
  642. {
  643. if( !item->IsVirtual() )
  644. m_iface->AddItem( item );
  645. }
  646. for( ITEM* item : changed )
  647. {
  648. if( !item->IsVirtual() )
  649. m_iface->UpdateItem( item );
  650. }
  651. m_iface->Commit();
  652. m_world->Commit( aNode );
  653. }
  654. bool ROUTER::FixRoute( const VECTOR2I& aP, ITEM* aEndItem, bool aForceFinish, bool aForceCommit )
  655. {
  656. bool rv = false;
  657. if( m_logger )
  658. m_logger->Log( LOGGER::EVT_FIX, aP, aEndItem );
  659. switch( m_state )
  660. {
  661. case ROUTE_TRACK:
  662. rv = m_placer->FixRoute( aP, aEndItem, aForceFinish );
  663. break;
  664. case DRAG_SEGMENT:
  665. case DRAG_COMPONENT:
  666. rv = m_dragger->FixRoute( aForceCommit );
  667. break;
  668. default:
  669. break;
  670. }
  671. return rv;
  672. }
  673. std::optional<VECTOR2I> ROUTER::UndoLastSegment()
  674. {
  675. if( !RoutingInProgress() )
  676. return std::nullopt;
  677. if( m_logger )
  678. m_logger->Log( LOGGER::EVT_UNFIX );
  679. return m_placer->UnfixRoute();
  680. }
  681. void ROUTER::CommitRouting()
  682. {
  683. if( m_state == ROUTE_TRACK )
  684. m_placer->CommitPlacement();
  685. StopRouting();
  686. }
  687. void ROUTER::StopRouting()
  688. {
  689. // Update the ratsnest with new changes
  690. if( m_placer )
  691. {
  692. std::vector<NET_HANDLE> nets;
  693. m_placer->GetModifiedNets( nets );
  694. // Update the ratsnest with new changes
  695. for( NET_HANDLE n : nets )
  696. m_iface->UpdateNet( n );
  697. }
  698. if( !RoutingInProgress() )
  699. return;
  700. m_placer.reset();
  701. m_dragger.reset();
  702. m_iface->EraseView();
  703. m_state = IDLE;
  704. m_world->KillChildren();
  705. m_world->ClearRanks();
  706. }
  707. void ROUTER::ClearViewDecorations()
  708. {
  709. m_iface->EraseView();
  710. }
  711. void ROUTER::FlipPosture()
  712. {
  713. if( m_state == ROUTE_TRACK )
  714. {
  715. m_placer->FlipPosture();
  716. }
  717. }
  718. bool ROUTER::SwitchLayer( int aLayer )
  719. {
  720. if( m_state == ROUTE_TRACK )
  721. return m_placer->SetLayer( aLayer );
  722. return false;
  723. }
  724. void ROUTER::ToggleViaPlacement()
  725. {
  726. if( m_state == ROUTE_TRACK )
  727. {
  728. bool toggle = !m_placer->IsPlacingVia();
  729. m_placer->ToggleVia( toggle );
  730. if( m_logger )
  731. m_logger->Log( LOGGER::EVT_TOGGLE_VIA, VECTOR2I(), nullptr, &m_sizes );
  732. }
  733. }
  734. const std::vector<NET_HANDLE> ROUTER::GetCurrentNets() const
  735. {
  736. if( m_placer )
  737. return m_placer->CurrentNets();
  738. else if( m_dragger )
  739. return m_dragger->CurrentNets();
  740. return std::vector<NET_HANDLE>();
  741. }
  742. int ROUTER::GetCurrentLayer() const
  743. {
  744. if( m_placer )
  745. return m_placer->CurrentLayer();
  746. else if( m_dragger )
  747. return m_dragger->CurrentLayer();
  748. return -1;
  749. }
  750. LOGGER* ROUTER::Logger()
  751. {
  752. return m_logger;
  753. }
  754. bool ROUTER::IsPlacingVia() const
  755. {
  756. if( !m_placer )
  757. return false;
  758. return m_placer->IsPlacingVia();
  759. }
  760. void ROUTER::ToggleCornerMode()
  761. {
  762. DIRECTION_45::CORNER_MODE mode = m_settings->GetCornerMode();
  763. switch( m_settings->GetCornerMode() )
  764. {
  765. case DIRECTION_45::CORNER_MODE::MITERED_45: mode = DIRECTION_45::CORNER_MODE::ROUNDED_45; break;
  766. case DIRECTION_45::CORNER_MODE::ROUNDED_45: mode = DIRECTION_45::CORNER_MODE::MITERED_90; break;
  767. case DIRECTION_45::CORNER_MODE::MITERED_90: mode = DIRECTION_45::CORNER_MODE::ROUNDED_90; break;
  768. case DIRECTION_45::CORNER_MODE::ROUNDED_90: mode = DIRECTION_45::CORNER_MODE::MITERED_45; break;
  769. }
  770. m_settings->SetCornerMode( mode );
  771. }
  772. void ROUTER::SetOrthoMode( bool aEnable )
  773. {
  774. if( !m_placer )
  775. return;
  776. m_placer->SetOrthoMode( aEnable );
  777. }
  778. void ROUTER::SetMode( ROUTER_MODE aMode )
  779. {
  780. m_mode = aMode;
  781. }
  782. void ROUTER::SetInterface( ROUTER_IFACE *aIface )
  783. {
  784. m_iface = aIface;
  785. }
  786. void ROUTER::BreakSegmentOrArc( ITEM *aItem, const VECTOR2I& aP )
  787. {
  788. NODE *node = m_world->Branch();
  789. LINE_PLACER placer( this );
  790. bool ret = false;
  791. if( aItem->OfKind( ITEM::SEGMENT_T ) )
  792. ret = placer.SplitAdjacentSegments( node, aItem, aP );
  793. else if( aItem->OfKind( ITEM::ARC_T ) )
  794. ret = placer.SplitAdjacentArcs( node, aItem, aP );
  795. if( ret )
  796. {
  797. CommitRouting( node );
  798. }
  799. else
  800. {
  801. delete node;
  802. }
  803. }
  804. }