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.

673 lines
15 KiB

11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
11 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
10 years ago
11 years ago
10 years ago
11 years ago
10 years ago
10 years ago
10 years ago
11 years ago
10 years ago
10 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
  1. /*
  2. * KiRouter - a push-and-(sometimes-)shove PCB router
  3. *
  4. * Copyright (C) 2013-2014 CERN
  5. * Copyright (C) 2016-2019 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 "pns_arc.h"
  22. #include "pns_dragger.h"
  23. #include "pns_shove.h"
  24. #include "pns_router.h"
  25. #include "pns_debug_decorator.h"
  26. #include "pns_walkaround.h"
  27. namespace PNS {
  28. DRAGGER::DRAGGER( ROUTER* aRouter ) :
  29. DRAG_ALGO( aRouter )
  30. {
  31. m_world = NULL;
  32. m_lastNode = NULL;
  33. m_mode = DM_SEGMENT;
  34. m_draggedSegmentIndex = 0;
  35. m_dragStatus = false;
  36. m_currentMode = RM_MarkObstacles;
  37. m_freeAngleMode = false;
  38. }
  39. DRAGGER::~DRAGGER()
  40. {
  41. }
  42. bool DRAGGER::propagateViaForces( NODE* node, std::set<VIA*>& vias )
  43. {
  44. VIA* via = *vias.begin();
  45. VECTOR2I force;
  46. VECTOR2I lead = m_mouseTrailTracer.GetTrailLeadVector();
  47. bool solidsOnly = false;// ( m_currentMode != RM_Walkaround );
  48. if( via->PushoutForce( node, lead, force, solidsOnly, 40 ) )
  49. {
  50. via->SetPos( via->Pos() + force );
  51. return true;
  52. }
  53. return false;
  54. }
  55. bool DRAGGER::startDragSegment( const VECTOR2D& aP, SEGMENT* aSeg )
  56. {
  57. int w2 = aSeg->Width() / 2;
  58. m_draggedLine = m_world->AssembleLine( aSeg, &m_draggedSegmentIndex );
  59. if( m_shove )
  60. {
  61. m_shove->SetInitialLine( m_draggedLine );
  62. }
  63. auto distA = ( aP - aSeg->Seg().A ).EuclideanNorm();
  64. auto distB = ( aP - aSeg->Seg().B ).EuclideanNorm();
  65. if( distA <= w2 )
  66. {
  67. m_mode = DM_CORNER;
  68. }
  69. else if( distB <= w2 )
  70. {
  71. //todo (snh) Adjust segment for arcs
  72. m_draggedSegmentIndex++;
  73. m_mode = DM_CORNER;
  74. }
  75. else if ( m_freeAngleMode )
  76. {
  77. if( distB < distA )
  78. {
  79. m_draggedSegmentIndex++;
  80. }
  81. m_mode = DM_CORNER;
  82. }
  83. else
  84. {
  85. m_mode = DM_SEGMENT;
  86. }
  87. return true;
  88. }
  89. bool DRAGGER::startDragArc( const VECTOR2D& aP, ARC* aArc )
  90. {
  91. m_draggedLine = m_world->AssembleLine( aArc, &m_draggedSegmentIndex );
  92. m_shove->SetInitialLine( m_draggedLine );
  93. m_mode = DM_ARC;
  94. return true;
  95. }
  96. bool DRAGGER::startDragVia( VIA* aVia )
  97. {
  98. m_initialVia = aVia->MakeHandle();
  99. m_draggedVia = m_initialVia;
  100. m_mode = DM_VIA;
  101. return true;
  102. }
  103. const ITEM_SET DRAGGER::findViaFanoutByHandle ( NODE *aNode, const VIA_HANDLE& handle )
  104. {
  105. ITEM_SET rv;
  106. JOINT* jt = aNode->FindJoint( handle.pos, handle.layers.Start(), handle.net );
  107. if( !jt )
  108. return rv;
  109. for( ITEM* item : jt->LinkList() )
  110. {
  111. if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
  112. {
  113. int segIndex;
  114. LINKED_ITEM* seg = ( LINKED_ITEM*) item;
  115. LINE l = aNode->AssembleLine( seg, &segIndex );
  116. if( segIndex != 0 )
  117. l.Reverse();
  118. rv.Add( l );
  119. }
  120. else if( item->OfKind( ITEM::VIA_T ) )
  121. {
  122. rv.Add( item );
  123. }
  124. }
  125. return rv;
  126. }
  127. bool DRAGGER::Start( const VECTOR2I& aP, ITEM_SET& aPrimitives )
  128. {
  129. if( aPrimitives.Empty() )
  130. return false;
  131. ITEM* startItem = aPrimitives[0];
  132. m_lastNode = NULL;
  133. m_draggedItems.Clear();
  134. m_currentMode = Settings().Mode();
  135. m_freeAngleMode = (m_mode & DM_FREE_ANGLE);
  136. m_lastValidPoint = aP;
  137. m_mouseTrailTracer.Clear();
  138. m_mouseTrailTracer.AddTrailPoint( aP );
  139. if( m_currentMode == RM_Shove && !m_freeAngleMode )
  140. {
  141. m_shove = std::make_unique<SHOVE>( m_world, Router() );
  142. m_shove->SetLogger( Logger() );
  143. m_shove->SetDebugDecorator( Dbg() );
  144. }
  145. startItem->Unmark( MK_LOCKED );
  146. wxLogTrace( "PNS", "StartDragging: item %p [kind %d]", startItem, (int) startItem->Kind() );
  147. switch( startItem->Kind() )
  148. {
  149. case ITEM::SEGMENT_T:
  150. return startDragSegment( aP, static_cast<SEGMENT*>( startItem ) );
  151. case ITEM::VIA_T:
  152. return startDragVia( static_cast<VIA*>( startItem ) );
  153. case ITEM::ARC_T:
  154. return startDragArc( aP, static_cast<ARC*>( startItem ) );
  155. default:
  156. return false;
  157. }
  158. }
  159. void DRAGGER::SetMode( int aMode )
  160. {
  161. m_mode = aMode;
  162. }
  163. bool DRAGGER::dragMarkObstacles( const VECTOR2I& aP )
  164. {
  165. // fixme: rewrite using shared_ptr...
  166. if( m_lastNode )
  167. {
  168. delete m_lastNode;
  169. m_lastNode = nullptr;
  170. }
  171. m_lastNode = m_world->Branch();
  172. switch( m_mode )
  173. {
  174. case DM_SEGMENT:
  175. case DM_CORNER:
  176. {
  177. //TODO: Make threshhold configurable
  178. int thresh = Settings().SmoothDraggedSegments() ? m_draggedLine.Width() / 4 : 0;
  179. LINE origLine( m_draggedLine );
  180. LINE dragged( m_draggedLine );
  181. dragged.SetSnapThreshhold( thresh );
  182. dragged.ClearLinks();
  183. if( m_mode == DM_SEGMENT )
  184. dragged.DragSegment( aP, m_draggedSegmentIndex );
  185. else
  186. dragged.DragCorner( aP, m_draggedSegmentIndex, m_freeAngleMode );
  187. m_lastNode->Remove( origLine );
  188. m_lastNode->Add( dragged );
  189. m_draggedItems.Clear();
  190. m_draggedItems.Add( dragged );
  191. break;
  192. }
  193. case DM_VIA: // fixme...
  194. {
  195. dragViaMarkObstacles( m_initialVia, m_lastNode, aP );
  196. break;
  197. }
  198. }
  199. if( Settings().CanViolateDRC() )
  200. m_dragStatus = true;
  201. else
  202. m_dragStatus = !m_world->CheckColliding( m_draggedItems );
  203. return true;
  204. }
  205. bool DRAGGER::dragViaMarkObstacles( const VIA_HANDLE& aHandle, NODE* aNode, const VECTOR2I& aP )
  206. {
  207. m_draggedItems.Clear();
  208. ITEM_SET fanout = findViaFanoutByHandle( aNode, aHandle );
  209. if( fanout.Empty() )
  210. {
  211. return true;
  212. }
  213. for( ITEM* item : fanout.Items() )
  214. {
  215. if( const LINE* l = dyn_cast<const LINE*>( item ) )
  216. {
  217. LINE origLine( *l );
  218. LINE draggedLine( *l );
  219. draggedLine.DragCorner( aP, origLine.CLine().Find( aHandle.pos ), m_freeAngleMode );
  220. draggedLine.ClearLinks();
  221. m_draggedItems.Add( draggedLine );
  222. m_lastNode->Remove( origLine );
  223. m_lastNode->Add( draggedLine );
  224. }
  225. else if ( VIA *via = dyn_cast<VIA*>( item ) )
  226. {
  227. auto nvia = Clone( *via );
  228. nvia->SetPos( aP );
  229. m_draggedItems.Add( nvia.get() );
  230. m_lastNode->Remove( via );
  231. m_lastNode->Add( std::move( nvia ) );
  232. }
  233. }
  234. return true;
  235. }
  236. bool DRAGGER::dragViaWalkaround( const VIA_HANDLE& aHandle, NODE* aNode, const VECTOR2I& aP )
  237. {
  238. m_draggedItems.Clear();
  239. ITEM_SET fanout = findViaFanoutByHandle( aNode, aHandle );
  240. if( fanout.Empty() )
  241. {
  242. return true;
  243. }
  244. bool viaPropOk = false;
  245. VECTOR2I viaTargetPos;
  246. for( ITEM* item : fanout.Items() )
  247. {
  248. if ( VIA *via = dyn_cast<VIA*>( item ) )
  249. {
  250. auto draggedVia = Clone( *via );
  251. draggedVia->SetPos( aP );
  252. m_draggedItems.Add( draggedVia.get() );
  253. std::set<VIA*> vias;
  254. vias.insert( draggedVia.get() );
  255. bool ok = propagateViaForces( m_lastNode, vias );
  256. if( ok )
  257. {
  258. viaTargetPos = draggedVia->Pos();
  259. viaPropOk = true;
  260. m_lastNode->Remove( via );
  261. m_lastNode->Add( std::move(draggedVia) );
  262. }
  263. }
  264. }
  265. if( !viaPropOk ) // can't force-propagate the via? bummer...
  266. return false;
  267. for( ITEM* item : fanout.Items() )
  268. {
  269. if( const LINE* l = dyn_cast<const LINE*>( item ) )
  270. {
  271. LINE origLine( *l );
  272. LINE draggedLine( *l );
  273. LINE walkLine( *l );
  274. draggedLine.DragCorner( viaTargetPos, origLine.CLine().Find( aHandle.pos ), m_freeAngleMode );
  275. draggedLine.ClearLinks();
  276. if ( m_world->CheckColliding( &draggedLine ) )
  277. {
  278. bool ok = tryWalkaround( m_lastNode, draggedLine, walkLine );
  279. if( !ok )
  280. return false;
  281. m_lastNode->Remove( origLine );
  282. optimizeAndUpdateDraggedLine( walkLine, origLine, aP );
  283. }
  284. else
  285. {
  286. m_draggedItems.Add( draggedLine );
  287. m_lastNode->Remove( origLine );
  288. m_lastNode->Add( draggedLine );
  289. }
  290. }
  291. }
  292. return true;
  293. }
  294. void DRAGGER::optimizeAndUpdateDraggedLine( LINE& aDragged, const LINE& aOrig, const VECTOR2I& aP )
  295. {
  296. VECTOR2D lockV;
  297. aDragged.ClearLinks();
  298. aDragged.Unmark();
  299. lockV = aDragged.CLine().NearestPoint( aP );
  300. if( Settings().GetOptimizeDraggedTrack() )
  301. {
  302. OPTIMIZER optimizer( m_lastNode );
  303. optimizer.SetEffortLevel( OPTIMIZER::MERGE_SEGMENTS | OPTIMIZER::KEEP_TOPOLOGY );
  304. OPT_BOX2I affectedArea = aDragged.ChangedArea( &aOrig );
  305. VECTOR2I anchor( aP );
  306. if( aDragged.CLine().Find( aP ) < 0 )
  307. {
  308. anchor = aDragged.CLine().NearestPoint( aP );
  309. }
  310. optimizer.SetPreserveVertex( anchor );
  311. if( affectedArea )
  312. {
  313. Dbg()->AddPoint( anchor, 3 );
  314. Dbg()->AddBox( *affectedArea, 2 );
  315. optimizer.SetRestrictArea( *affectedArea );
  316. optimizer.Optimize( &aDragged );
  317. OPT_BOX2I optArea = *aDragged.ChangedArea( &aOrig );
  318. if( optArea )
  319. Dbg()->AddBox( *optArea, 4 );
  320. }
  321. }
  322. m_lastNode->Add( aDragged );
  323. m_draggedItems.Clear();
  324. m_draggedItems.Add( aDragged );
  325. }
  326. bool DRAGGER::tryWalkaround( NODE* aNode, LINE& aOrig, LINE& aWalk )
  327. {
  328. WALKAROUND walkaround( aNode, Router() );
  329. bool ok = false;
  330. walkaround.SetSolidsOnly( false );
  331. walkaround.SetDebugDecorator( Dbg() );
  332. walkaround.SetLogger( Logger() );
  333. walkaround.SetIterationLimit( Settings().WalkaroundIterationLimit() );
  334. aWalk = aOrig;
  335. WALKAROUND::RESULT wr = walkaround.Route( aWalk );
  336. if( wr.statusCcw == WALKAROUND::DONE && wr.statusCw == WALKAROUND::DONE )
  337. {
  338. aWalk = ( wr.lineCw.CLine().Length() < wr.lineCcw.CLine().Length() ? wr.lineCw :
  339. wr.lineCcw );
  340. ok = true;
  341. }
  342. else if( wr.statusCw == WALKAROUND::DONE )
  343. {
  344. aWalk = wr.lineCw;
  345. ok = true;
  346. }
  347. else if( wr.statusCcw == WALKAROUND::DONE )
  348. {
  349. aWalk = wr.lineCcw;
  350. ok = true;
  351. }
  352. return ok;
  353. }
  354. bool DRAGGER::dragWalkaround( const VECTOR2I& aP )
  355. {
  356. bool ok = false;
  357. // fixme: rewrite using shared_ptr...
  358. if( m_lastNode )
  359. {
  360. delete m_lastNode;
  361. m_lastNode = nullptr;
  362. }
  363. m_lastNode = m_world->Branch();
  364. switch( m_mode )
  365. {
  366. case DM_SEGMENT:
  367. case DM_CORNER:
  368. {
  369. int thresh = Settings().SmoothDraggedSegments() ? m_draggedLine.Width() / 4 : 0;
  370. LINE dragged( m_draggedLine );
  371. LINE draggedWalk( m_draggedLine );
  372. LINE origLine( m_draggedLine );
  373. dragged.SetSnapThreshhold( thresh );
  374. if( m_mode == DM_SEGMENT )
  375. dragged.DragSegment( aP, m_draggedSegmentIndex );
  376. else
  377. dragged.DragCorner( aP, m_draggedSegmentIndex );
  378. if ( m_world->CheckColliding( &dragged ) )
  379. {
  380. ok = tryWalkaround( m_lastNode, dragged, draggedWalk );
  381. }
  382. else
  383. {
  384. draggedWalk = dragged;
  385. ok = true;
  386. }
  387. if( ok )
  388. {
  389. //Dbg()->AddLine( origLine.CLine(), 4, 100000 );
  390. m_lastNode->Remove( origLine );
  391. optimizeAndUpdateDraggedLine( draggedWalk, origLine, aP );
  392. }
  393. break;
  394. }
  395. case DM_VIA: // fixme...
  396. {
  397. ok = dragViaWalkaround( m_initialVia, m_lastNode, aP );
  398. break;
  399. }
  400. }
  401. m_dragStatus = ok;
  402. return true;
  403. }
  404. bool DRAGGER::dragShove( const VECTOR2I& aP )
  405. {
  406. bool ok = false;
  407. if( m_lastNode )
  408. {
  409. delete m_lastNode;
  410. m_lastNode = NULL;
  411. }
  412. switch( m_mode )
  413. {
  414. case DM_SEGMENT:
  415. case DM_CORNER:
  416. {
  417. //TODO: Make threshhold configurable
  418. int thresh = Settings().SmoothDraggedSegments() ? m_draggedLine.Width() / 2 : 0;
  419. LINE dragged( m_draggedLine );
  420. dragged.SetSnapThreshhold( thresh );
  421. if( m_mode == DM_SEGMENT )
  422. dragged.DragSegment( aP, m_draggedSegmentIndex );
  423. else
  424. dragged.DragCorner( aP, m_draggedSegmentIndex );
  425. SHOVE::SHOVE_STATUS st = m_shove->ShoveLines( dragged );
  426. if( st == SHOVE::SH_OK )
  427. ok = true;
  428. else if( st == SHOVE::SH_HEAD_MODIFIED )
  429. {
  430. dragged = m_shove->NewHead();
  431. ok = true;
  432. }
  433. m_lastNode = m_shove->CurrentNode()->Branch();
  434. if( ok )
  435. {
  436. VECTOR2D lockV;
  437. dragged.ClearLinks();
  438. dragged.Unmark();
  439. optimizeAndUpdateDraggedLine( dragged, m_draggedLine, aP );
  440. }
  441. break;
  442. }
  443. case DM_VIA:
  444. {
  445. VIA_HANDLE newVia;
  446. SHOVE::SHOVE_STATUS st = m_shove->ShoveDraggingVia( m_draggedVia, aP, newVia );
  447. if( st == SHOVE::SH_OK || st == SHOVE::SH_HEAD_MODIFIED )
  448. ok = true;
  449. m_lastNode = m_shove->CurrentNode()->Branch();
  450. if( newVia.valid )
  451. m_draggedVia = newVia;
  452. m_draggedItems.Clear();
  453. break;
  454. }
  455. }
  456. m_dragStatus = ok;
  457. return ok;
  458. }
  459. bool DRAGGER::FixRoute()
  460. {
  461. NODE* node = CurrentNode();
  462. if( node )
  463. {
  464. // If collisions exist, we can fix in shove/smart mode because all tracks to be committed
  465. // will be in valid positions (even if the current routing solution to the mouse cursor is
  466. // invalid). In other modes, we can only commit if "Allow DRC violations" is enabled.
  467. if( !m_dragStatus )
  468. {
  469. Drag( m_lastValidPoint );
  470. node = CurrentNode();
  471. if( !node )
  472. return false;
  473. }
  474. if( !m_dragStatus && !Settings().CanViolateDRC() )
  475. return false;
  476. Router()->CommitRouting( node );
  477. return true;
  478. }
  479. return false;
  480. }
  481. bool DRAGGER::Drag( const VECTOR2I& aP )
  482. {
  483. m_mouseTrailTracer.AddTrailPoint( aP );
  484. bool ret = false;
  485. if( m_freeAngleMode )
  486. {
  487. ret = dragMarkObstacles( aP );
  488. }
  489. else
  490. {
  491. switch( m_currentMode )
  492. {
  493. case RM_MarkObstacles:
  494. ret = dragMarkObstacles( aP );
  495. break;
  496. case RM_Shove:
  497. case RM_Smart:
  498. ret = dragShove( aP );
  499. break;
  500. case RM_Walkaround:
  501. ret = dragWalkaround( aP );
  502. break;
  503. default:
  504. break;
  505. }
  506. }
  507. if( ret )
  508. m_lastValidPoint = aP;
  509. return ret;
  510. }
  511. NODE* DRAGGER::CurrentNode() const
  512. {
  513. return m_lastNode ? m_lastNode : m_world;
  514. }
  515. const ITEM_SET DRAGGER::Traces()
  516. {
  517. return m_draggedItems;
  518. }
  519. }