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.

2141 lines
60 KiB

9 months ago
9 months ago
11 years ago
10 years ago
10 years ago
11 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
11 years ago
11 years ago
9 years ago
11 years ago
11 years ago
9 years ago
9 years ago
11 years ago
11 years ago
12 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
5 years ago
11 years ago
5 years ago
11 years ago
11 years ago
5 years ago
11 years ago
11 years ago
11 years ago
11 years ago
9 years ago
11 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-2017 CERN
  5. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  6. * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  7. *
  8. * This program is free software: you can redistribute it and/or modify it
  9. * under the terms of the GNU General Public License as published by the
  10. * Free Software Foundation, either version 3 of the License, or (at your
  11. * option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License along
  19. * with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #include <optional>
  22. #include <memory>
  23. #include "pns_arc.h"
  24. #include "pns_debug_decorator.h"
  25. #include "pns_line_placer.h"
  26. #include "pns_node.h"
  27. #include "pns_router.h"
  28. #include "pns_shove.h"
  29. #include "pns_solid.h"
  30. #include "pns_topology.h"
  31. #include "pns_walkaround.h"
  32. #include "pns_mouse_trail_tracer.h"
  33. #include <wx/log.h>
  34. namespace PNS {
  35. LINE_PLACER::LINE_PLACER( ROUTER* aRouter ) :
  36. PLACEMENT_ALGO( aRouter )
  37. {
  38. m_initial_direction = DIRECTION_45::N;
  39. m_world = nullptr;
  40. m_shove = nullptr;
  41. m_currentNode = nullptr;
  42. m_idle = true;
  43. // Init temporary variables (do not leave uninitialized members)
  44. m_lastNode = nullptr;
  45. m_placingVia = false;
  46. m_currentNet = nullptr;
  47. m_currentLayer = 0;
  48. m_startItem = nullptr;
  49. m_endItem = nullptr;
  50. m_chainedPlacement = false;
  51. m_orthoMode = false;
  52. m_placementCorrect = false;
  53. }
  54. LINE_PLACER::~LINE_PLACER()
  55. {
  56. }
  57. void LINE_PLACER::setWorld( NODE* aWorld )
  58. {
  59. m_world = aWorld;
  60. }
  61. const VIA LINE_PLACER::makeVia( const VECTOR2I& aP )
  62. {
  63. // fixme: should belong to KICAD_IFACE
  64. auto iface = Router()->GetInterface();
  65. int start = m_sizes.ViaType() == VIATYPE::THROUGH ? iface->GetPNSLayerFromBoardLayer( F_Cu )
  66. : m_sizes.GetLayerTop();
  67. int end = m_sizes.ViaType() == VIATYPE::THROUGH ? iface->GetPNSLayerFromBoardLayer( B_Cu )
  68. : m_sizes.GetLayerBottom();
  69. const PNS_LAYER_RANGE layers(
  70. start ,
  71. end
  72. );
  73. return VIA( aP, layers, m_sizes.ViaDiameter(), m_sizes.ViaDrill(), nullptr, m_sizes.ViaType() );
  74. }
  75. bool LINE_PLACER::ToggleVia( bool aEnabled )
  76. {
  77. m_placingVia = aEnabled;
  78. if( !aEnabled )
  79. m_head.RemoveVia();
  80. return true;
  81. }
  82. void LINE_PLACER::setInitialDirection( const DIRECTION_45& aDirection )
  83. {
  84. m_initial_direction = aDirection;
  85. if( m_tail.SegmentCount() == 0 )
  86. m_direction = aDirection;
  87. }
  88. bool LINE_PLACER::handleSelfIntersections()
  89. {
  90. SHAPE_LINE_CHAIN::INTERSECTIONS ips;
  91. SHAPE_LINE_CHAIN& head = m_head.Line();
  92. SHAPE_LINE_CHAIN& tail = m_tail.Line();
  93. // if there is no tail, there is nothing to intersect with
  94. if( tail.PointCount() < 2 )
  95. return false;
  96. if( head.PointCount() < 2 )
  97. return false;
  98. // completely new head trace? chop off the tail
  99. if( tail.CPoint(0) == head.CPoint(0) )
  100. {
  101. m_direction = m_initial_direction;
  102. tail.Clear();
  103. return true;
  104. }
  105. tail.Intersect( head, ips );
  106. // no intesection points - nothing to reduce
  107. if( ips.empty() )
  108. return false;
  109. int n = INT_MAX;
  110. VECTOR2I ipoint;
  111. // if there is more than one intersection, find the one that is
  112. // closest to the beginning of the tail.
  113. for( const SHAPE_LINE_CHAIN::INTERSECTION& i : ips )
  114. {
  115. if( i.index_our < n )
  116. {
  117. n = i.index_our;
  118. ipoint = i.p;
  119. }
  120. }
  121. // ignore the point where head and tail meet
  122. if( ipoint == head.CPoint( 0 ) || ipoint == tail.CPoint( -1 ) )
  123. return false;
  124. // Intersection point is on the first or the second segment: just start routing
  125. // from the beginning
  126. if( n < 2 )
  127. {
  128. m_direction = m_initial_direction;
  129. tail.Clear();
  130. head.Clear();
  131. return true;
  132. }
  133. else
  134. {
  135. // Clip till the last tail segment before intersection.
  136. // Set the direction to the one of this segment.
  137. const SEG last = tail.CSegment( n - 1 );
  138. m_direction = DIRECTION_45( last );
  139. tail.Remove( n, -1 );
  140. return true;
  141. }
  142. return false;
  143. }
  144. bool LINE_PLACER::handlePullback()
  145. {
  146. SHAPE_LINE_CHAIN& head = m_head.Line();
  147. SHAPE_LINE_CHAIN& tail = m_tail.Line();
  148. if( head.PointCount() < 2 )
  149. return false;
  150. int n = tail.PointCount();
  151. if( n == 0 )
  152. {
  153. return false;
  154. }
  155. else if( n == 1 )
  156. {
  157. tail.Clear();
  158. return true;
  159. }
  160. DIRECTION_45 first_head, last_tail;
  161. wxASSERT( tail.PointCount() >= 2 );
  162. if( !head.IsPtOnArc( 0 ) )
  163. first_head = DIRECTION_45( head.CSegment( 0 ) );
  164. else
  165. first_head = DIRECTION_45( head.CArcs()[head.ArcIndex(0)] );
  166. int lastSegIdx = tail.PointCount() - 2;
  167. if( !tail.IsPtOnArc( lastSegIdx ) )
  168. last_tail = DIRECTION_45( tail.CSegment( lastSegIdx ) );
  169. else
  170. last_tail = DIRECTION_45( tail.CArcs()[tail.ArcIndex(lastSegIdx)] );
  171. DIRECTION_45::AngleType angle = first_head.Angle( last_tail );
  172. // case 1: we have a defined routing direction, and the currently computed
  173. // head goes in different one.
  174. bool pullback_1 = false; // (m_direction != DIRECTION_45::UNDEFINED && m_direction != first_head);
  175. // case 2: regardless of the current routing direction, if the tail/head
  176. // extremities form an acute or right angle, reduce the tail by one segment
  177. // (and hope that further iterations) will result with a cleaner trace
  178. bool pullback_2 = ( angle == DIRECTION_45::ANG_RIGHT || angle == DIRECTION_45::ANG_ACUTE );
  179. if( pullback_1 || pullback_2 )
  180. {
  181. if( !tail.IsArcSegment( lastSegIdx ) )
  182. {
  183. const SEG& seg = tail.CSegment( lastSegIdx );
  184. m_direction = DIRECTION_45( seg );
  185. PNS_DBG( Dbg(), AddPoint, m_p_start, WHITE, 10000, wxT( "new-pstart [pullback3]" ) );
  186. }
  187. else
  188. {
  189. const SHAPE_ARC& arc = tail.CArcs()[tail.ArcIndex( lastSegIdx )];
  190. m_direction = DIRECTION_45( arc );
  191. }
  192. PNS_DBG( Dbg(), Message, wxString::Format( "Placer: pullback triggered [%d] [%s %s]",
  193. n, last_tail.Format(), first_head.Format() ) );
  194. // erase the last point in the tail, hoping that the next iteration will
  195. // result with a head trace that starts with a segment following our
  196. // current direction.
  197. if( n < 2 )
  198. tail.Clear(); // don't leave a single-point tail
  199. else
  200. tail.RemoveShape( -1 );
  201. if( !tail.SegmentCount() )
  202. m_direction = m_initial_direction;
  203. return true;
  204. }
  205. return false;
  206. }
  207. bool LINE_PLACER::reduceTail( const VECTOR2I& aEnd )
  208. {
  209. SHAPE_LINE_CHAIN& head = m_head.Line();
  210. SHAPE_LINE_CHAIN& tail = m_tail.Line();
  211. int n = tail.SegmentCount();
  212. if( head.SegmentCount() < 1 )
  213. return false;
  214. // Don't attempt this for too short tails
  215. if( n < 2 )
  216. return false;
  217. // Start from the segment farthest from the end of the tail
  218. // int start_index = std::max(n - 1 - ReductionDepth, 0);
  219. DIRECTION_45 new_direction;
  220. VECTOR2I new_start;
  221. int reduce_index = -1;
  222. for( int i = tail.SegmentCount() - 1; i >= 0; i-- )
  223. {
  224. const SEG s = tail.CSegment( i );
  225. DIRECTION_45 dir( s );
  226. // calculate a replacement route and check if it matches
  227. // the direction of the segment to be replaced
  228. SHAPE_LINE_CHAIN replacement = dir.BuildInitialTrace( s.A, aEnd );
  229. if( replacement.SegmentCount() < 1 )
  230. continue;
  231. LINE tmp( m_tail, replacement );
  232. if( m_currentNode->CheckColliding( &tmp, ITEM::ANY_T ) )
  233. break;
  234. if( DIRECTION_45( replacement.CSegment( 0 ) ) == dir )
  235. {
  236. new_start = s.A;
  237. new_direction = dir;
  238. reduce_index = i;
  239. }
  240. }
  241. if( reduce_index >= 0 )
  242. {
  243. PNS_DBG( Dbg(), Message, wxString::Format( "Placer: reducing tail: %d" , reduce_index ) );
  244. SHAPE_LINE_CHAIN reducedLine = new_direction.BuildInitialTrace( new_start, aEnd );
  245. m_direction = new_direction;
  246. tail.Remove( reduce_index + 1, -1 );
  247. head.Clear();
  248. return true;
  249. }
  250. if( !tail.SegmentCount() )
  251. m_direction = m_initial_direction;
  252. return false;
  253. }
  254. bool LINE_PLACER::mergeHead()
  255. {
  256. SHAPE_LINE_CHAIN& head = m_head.Line();
  257. SHAPE_LINE_CHAIN& tail = m_tail.Line();
  258. const int ForbiddenAngles = DIRECTION_45::ANG_ACUTE
  259. | DIRECTION_45::ANG_HALF_FULL
  260. | DIRECTION_45::ANG_UNDEFINED;
  261. head.Simplify();
  262. tail.Simplify();
  263. int n_head = head.ShapeCount();
  264. int n_tail = tail.ShapeCount();
  265. if( n_head < 3 )
  266. {
  267. PNS_DBG( Dbg(), Message, wxT( "Merge failed: not enough head segs." ) );
  268. return false;
  269. }
  270. if( n_tail && head.CPoint( 0 ) != tail.CPoint( -1 ) )
  271. {
  272. PNS_DBG( Dbg(), Message, wxT( "Merge failed: head and tail discontinuous." ) );
  273. return false;
  274. }
  275. if( m_head.CountCorners( ForbiddenAngles ) != 0 )
  276. return false;
  277. DIRECTION_45 dir_tail, dir_head;
  278. if( !head.IsPtOnArc( 0 ) )
  279. dir_head = DIRECTION_45( head.CSegment( 0 ) );
  280. else
  281. dir_head = DIRECTION_45( head.CArcs()[head.ArcIndex( 0 )] );
  282. if( n_tail )
  283. {
  284. wxASSERT( tail.PointCount() >= 2 );
  285. int lastSegIdx = tail.PointCount() - 2;
  286. if( !tail.IsPtOnArc( lastSegIdx ) )
  287. dir_tail = DIRECTION_45( tail.CSegment( -1 ) );
  288. else
  289. dir_tail = DIRECTION_45( tail.CArcs()[tail.ArcIndex( lastSegIdx )] );
  290. if( dir_head.Angle( dir_tail ) & ForbiddenAngles )
  291. return false;
  292. }
  293. tail.Append( head );
  294. tail.Simplify();
  295. int lastSegIdx = tail.PointCount() - 2;
  296. if( !tail.IsArcSegment( lastSegIdx ) )
  297. m_direction = DIRECTION_45( tail.CSegment( -1 ) );
  298. else
  299. m_direction = DIRECTION_45( tail.CArcs()[tail.ArcIndex( lastSegIdx )] );
  300. head.Remove( 0, -1 );
  301. PNS_DBG( Dbg(), Message, wxString::Format( "Placer: merge %d, new direction: %s" , n_head,
  302. m_direction.Format() ) );
  303. head.Simplify();
  304. tail.Simplify();
  305. return true;
  306. }
  307. bool LINE_PLACER::clipAndCheckCollisions( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& aL,
  308. SHAPE_LINE_CHAIN& aOut, int &thresholdDist )
  309. {
  310. SHAPE_LINE_CHAIN l( aL );
  311. int idx = l.Split( aP );
  312. if( idx < 0)
  313. return false;
  314. bool rv = true;
  315. SHAPE_LINE_CHAIN l2 = l.Slice( 0, idx );
  316. int dist = l2.Length();
  317. PNS_DBG( Dbg(), AddPoint, aP, BLUE, 500000, wxString::Format( "hug-target-check-%d", idx ) );
  318. PNS_DBG( Dbg(), AddShape, &l2, BLUE, 500000, wxT( "hug-target-line" ) );
  319. if( dist < thresholdDist )
  320. rv = false;
  321. LINE ctest( m_head, l2 );
  322. if( m_currentNode->CheckColliding( &ctest ).has_value() )
  323. rv = false;
  324. if( rv )
  325. {
  326. aOut = l2;
  327. thresholdDist = dist;
  328. }
  329. return rv;
  330. }
  331. bool LINE_PLACER::cursorDistMinimum( const SHAPE_LINE_CHAIN& aL, const VECTOR2I& aCursor,
  332. double lengthThreshold, SHAPE_LINE_CHAIN &aOut )
  333. {
  334. std::vector<int> dists;
  335. std::vector<VECTOR2I> pts;
  336. if( aL.PointCount() == 0 )
  337. return false;
  338. VECTOR2I lastP = aL.CPoint(-1);
  339. int accumulatedDist = 0;
  340. dists.reserve( 2 * aL.PointCount() );
  341. for( int i = 0; i < aL.SegmentCount(); i++ )
  342. {
  343. const SEG& s = aL.CSegment( i );
  344. dists.push_back( ( aCursor - s.A ).EuclideanNorm() );
  345. pts.push_back( s.A );
  346. auto pn = s.NearestPoint( aCursor );
  347. if( pn != s.A && pn != s.B )
  348. {
  349. dists.push_back( ( pn - aCursor ).EuclideanNorm() );
  350. pts.push_back( pn );
  351. }
  352. accumulatedDist += s.Length();
  353. if ( accumulatedDist > lengthThreshold )
  354. {
  355. lastP = s.B;
  356. break;
  357. }
  358. }
  359. dists.push_back( ( aCursor - lastP ).EuclideanNorm() );
  360. pts.push_back( lastP );
  361. int minDistLoc = std::numeric_limits<int>::max();
  362. int minPLoc = -1;
  363. int minDistGlob = std::numeric_limits<int>::max();
  364. int minPGlob = -1;
  365. for( int i = 0; i < dists.size(); i++ )
  366. {
  367. int d = dists[i];
  368. if( d < minDistGlob )
  369. {
  370. minDistGlob = d;
  371. minPGlob = i;
  372. }
  373. }
  374. if( dists.size() >= 3 )
  375. {
  376. for( int i = 0; i < dists.size() - 3; i++ )
  377. {
  378. if( dists[i + 2] > dists[i + 1] && dists[i] > dists[i + 1] )
  379. {
  380. int d = dists[i + 1];
  381. if( d < minDistLoc )
  382. {
  383. minDistLoc = d;
  384. minPLoc = i + 1;
  385. }
  386. }
  387. }
  388. if( dists.back() < minDistLoc && minPLoc >= 0 )
  389. {
  390. minDistLoc = dists.back();
  391. minPLoc = dists.size() - 1;
  392. }
  393. }
  394. else
  395. {
  396. // Too few points: just use the global
  397. minDistLoc = minDistGlob;
  398. minPLoc = minPGlob;
  399. }
  400. // fixme: I didn't make my mind yet if local or global minimum feels better. I'm leaving both
  401. // in the code, enabling the global one by default
  402. minPLoc = -1;
  403. int preferred;
  404. if( minPLoc < 0 )
  405. {
  406. preferred = minPGlob;
  407. }
  408. else
  409. {
  410. preferred = minPLoc;
  411. }
  412. int thresholdDist = 0;
  413. if( clipAndCheckCollisions( pts[preferred], aL, aOut, thresholdDist ) )
  414. return true;
  415. thresholdDist = 0;
  416. SHAPE_LINE_CHAIN l( aL ), prefL;
  417. int minDist = std::numeric_limits<int>::max();
  418. bool ok = false;
  419. for( int i = 0; i < pts.size() ; i++)
  420. {
  421. //PNS_DBG( Dbg(), AddPoint, pts[i], BLUE, 500000, wxT( "hug-target-fallback" ) );
  422. ok |= clipAndCheckCollisions( pts[i], aL, aOut, thresholdDist );
  423. }
  424. return ok;
  425. }
  426. bool LINE_PLACER::rhWalkBase( const VECTOR2I& aP, LINE& aWalkLine, int aCollisionMask,
  427. PNS::PNS_MODE aMode, bool& aViaOk )
  428. {
  429. LINE walkFull( m_head );
  430. LINE l1( m_head );
  431. PNS_DBG( Dbg(), AddItem, &m_tail, GREEN, 100000, wxT( "walk-base-old-tail" ) );
  432. PNS_DBG( Dbg(), AddItem, &m_head, BLUE, 100000, wxT( "walk-base-old-head" ) );
  433. VECTOR2I walkP = aP;
  434. WALKAROUND walkaround( m_currentNode, Router() );
  435. walkaround.SetSolidsOnly( false );
  436. walkaround.SetDebugDecorator( Dbg() );
  437. walkaround.SetLogger( Logger() );
  438. walkaround.SetIterationLimit( Settings().WalkaroundIterationLimit() );
  439. walkaround.SetItemMask( aCollisionMask );
  440. walkaround.SetAllowedPolicies( { WALKAROUND::WP_CCW, WALKAROUND::WP_CW } );
  441. int round = 0;
  442. do
  443. {
  444. l1.Clear();
  445. PNS_DBG( Dbg(), BeginGroup, wxString::Format( "walk-round-%d", round ), 0 );
  446. round++;
  447. aViaOk = buildInitialLine( walkP, l1, aMode, round == 0 );
  448. PNS_DBG( Dbg(), AddItem, &l1, BLUE, 20000, wxT( "walk-base-l1" ) );
  449. if( l1.EndsWithVia() )
  450. PNS_DBG( Dbg(), AddPoint, l1.Via().Pos(), BLUE, 100000, wxT( "walk-base-l1-via" ) );
  451. LINE initTrack( m_tail );
  452. initTrack.Line().Append( l1.CLine() );
  453. initTrack.Line().Simplify();
  454. double initialLength = initTrack.CLine().Length();
  455. double hugThresholdLength = initialLength * Settings().WalkaroundHugLengthThreshold();
  456. double hugThresholdLengthComplete =
  457. 2.0 * initialLength * Settings().WalkaroundHugLengthThreshold();
  458. WALKAROUND::RESULT wr = walkaround.Route( initTrack );
  459. std::optional<LINE> bestLine;
  460. OPTIMIZER optimizer( m_currentNode );
  461. optimizer.SetEffortLevel( OPTIMIZER::MERGE_SEGMENTS );
  462. optimizer.SetCollisionMask( aCollisionMask );
  463. using WALKAROUND::WP_CW;
  464. using WALKAROUND::WP_CCW;
  465. int len_cw = wr.status[WP_CW] != WALKAROUND::ST_STUCK ? wr.lines[WP_CW].CLine().Length()
  466. : std::numeric_limits<int>::max();
  467. int len_ccw = wr.status[WP_CCW] != WALKAROUND::ST_STUCK ? wr.lines[WP_CCW].CLine().Length()
  468. : std::numeric_limits<int>::max();
  469. if( wr.status[ WP_CW ] == WALKAROUND::ST_DONE )
  470. {
  471. PNS_DBG( Dbg(), AddItem, &wr.lines[WP_CW], BLUE, 20000, wxT( "wf-result-cw-preopt" ) );
  472. LINE tmpHead, tmpTail;
  473. OPTIMIZER::Optimize( &wr.lines[WP_CW], OPTIMIZER::MERGE_SEGMENTS, m_currentNode );
  474. if( splitHeadTail( wr.lines[WP_CW], m_tail, tmpHead, tmpTail ) )
  475. {
  476. optimizer.Optimize( &tmpHead );
  477. wr.lines[WP_CW].SetShape( tmpTail.CLine () );
  478. wr.lines[WP_CW].Line().Append( tmpHead.CLine( ) );
  479. }
  480. PNS_DBG( Dbg(), AddItem, &wr.lines[WP_CW], RED, 20000, wxT( "wf-result-cw-postopt" ) );
  481. len_cw = wr.lines[WP_CW].CLine().Length();
  482. bestLine = wr.lines[WP_CW];
  483. }
  484. if( wr.status[WP_CCW] == WALKAROUND::ST_DONE )
  485. {
  486. PNS_DBG( Dbg(), AddItem, &wr.lines[WP_CCW], BLUE, 20000, wxT( "wf-result-ccw-preopt" ) );
  487. LINE tmpHead, tmpTail;
  488. OPTIMIZER::Optimize( &wr.lines[WP_CCW], OPTIMIZER::MERGE_SEGMENTS, m_currentNode );
  489. if( splitHeadTail( wr.lines[WP_CCW], m_tail, tmpHead, tmpTail ) )
  490. {
  491. optimizer.Optimize( &tmpHead );
  492. wr.lines[WP_CCW].SetShape( tmpTail.CLine () );
  493. wr.lines[WP_CCW].Line().Append( tmpHead.CLine( ) );
  494. }
  495. PNS_DBG( Dbg(), AddItem, &wr.lines[WP_CCW], RED, 20000, wxT( "wf-result-ccw-postopt" ) );
  496. len_ccw = wr.lines[WP_CCW].CLine().Length();
  497. if( len_ccw < len_cw )
  498. bestLine = wr.lines[WP_CCW];
  499. }
  500. int bestLength = len_cw < len_ccw ? len_cw : len_ccw;
  501. if( bestLength < hugThresholdLengthComplete && bestLine.has_value() )
  502. {
  503. walkFull.SetShape( bestLine->CLine() );
  504. walkP = walkFull.CLine().CPoint(-1);
  505. PNS_DBGN( Dbg(), EndGroup );
  506. continue;
  507. }
  508. bool validCw = false;
  509. bool validCcw = false;
  510. int distCcw = std::numeric_limits<int>::max();
  511. int distCw = std::numeric_limits<int>::max();
  512. SHAPE_LINE_CHAIN l_cw, l_ccw;
  513. if( wr.status[WP_CW] != WALKAROUND::ST_STUCK )
  514. {
  515. validCw = cursorDistMinimum( wr.lines[WP_CW].CLine(), aP, hugThresholdLength, l_cw );
  516. if( validCw )
  517. distCw = ( aP - l_cw.CPoint( -1 ) ).EuclideanNorm();
  518. PNS_DBG( Dbg(), AddShape, &l_cw, MAGENTA, 200000, wxString::Format( "wh-result-cw %s",
  519. validCw ? "non-colliding"
  520. : "colliding" ) );
  521. }
  522. if( wr.status[WP_CCW] != WALKAROUND::ST_STUCK )
  523. {
  524. validCcw = cursorDistMinimum( wr.lines[WP_CCW].CLine(), aP, hugThresholdLength, l_ccw );
  525. if( validCcw )
  526. distCcw = ( aP - l_ccw.CPoint( -1 ) ).EuclideanNorm();
  527. PNS_DBG( Dbg(), AddShape, &l_ccw, MAGENTA, 200000, wxString::Format( "wh-result-ccw %s",
  528. validCcw ? "non-colliding"
  529. : "colliding" ) );
  530. }
  531. if( distCw < distCcw && validCw )
  532. {
  533. walkFull.SetShape( l_cw );
  534. walkP = l_cw.CPoint(-1);
  535. }
  536. else if( validCcw )
  537. {
  538. walkFull.SetShape( l_ccw );
  539. walkP = l_ccw.CPoint(-1);
  540. }
  541. else
  542. {
  543. PNS_DBGN( Dbg(), EndGroup );
  544. return false;
  545. }
  546. PNS_DBGN( Dbg(), EndGroup );
  547. } while( round < 2 && m_placingVia );
  548. if( l1.EndsWithVia() )
  549. {
  550. VIA v ( l1.Via() );
  551. v.SetPos( walkFull.CPoint( -1 ) );
  552. walkFull.AppendVia( v );
  553. }
  554. PNS_DBG( Dbg(), AddItem, &walkFull, GREEN, 200000, wxT( "walk-full" ) );
  555. if( walkFull.EndsWithVia() )
  556. {
  557. PNS_DBG( Dbg(), AddPoint, walkFull.Via().Pos(), GREEN, 200000,
  558. wxString::Format( "walk-via ok %d", aViaOk ? 1 : 0 ) );
  559. }
  560. aWalkLine = walkFull;
  561. return !walkFull.EndsWithVia() || aViaOk;
  562. }
  563. bool LINE_PLACER::rhWalkOnly( const VECTOR2I& aP, LINE& aNewHead, LINE& aNewTail )
  564. {
  565. LINE walkFull;
  566. int effort = 0;
  567. bool viaOk = false;
  568. if( ! rhWalkBase( aP, walkFull, ITEM::ANY_T, RM_Walkaround, viaOk ) )
  569. return false;
  570. switch( Settings().OptimizerEffort() )
  571. {
  572. case OE_LOW:
  573. effort = 0;
  574. break;
  575. case OE_MEDIUM:
  576. case OE_FULL:
  577. effort = OPTIMIZER::MERGE_SEGMENTS;
  578. break;
  579. }
  580. DIRECTION_45::CORNER_MODE cornerMode = Settings().GetCornerMode();
  581. // Smart Pads is incompatible with 90-degree mode for now
  582. if( Settings().SmartPads()
  583. && ( cornerMode == DIRECTION_45::MITERED_45 || cornerMode == DIRECTION_45::ROUNDED_45 )
  584. && !m_mouseTrailTracer.IsManuallyForced() )
  585. {
  586. effort |= OPTIMIZER::SMART_PADS;
  587. }
  588. if( m_currentNode->CheckColliding( &walkFull ) )
  589. {
  590. PNS_DBG( Dbg(), AddItem, &walkFull, GREEN, 100000, wxString::Format( "collision check fail" ) );
  591. return false;
  592. }
  593. // OK, this deserves a bit of explanation. We used to calculate the walk path for the head only,
  594. // but then the clearance epsilon was added, with the intent of improving collision resolution robustness
  595. // (now a hull or a walk/shove line cannot collide with the 'owner' of the hull under any circumstances).
  596. // This, however, introduced a subtle bug. For a row/column/any other 'regular' arrangement
  597. // of overlapping hulls (think of pads of a SOP/SOIC chip or a regular via grid), walking around may
  598. // produce a new 'head' that is not considered colliding (due to the clearance epsilon), but with
  599. // its start point inside one of the subsequent hulls to process.
  600. // We can't have head[0] inside any hull for the algorithm to work - therefore, we now consider the entire
  601. // 'tail+head' trace when walking around and in case of success, reconstruct the
  602. // 'head' and 'tail' by splitting the walk line at a point that is as close as possible to the original
  603. // head[0], but not inside any obstacle hull.
  604. //
  605. // EXECUTIVE SUMMARY: asinine heuristic to make the router get stuck much less often.
  606. if( ! splitHeadTail( walkFull, m_tail, aNewHead, aNewTail ) )
  607. return false;
  608. if( m_placingVia && viaOk )
  609. {
  610. PNS_DBG( Dbg(), AddPoint, aNewHead.CPoint(-1), RED, 1000000, wxString::Format( "VIA" ) );
  611. aNewHead.AppendVia( makeVia( aNewHead.CPoint( -1 ) ) );
  612. }
  613. OPTIMIZER::Optimize( &aNewHead, effort, m_currentNode );
  614. PNS_DBG( Dbg(), AddItem, &aNewHead, GREEN, 100000, wxString::Format( "walk-new-head" ) );
  615. PNS_DBG( Dbg(), AddItem, &aNewTail, BLUE, 100000, wxT( "walk-new-tail" ) );
  616. return true;
  617. }
  618. bool LINE_PLACER::rhMarkObstacles( const VECTOR2I& aP, LINE& aNewHead, LINE& aNewTail )
  619. {
  620. buildInitialLine( aP, m_head, RM_MarkObstacles );
  621. m_head.SetBlockingObstacle( nullptr );
  622. auto obs = m_currentNode->NearestObstacle( &m_head );
  623. // If the head is in colliding state, snap to the hull of the first obstacle.
  624. // This way, one can route tracks as tightly as possible without enabling
  625. // the shove/walk mode that certain users find too intrusive.
  626. if( obs )
  627. {
  628. int clearance = m_currentNode->GetClearance( obs->m_item, &m_head, false );
  629. SHAPE_LINE_CHAIN hull = obs->m_item->Hull( clearance, m_head.Width(), m_head.Layer() );
  630. VECTOR2I nearest;
  631. DIRECTION_45::CORNER_MODE cornerMode = Settings().GetCornerMode();
  632. if( cornerMode == DIRECTION_45::MITERED_90 || cornerMode == DIRECTION_45::ROUNDED_90 )
  633. nearest = hull.BBox().NearestPoint( aP );
  634. else
  635. nearest = hull.NearestPoint( aP );
  636. if( ( nearest - aP ).EuclideanNorm() < m_head.Width() / 2 )
  637. buildInitialLine( nearest, m_head, RM_MarkObstacles );
  638. }
  639. // Note: Something like the below could be used to implement a "stop at first obstacle" mode,
  640. // but we don't have one right now and there isn't a lot of demand for one. If we do end up
  641. // doing that, put it in a new routing mode as "highlight collisions" mode should not have
  642. // collision handling other than highlighting.
  643. #if 0
  644. if( !Settings().AllowDRCViolations() )
  645. {
  646. NODE::OPT_OBSTACLE obs = m_currentNode->NearestObstacle( &m_head );
  647. if( obs && obs->m_distFirst != INT_MAX )
  648. {
  649. buildInitialLine( obs->m_ipFirst, m_head );
  650. m_head.SetBlockingObstacle( obs->m_item );
  651. }
  652. }
  653. #endif
  654. aNewHead = m_head;
  655. aNewTail = m_tail;
  656. return true;
  657. }
  658. bool LINE_PLACER::splitHeadTail( const LINE& aNewLine, const LINE& aOldTail, LINE& aNewHead,
  659. LINE& aNewTail )
  660. {
  661. LINE newTail( aOldTail );
  662. LINE newHead( aOldTail );
  663. LINE l2( aNewLine );
  664. newTail.RemoveVia();
  665. newHead.Clear();
  666. int i;
  667. bool found = false;
  668. int n = l2.PointCount();
  669. if( n > 1 && aOldTail.PointCount() > 1 )
  670. {
  671. if( l2.CLine().PointOnEdge( aOldTail.CPoint( -1 ) ) )
  672. {
  673. l2.Line().Split( aOldTail.CPoint( -1 ) );
  674. }
  675. for( i = 0; i < aOldTail.PointCount(); i++ )
  676. {
  677. if( l2.CLine().Find( aOldTail.CPoint( i ) ) < 0 )
  678. {
  679. found = true;
  680. break;
  681. }
  682. }
  683. if( !found )
  684. i--;
  685. // If the old tail doesn't have any points of the new line, we can't split it.
  686. if( i >= l2.PointCount() )
  687. i = l2.PointCount() - 1;
  688. newHead.Clear();
  689. if( i == 0 )
  690. newTail.Clear();
  691. else
  692. newTail.SetShape( l2.CLine().Slice( 0, i ) );
  693. newHead.SetShape( l2.CLine().Slice( i, -1 ) );
  694. }
  695. else
  696. {
  697. newTail.Clear();
  698. newHead = l2;
  699. }
  700. PNS_DBG( Dbg(), AddItem, &newHead, BLUE, 500000, wxT( "head-post-split" ) );
  701. aNewHead = newHead;
  702. aNewTail = newTail;
  703. return true;
  704. }
  705. bool LINE_PLACER::rhShoveOnly( const VECTOR2I& aP, LINE& aNewHead, LINE& aNewTail )
  706. {
  707. LINE walkSolids;
  708. bool viaOk = false;
  709. if( ! rhWalkBase( aP, walkSolids, ITEM::SOLID_T, RM_Shove, viaOk ) )
  710. return false;
  711. m_currentNode = m_shove->CurrentNode();
  712. m_shove->SetLogger( Logger() );
  713. m_shove->SetDebugDecorator( Dbg() );
  714. if( m_endItem )
  715. {
  716. // Make sure the springback algorithm won't erase the NODE that owns m_endItem.
  717. m_shove->SetSpringbackDoNotTouchNode( static_cast<const NODE*>( m_endItem->Owner() ) );
  718. }
  719. LINE newHead( walkSolids );
  720. if( walkSolids.EndsWithVia() )
  721. PNS_DBG( Dbg(), AddPoint, newHead.Via().Pos(), RED, 1000000, wxString::Format( "SVIA [%d]", viaOk?1:0 ) );
  722. if( m_placingVia && viaOk )
  723. {
  724. newHead.AppendVia( makeVia( newHead.CPoint( -1 ) ) );
  725. PNS_DBG( Dbg(), AddPoint, newHead.Via().Pos(), GREEN, 1000000, "shove-new-via" );
  726. }
  727. m_shove->ClearHeads();
  728. m_shove->AddHeads( newHead, SHOVE::SHP_SHOVE );
  729. bool shoveOk = m_shove->Run() == SHOVE::SH_OK;
  730. m_currentNode = m_shove->CurrentNode();
  731. int effort = 0;
  732. switch( Settings().OptimizerEffort() )
  733. {
  734. case OE_LOW:
  735. effort = 0;
  736. break;
  737. case OE_MEDIUM:
  738. case OE_FULL:
  739. effort = OPTIMIZER::MERGE_SEGMENTS;
  740. break;
  741. }
  742. DIRECTION_45::CORNER_MODE cornerMode = Settings().GetCornerMode();
  743. // Smart Pads is incompatible with 90-degree mode for now
  744. if( Settings().SmartPads()
  745. && ( cornerMode == DIRECTION_45::MITERED_45 || cornerMode == DIRECTION_45::ROUNDED_45 )
  746. && !m_mouseTrailTracer.IsManuallyForced() )
  747. {
  748. effort |= OPTIMIZER::SMART_PADS;
  749. }
  750. if( shoveOk )
  751. {
  752. if( m_shove->HeadsModified() )
  753. newHead = m_shove->GetModifiedHead( 0 );
  754. if( newHead.EndsWithVia() )
  755. {
  756. PNS_DBG( Dbg(), AddPoint, newHead.Via().Pos(), GREEN, 1000000, "shove-via-preopt" );
  757. PNS_DBG( Dbg(), AddPoint, newHead.Via().Pos(), GREEN, 1000000, "shove-via-postopt" );
  758. }
  759. if( ! splitHeadTail( newHead, m_tail, aNewHead, aNewTail ) )
  760. return false;
  761. if( newHead.EndsWithVia() )
  762. aNewHead.AppendVia( newHead.Via() );
  763. OPTIMIZER::Optimize( &aNewHead, effort, m_currentNode );
  764. PNS_DBG( Dbg(), AddItem, aNewHead.Clone(), GREEN, 1000000, "head-sh-postopt" );
  765. return true;
  766. }
  767. else
  768. {
  769. return rhWalkOnly( aP, aNewHead, aNewTail );
  770. }
  771. return false;
  772. }
  773. bool LINE_PLACER::routeHead( const VECTOR2I& aP, LINE& aNewHead, LINE& aNewTail )
  774. {
  775. switch( Settings().Mode() )
  776. {
  777. case RM_MarkObstacles:
  778. return rhMarkObstacles( aP, aNewHead, aNewTail );
  779. case RM_Walkaround:
  780. return rhWalkOnly( aP, aNewHead, aNewTail );
  781. case RM_Shove:
  782. return rhShoveOnly( aP, aNewHead, aNewTail );
  783. default:
  784. break;
  785. }
  786. return false;
  787. }
  788. bool LINE_PLACER::optimizeTailHeadTransition()
  789. {
  790. LINE linetmp = Trace();
  791. PNS_DBG( Dbg(), Message, "optimize HT" );
  792. // NOTE: FANOUT_CLEANUP can override posture setting at the moment
  793. if( !m_mouseTrailTracer.IsManuallyForced() &&
  794. OPTIMIZER::Optimize( &linetmp, OPTIMIZER::FANOUT_CLEANUP, m_currentNode ) )
  795. {
  796. if( linetmp.SegmentCount() < 1 )
  797. return false;
  798. m_head = linetmp;
  799. m_direction = DIRECTION_45( linetmp.CSegment( 0 ) );
  800. m_tail.Line().Clear();
  801. return true;
  802. }
  803. SHAPE_LINE_CHAIN& head = m_head.Line();
  804. SHAPE_LINE_CHAIN& tail = m_tail.Line();
  805. int tailLookbackSegments = 3;
  806. //if(m_currentMode() == RM_Walkaround)
  807. // tailLookbackSegments = 10000;
  808. int threshold = std::min( tail.PointCount(), tailLookbackSegments + 1 );
  809. if( tail.ShapeCount() < 3 )
  810. return false;
  811. // assemble TailLookbackSegments tail segments with the current head
  812. SHAPE_LINE_CHAIN opt_line = tail.Slice( -threshold, -1 );
  813. int end = std::min(2, head.PointCount() - 1 );
  814. opt_line.Append( head.Slice( 0, end ) );
  815. LINE new_head( m_tail, opt_line );
  816. // and see if it could be made simpler by merging obtuse/collnear segments.
  817. // If so, replace the (threshold) last tail points and the head with
  818. // the optimized line
  819. PNS_DBG( Dbg(), AddItem, &new_head, LIGHTCYAN, 10000, wxT( "ht-newline" ) );
  820. if( OPTIMIZER::Optimize( &new_head, OPTIMIZER::MERGE_SEGMENTS, m_currentNode ) )
  821. {
  822. LINE tmp( m_tail, opt_line );
  823. head.Clear();
  824. tail.Replace( -threshold, -1, new_head.CLine() );
  825. tail.Simplify();
  826. m_direction = DIRECTION_45( new_head.CSegment( -1 ) );
  827. return true;
  828. }
  829. return false;
  830. }
  831. void LINE_PLACER::updatePStart( const LINE& tail )
  832. {
  833. if( tail.CLine().PointCount() )
  834. m_p_start = tail.CLine().CPoint(-1);
  835. else
  836. m_p_start = m_currentStart;
  837. }
  838. void LINE_PLACER::routeStep( const VECTOR2I& aP )
  839. {
  840. bool fail = false;
  841. bool go_back = false;
  842. int i, n_iter = 1;
  843. PNS_DBG( Dbg(), Message, wxString::Format( "routeStep: direction: %s head: %d, tail: %d shapes" ,
  844. m_direction.Format(),
  845. m_head.ShapeCount(),
  846. m_tail.ShapeCount() ) );
  847. PNS_DBG( Dbg(), BeginGroup, wxT( "route-step" ), 0 );
  848. PNS_DBG( Dbg(), AddItem, &m_tail, WHITE, 10000, wxT( "tail-init" ) );
  849. PNS_DBG( Dbg(), AddItem, &m_head, GREEN, 10000, wxT( "head-init" ) );
  850. for( i = 0; i < n_iter; i++ )
  851. {
  852. LINE prevTail( m_tail );
  853. LINE prevHead( m_head );
  854. LINE newHead, newTail;
  855. if( !go_back && Settings().FollowMouse() )
  856. reduceTail( aP );
  857. PNS_DBG( Dbg(), AddItem, &m_tail, WHITE, 10000, wxT( "tail-after-reduce" ) );
  858. PNS_DBG( Dbg(), AddItem, &m_head, GREEN, 10000, wxT( "head-after-reduce" ) );
  859. go_back = false;
  860. updatePStart( m_tail );
  861. if( !routeHead( aP, newHead, newTail ) )
  862. {
  863. m_tail = prevTail;
  864. m_head = prevHead;
  865. // If we fail to walk out of the initial point (no tail), instead of returning an empty
  866. // line, return a zero-length line so that the user gets some feedback that routing is
  867. // happening. This will get pruned later.
  868. if( m_tail.PointCount() == 0 )
  869. {
  870. m_tail.Line().Append( m_p_start );
  871. m_tail.Line().Append( m_p_start, true );
  872. }
  873. fail = true;
  874. }
  875. updatePStart( m_tail );
  876. PNS_DBG( Dbg(), AddItem, &newHead, LIGHTGREEN, 100000, wxString::Format( "new_head [fail: %d]", fail?1:0 ) );
  877. if( fail )
  878. break;
  879. PNS_DBG( Dbg(), Message, wxString::Format( "N VIA H %d T %d\n", m_head.EndsWithVia() ? 1 : 0, m_tail.EndsWithVia() ? 1 : 0 ) );
  880. m_head = newHead;
  881. m_tail = newTail;
  882. if( handleSelfIntersections() )
  883. {
  884. n_iter++;
  885. go_back = true;
  886. }
  887. PNS_DBG( Dbg(), Message, wxString::Format( "SI VIA H %d T %d\n", m_head.EndsWithVia() ? 1 : 0, m_tail.EndsWithVia() ? 1 : 0 ) );
  888. PNS_DBG( Dbg(), AddItem, &m_tail, WHITE, 10000, wxT( "tail-after-si" ) );
  889. PNS_DBG( Dbg(), AddItem, &m_head, GREEN, 10000, wxT( "head-after-si" ) );
  890. if( !go_back && handlePullback() )
  891. {
  892. n_iter++;
  893. m_head.Clear();
  894. go_back = true;
  895. }
  896. PNS_DBG( Dbg(), Message, wxString::Format( "PB VIA H %d T %d\n", m_head.EndsWithVia() ? 1 : 0, m_tail.EndsWithVia() ? 1 : 0 ) );
  897. PNS_DBG( Dbg(), AddItem, &m_tail, WHITE, 100000, wxT( "tail-after-pb" ) );
  898. PNS_DBG( Dbg(), AddItem, &m_head, GREEN, 100000, wxT( "head-after-pb" ) );
  899. }
  900. if( !fail && Settings().FollowMouse() )
  901. {
  902. PNS_DBG( Dbg(), AddItem, &m_tail, WHITE, 10000, wxT( "tail-pre-merge" ) );
  903. PNS_DBG( Dbg(), AddItem, &m_head, GREEN, 10000, wxT( "head-pre-merge" ) );
  904. if( !optimizeTailHeadTransition() )
  905. {
  906. PNS_DBG( Dbg(), Message, wxString::Format( "PreM VIA H %d T %d\n", m_head.EndsWithVia() ? 1 : 0, m_tail.EndsWithVia() ? 1 : 0 ) );
  907. mergeHead();
  908. PNS_DBG( Dbg(), Message, wxString::Format( "PostM VIA H %d T %d\n", m_head.EndsWithVia() ? 1 : 0, m_tail.EndsWithVia() ? 1 : 0 ) );
  909. }
  910. PNS_DBG( Dbg(), AddItem, &m_tail, WHITE, 100000, wxT( "tail-post-merge" ) );
  911. PNS_DBG( Dbg(), AddItem, &m_head, GREEN, 100000, wxT( "head-post-merge" ) );
  912. }
  913. m_last_p_end = aP;
  914. PNS_DBGN( Dbg(), EndGroup );
  915. }
  916. bool LINE_PLACER::route( const VECTOR2I& aP )
  917. {
  918. routeStep( aP );
  919. if( !m_head.PointCount() )
  920. return false;
  921. return m_head.CPoint( -1 ) == aP;
  922. }
  923. const LINE LINE_PLACER::Trace() const
  924. {
  925. SHAPE_LINE_CHAIN l( m_tail.CLine() );
  926. l.Append( m_head.CLine() );
  927. // Only simplify if we have more than two points, because if we have a zero-length seg as the
  928. // only part of the trace, we don't want it to be removed at this stage (will be the case if
  929. // the routing start point violates DRC due to track width in shove/walk mode, for example).
  930. if( l.PointCount() > 2 )
  931. l.Simplify();
  932. LINE tmp( m_head );
  933. tmp.SetShape( l );
  934. PNS_DBG( Dbg(), AddItem, &m_tail, GREEN, 100000, wxT( "tmp-tail" ) );
  935. PNS_DBG( Dbg(), AddItem, &m_head, LIGHTGREEN, 100000, wxT( "tmp-head" ) );
  936. return tmp;
  937. }
  938. const ITEM_SET LINE_PLACER::Traces()
  939. {
  940. m_currentTrace = Trace();
  941. return ITEM_SET( &m_currentTrace );
  942. }
  943. void LINE_PLACER::FlipPosture()
  944. {
  945. // In order to fix issue 12369 get the current line placer first direction
  946. // and copy it to the mouse trail tracer, as the current placer may have
  947. // changed the route.
  948. if( m_mouseTrailTracer.IsManuallyForced() == false && m_currentTrace.SegmentCount() > 0 )
  949. {
  950. DIRECTION_45 firstDirection( m_currentTrace.CSegment( 0 ) );
  951. m_mouseTrailTracer.SetDefaultDirections( firstDirection, DIRECTION_45::UNDEFINED );
  952. }
  953. m_mouseTrailTracer.FlipPosture();
  954. }
  955. NODE* LINE_PLACER::CurrentNode( bool aLoopsRemoved ) const
  956. {
  957. if( aLoopsRemoved && m_lastNode )
  958. return m_lastNode;
  959. return m_currentNode;
  960. }
  961. bool LINE_PLACER::SplitAdjacentSegments( NODE* aNode, ITEM* aSeg, const VECTOR2I& aP )
  962. {
  963. if( !aSeg )
  964. return false;
  965. if( !aSeg->OfKind( ITEM::SEGMENT_T ) )
  966. return false;
  967. const JOINT* jt = aNode->FindJoint( aP, aSeg );
  968. if( jt && jt->LinkCount() >= 1 )
  969. return false;
  970. SEGMENT* s_old = static_cast<SEGMENT*>( aSeg );
  971. std::unique_ptr<SEGMENT> s_new[2] = { Clone( *s_old ), Clone( *s_old ) };
  972. s_new[0]->SetEnds( s_old->Seg().A, aP );
  973. s_new[1]->SetEnds( aP, s_old->Seg().B );
  974. aNode->Remove( s_old );
  975. aNode->Add( std::move( s_new[0] ), true );
  976. aNode->Add( std::move( s_new[1] ), true );
  977. return true;
  978. }
  979. bool LINE_PLACER::SplitAdjacentArcs( NODE* aNode, ITEM* aArc, const VECTOR2I& aP )
  980. {
  981. if( !aArc )
  982. return false;
  983. if( !aArc->OfKind( ITEM::ARC_T ) )
  984. return false;
  985. const JOINT* jt = aNode->FindJoint( aP, aArc );
  986. if( jt && jt->LinkCount() >= 1 )
  987. return false;
  988. ARC* a_old = static_cast<ARC*>( aArc );
  989. const SHAPE_ARC& o_arc = a_old->Arc();
  990. std::unique_ptr<ARC> a_new[2] = { Clone( *a_old ), Clone( *a_old ) };
  991. a_new[0]->Arc().ConstructFromStartEndCenter( o_arc.GetP0(), aP, o_arc.GetCenter(),
  992. o_arc.IsClockwise(), o_arc.GetWidth() );
  993. a_new[1]->Arc().ConstructFromStartEndCenter( aP, o_arc.GetP1(), o_arc.GetCenter(),
  994. o_arc.IsClockwise(), o_arc.GetWidth() );
  995. aNode->Remove( a_old );
  996. aNode->Add( std::move( a_new[0] ), true );
  997. aNode->Add( std::move( a_new[1] ), true );
  998. return true;
  999. }
  1000. bool LINE_PLACER::SetLayer( int aLayer )
  1001. {
  1002. if( m_idle )
  1003. {
  1004. m_currentLayer = aLayer;
  1005. return true;
  1006. }
  1007. else if( m_chainedPlacement )
  1008. {
  1009. return false;
  1010. }
  1011. else if( !m_startItem
  1012. || ( m_startItem->OfKind( ITEM::VIA_T ) && m_startItem->Layers().Overlaps( aLayer ) )
  1013. || ( m_startItem->OfKind( ITEM::SOLID_T ) && m_startItem->Layers().Overlaps( aLayer ) ) )
  1014. {
  1015. m_currentLayer = aLayer;
  1016. m_p_start = m_currentStart;
  1017. m_direction = m_initial_direction;
  1018. m_mouseTrailTracer.Clear();
  1019. m_head.Line().Clear();
  1020. m_tail.Line().Clear();
  1021. m_head.RemoveVia();
  1022. m_tail.RemoveVia();
  1023. m_head.SetLayer( m_currentLayer );
  1024. m_tail.SetLayer( m_currentLayer );
  1025. Move( m_currentEnd, nullptr );
  1026. return true;
  1027. }
  1028. return false;
  1029. }
  1030. bool LINE_PLACER::Start( const VECTOR2I& aP, ITEM* aStartItem )
  1031. {
  1032. m_placementCorrect = false;
  1033. m_currentStart = VECTOR2I( aP );
  1034. m_fixStart = VECTOR2I( aP );
  1035. m_currentEnd = VECTOR2I( aP );
  1036. m_currentNet = aStartItem ? aStartItem->Net() : Router()->GetInterface()->GetOrphanedNetHandle();
  1037. m_startItem = aStartItem;
  1038. m_placingVia = false;
  1039. m_chainedPlacement = false;
  1040. m_fixedTail.Clear();
  1041. m_endItem = nullptr;
  1042. setInitialDirection( Settings().InitialDirection() );
  1043. initPlacement();
  1044. DIRECTION_45 initialDir = m_initial_direction;
  1045. DIRECTION_45 lastSegDir = DIRECTION_45::UNDEFINED;
  1046. if( aStartItem && aStartItem->Kind() == ITEM::SEGMENT_T )
  1047. {
  1048. // If we land on a segment endpoint, assume the starting direction is continuing along
  1049. // the same direction as the endpoint. If we started in the middle, don't set a
  1050. // direction so that the posture solver is not biased.
  1051. SEG seg = static_cast<SEGMENT*>( aStartItem )->Seg();
  1052. if( aP == seg.A )
  1053. lastSegDir = DIRECTION_45( seg.Reversed() );
  1054. else if( aP == seg.B )
  1055. lastSegDir = DIRECTION_45( seg );
  1056. }
  1057. else if( aStartItem && aStartItem->Kind() == ITEM::SOLID_T &&
  1058. static_cast<SOLID*>( aStartItem )->Parent()->Type() == PCB_PAD_T )
  1059. {
  1060. double angle = static_cast<SOLID*>( aStartItem )->GetOrientation().AsDegrees();
  1061. angle = ( angle + 22.5 ) / 45.0;
  1062. initialDir = DIRECTION_45( static_cast<DIRECTION_45::Directions>( int( angle ) ) );
  1063. }
  1064. PNS_DBG( Dbg(), Message, wxString::Format( "Posture: init %s, last seg %s",
  1065. initialDir.Format(), lastSegDir.Format() ) );
  1066. m_mouseTrailTracer.Clear();
  1067. m_mouseTrailTracer.AddTrailPoint( aP );
  1068. m_mouseTrailTracer.SetTolerance( m_head.Width() );
  1069. m_mouseTrailTracer.SetDefaultDirections( m_initial_direction, DIRECTION_45::UNDEFINED );
  1070. m_mouseTrailTracer.SetMouseDisabled( !Settings().GetAutoPosture() );
  1071. NODE *n;
  1072. if ( Settings().Mode() == PNS::RM_Shove )
  1073. n = m_shove->CurrentNode();
  1074. else
  1075. n = m_currentNode;
  1076. m_fixedTail.AddStage( m_fixStart, m_currentLayer, m_placingVia, m_direction, n );
  1077. return true;
  1078. }
  1079. void LINE_PLACER::initPlacement()
  1080. {
  1081. m_idle = false;
  1082. m_head.Line().Clear();
  1083. m_tail.Line().Clear();
  1084. m_head.SetNet( m_currentNet );
  1085. m_tail.SetNet( m_currentNet );
  1086. m_head.SetLayer( m_currentLayer );
  1087. m_tail.SetLayer( m_currentLayer );
  1088. m_head.SetWidth( m_sizes.TrackWidth() );
  1089. m_tail.SetWidth( m_sizes.TrackWidth() );
  1090. m_head.RemoveVia();
  1091. m_tail.RemoveVia();
  1092. m_last_p_end.reset();
  1093. m_p_start = m_currentStart;
  1094. m_direction = m_initial_direction;
  1095. NODE* world = Router()->GetWorld();
  1096. world->KillChildren();
  1097. NODE* rootNode = world->Branch();
  1098. SplitAdjacentSegments( rootNode, m_startItem, m_currentStart );
  1099. setWorld( rootNode );
  1100. wxLogTrace( wxT( "PNS" ), wxT( "world %p, intitial-direction %s layer %d" ),
  1101. m_world,
  1102. m_direction.Format().c_str(),
  1103. m_currentLayer );
  1104. m_lastNode = nullptr;
  1105. m_currentNode = m_world;
  1106. m_shove = std::make_unique<SHOVE>( m_world->Branch(), Router() );
  1107. }
  1108. bool LINE_PLACER::Move( const VECTOR2I& aP, ITEM* aEndItem )
  1109. {
  1110. LINE current;
  1111. int eiDepth = -1;
  1112. if( aEndItem && aEndItem->Owner() )
  1113. eiDepth = static_cast<const NODE*>( aEndItem->Owner() )->Depth();
  1114. if( m_lastNode )
  1115. {
  1116. delete m_lastNode;
  1117. m_lastNode = nullptr;
  1118. }
  1119. m_endItem = aEndItem;
  1120. bool reachesEnd = route( aP );
  1121. current = Trace();
  1122. if( !current.PointCount() )
  1123. m_currentEnd = m_p_start;
  1124. else
  1125. m_currentEnd = current.CLine().CPoint( -1 );
  1126. NODE* latestNode = m_currentNode;
  1127. m_lastNode = latestNode->Branch();
  1128. if( reachesEnd
  1129. && eiDepth >= 0
  1130. && aEndItem && latestNode->Depth() >= eiDepth
  1131. && current.SegmentCount() )
  1132. {
  1133. if ( aEndItem->Net() == m_currentNet )
  1134. SplitAdjacentSegments( m_lastNode, aEndItem, current.CPoint( -1 ) );
  1135. if( Settings().RemoveLoops() )
  1136. removeLoops( m_lastNode, current );
  1137. }
  1138. updateLeadingRatLine();
  1139. m_mouseTrailTracer.AddTrailPoint( aP );
  1140. return true;
  1141. }
  1142. bool LINE_PLACER::FixRoute( const VECTOR2I& aP, ITEM* aEndItem, bool aForceFinish )
  1143. {
  1144. bool fixAll = Settings().GetFixAllSegments();
  1145. bool realEnd = false;
  1146. LINE pl = Trace();
  1147. if( Settings().Mode() == RM_MarkObstacles )
  1148. {
  1149. // Mark Obstacles is sort of a half-manual, half-automated mode in which the
  1150. // user has more responsibility and authority.
  1151. if( aEndItem )
  1152. {
  1153. // The user has indicated a connection should be made. If either the trace or
  1154. // endItem is net-less, then allow the connection by adopting the net of the other.
  1155. if( m_router->GetInterface()->GetNetCode( m_currentNet ) <= 0 )
  1156. {
  1157. m_currentNet = aEndItem->Net();
  1158. pl.SetNet( m_currentNet );
  1159. }
  1160. else if( m_router->GetInterface()->GetNetCode( aEndItem->Net() ) <= 0 )
  1161. {
  1162. aEndItem->SetNet( m_currentNet );
  1163. }
  1164. }
  1165. }
  1166. // Collisions still prevent fixing unless "Allow DRC violations" is checked
  1167. // Note that collisions can occur even in walk/shove modes if the beginning of the trace
  1168. // collides (for example if the starting track width is too high).
  1169. if( !Settings().AllowDRCViolations() )
  1170. {
  1171. NODE* checkNode = ( Settings().Mode() == RM_Shove ) ? m_shove->CurrentNode() : m_world;
  1172. std::optional<OBSTACLE> obs = checkNode->CheckColliding( &pl );
  1173. if( obs )
  1174. {
  1175. // TODO: Determine why the shove node sometimes reports collisions against shoved objects.
  1176. // For now, to work around this issue, we consider only solids in shove mode.
  1177. if( Settings().Mode() != RM_Shove || obs->m_item->OfKind( ITEM::SOLID_T ) )
  1178. return false;
  1179. }
  1180. }
  1181. const SHAPE_LINE_CHAIN& l = pl.CLine();
  1182. if( !l.SegmentCount() )
  1183. {
  1184. if( m_lastNode )
  1185. {
  1186. // Do a final optimization to the stored state
  1187. NODE::ITEM_VECTOR removed, added;
  1188. m_lastNode->GetUpdatedItems( removed, added );
  1189. if( !added.empty() && added.back()->Kind() == ITEM::SEGMENT_T )
  1190. simplifyNewLine( m_lastNode, static_cast<SEGMENT*>( added.back() ) );
  1191. }
  1192. // Nothing to commit if we have an empty line
  1193. if( !pl.EndsWithVia() )
  1194. return false;
  1195. ///< @todo Determine what to do if m_lastNode is a null pointer. I'm guessing return
  1196. ///< false but someone with more knowledge of the code will need to determine that..
  1197. if( m_lastNode )
  1198. {
  1199. m_lastNode->Add( Clone( pl.Via() ) );
  1200. m_shove->AddLockedSpringbackNode( m_lastNode );
  1201. }
  1202. m_currentNode = nullptr;
  1203. m_idle = true;
  1204. m_placementCorrect = true;
  1205. return true;
  1206. }
  1207. VECTOR2I p_pre_last = l.CPoint( -1 );
  1208. const VECTOR2I p_last = l.CPoint( -1 );
  1209. if( l.PointCount() > 2 )
  1210. p_pre_last = l.CPoint( -2 );
  1211. if( aEndItem && m_currentNet && m_currentNet == aEndItem->Net() )
  1212. realEnd = true;
  1213. if( aForceFinish )
  1214. realEnd = true;
  1215. // TODO: Rollback doesn't work properly if fix-all isn't enabled and we are placing arcs,
  1216. // so if we are, act as though we are in fix-all mode.
  1217. if( !fixAll && l.ArcCount() )
  1218. fixAll = true;
  1219. // TODO: lastDirSeg will be calculated incorrectly if we end on an arc
  1220. SEG lastDirSeg = ( !fixAll && l.SegmentCount() > 1 ) ? l.CSegment( -2 ) : l.CSegment( -1 );
  1221. DIRECTION_45 d_last( lastDirSeg );
  1222. int lastV;
  1223. if( realEnd || m_placingVia || fixAll )
  1224. lastV = l.SegmentCount();
  1225. else
  1226. lastV = std::max( 1, l.SegmentCount() - 1 );
  1227. ARC arc;
  1228. SEGMENT seg;
  1229. LINKED_ITEM* lastItem = nullptr;
  1230. int lastArc = -1;
  1231. for( int i = 0; i < lastV; i++ )
  1232. {
  1233. ssize_t arcIndex = l.ArcIndex( i );
  1234. if( arcIndex < 0 || ( lastArc >= 0 && i == lastV - 1 && !l.IsPtOnArc( lastV ) ) )
  1235. {
  1236. seg = SEGMENT( pl.CSegment( i ), m_currentNet );
  1237. seg.SetWidth( pl.Width() );
  1238. seg.SetLayer( m_currentLayer );
  1239. std::unique_ptr<SEGMENT> sp = std::make_unique<SEGMENT>( seg );
  1240. lastItem = sp.get();
  1241. if( !m_lastNode->Add( std::move( sp ) ) )
  1242. lastItem = nullptr;
  1243. }
  1244. else
  1245. {
  1246. if( arcIndex == lastArc )
  1247. continue;
  1248. arc = ARC( l.Arc( arcIndex ), m_currentNet );
  1249. arc.SetWidth( pl.Width() );
  1250. arc.SetLayer( m_currentLayer );
  1251. std::unique_ptr<ARC> ap = std::make_unique<ARC>( arc );
  1252. lastItem = ap.get();
  1253. if( !m_lastNode->Add( std::move( ap ) ) )
  1254. lastItem = nullptr;
  1255. lastArc = arcIndex;
  1256. }
  1257. }
  1258. if( pl.EndsWithVia() )
  1259. m_lastNode->Add( Clone( pl.Via() ) );
  1260. if( realEnd && lastItem )
  1261. simplifyNewLine( m_lastNode, lastItem );
  1262. if( !realEnd )
  1263. {
  1264. setInitialDirection( d_last );
  1265. m_currentStart = ( m_placingVia || fixAll ) ? p_last : p_pre_last;
  1266. m_fixedTail.AddStage( m_fixStart, m_currentLayer, m_placingVia, m_direction, m_currentNode );
  1267. m_fixStart = m_currentStart;
  1268. m_startItem = nullptr;
  1269. m_placingVia = false;
  1270. m_chainedPlacement = !pl.EndsWithVia();
  1271. m_p_start = m_currentStart;
  1272. m_direction = m_initial_direction;
  1273. m_head.Line().Clear();
  1274. m_tail.Line().Clear();
  1275. m_head.RemoveVia();
  1276. m_tail.RemoveVia();
  1277. m_currentNode = m_lastNode;
  1278. m_lastNode = m_lastNode->Branch();
  1279. m_shove->AddLockedSpringbackNode( m_currentNode );
  1280. DIRECTION_45 lastSegDir = pl.EndsWithVia() ? DIRECTION_45::UNDEFINED : d_last;
  1281. m_mouseTrailTracer.Clear();
  1282. m_mouseTrailTracer.SetTolerance( m_head.Width() );
  1283. m_mouseTrailTracer.AddTrailPoint( m_currentStart );
  1284. m_mouseTrailTracer.SetDefaultDirections( lastSegDir, DIRECTION_45::UNDEFINED );
  1285. m_placementCorrect = true;
  1286. }
  1287. else
  1288. {
  1289. m_shove->AddLockedSpringbackNode( m_lastNode );
  1290. m_placementCorrect = true;
  1291. m_idle = true;
  1292. }
  1293. return realEnd;
  1294. }
  1295. std::optional<VECTOR2I> LINE_PLACER::UnfixRoute()
  1296. {
  1297. FIXED_TAIL::STAGE st;
  1298. std::optional<VECTOR2I> ret;
  1299. if ( !m_fixedTail.PopStage( st ) )
  1300. return ret;
  1301. if( m_head.Line().PointCount() )
  1302. ret = m_head.Line().CPoint( 0 );
  1303. m_head.Line().Clear();
  1304. m_tail.Line().Clear();
  1305. m_startItem = nullptr;
  1306. m_p_start = st.pts[0].p;
  1307. m_fixStart = m_p_start;
  1308. m_direction = st.pts[0].direction;
  1309. m_placingVia = st.pts[0].placingVias;
  1310. m_currentNode = st.commit;
  1311. m_currentLayer = st.pts[0].layer;
  1312. m_currentStart = m_p_start;
  1313. m_head.SetLayer( m_currentLayer );
  1314. m_tail.SetLayer( m_currentLayer );
  1315. m_head.RemoveVia();
  1316. m_tail.RemoveVia();
  1317. m_mouseTrailTracer.Clear();
  1318. m_mouseTrailTracer.SetDefaultDirections( m_initial_direction, m_direction );
  1319. m_mouseTrailTracer.AddTrailPoint( m_p_start );
  1320. m_shove->RewindSpringbackTo( m_currentNode );
  1321. m_shove->UnlockSpringbackNode( m_currentNode );
  1322. if( Settings().Mode() == PNS::RM_Shove )
  1323. {
  1324. m_currentNode = m_shove->CurrentNode();
  1325. m_currentNode->KillChildren();
  1326. }
  1327. m_lastNode = m_currentNode->Branch();
  1328. return ret;
  1329. }
  1330. bool LINE_PLACER::HasPlacedAnything() const
  1331. {
  1332. return m_placementCorrect || m_fixedTail.StageCount() > 1;
  1333. }
  1334. bool LINE_PLACER::CommitPlacement()
  1335. {
  1336. if( Settings().Mode() == PNS::RM_Shove )
  1337. {
  1338. m_shove->RewindToLastLockedNode();
  1339. m_lastNode = m_shove->CurrentNode();
  1340. m_lastNode->KillChildren();
  1341. }
  1342. if( m_lastNode )
  1343. Router()->CommitRouting( m_lastNode );
  1344. m_lastNode = nullptr;
  1345. m_currentNode = nullptr;
  1346. return true;
  1347. }
  1348. void LINE_PLACER::removeLoops( NODE* aNode, LINE& aLatest )
  1349. {
  1350. if( !aLatest.SegmentCount() )
  1351. return;
  1352. if( aLatest.CLine().CPoint( 0 ) == aLatest.CLine().CPoint( -1 ) )
  1353. return;
  1354. std::set<LINKED_ITEM *> toErase;
  1355. aLatest.ClearLinks();
  1356. aNode->Add( aLatest, true );
  1357. for( int s = 0; s < aLatest.LinkCount(); s++ )
  1358. {
  1359. LINKED_ITEM* seg = aLatest.GetLink(s);
  1360. LINE ourLine = aNode->AssembleLine( seg );
  1361. JOINT a, b;
  1362. std::vector<LINE> lines;
  1363. aNode->FindLineEnds( ourLine, a, b );
  1364. if( a == b )
  1365. aNode->FindLineEnds( aLatest, a, b );
  1366. aNode->FindLinesBetweenJoints( a, b, lines );
  1367. int removedCount = 0;
  1368. int total = 0;
  1369. for( LINE& line : lines )
  1370. {
  1371. total++;
  1372. if( !( line.ContainsLink( seg ) ) && line.SegmentCount() )
  1373. {
  1374. for( LINKED_ITEM* ss : line.Links() )
  1375. toErase.insert( ss );
  1376. removedCount++;
  1377. }
  1378. }
  1379. PNS_DBG( Dbg(), Message, wxString::Format( "total segs removed: %d/%d", removedCount, total ) );
  1380. }
  1381. for( LINKED_ITEM* s : toErase )
  1382. aNode->Remove( s );
  1383. aNode->Remove( aLatest );
  1384. }
  1385. void LINE_PLACER::simplifyNewLine( NODE* aNode, LINKED_ITEM* aLatest )
  1386. {
  1387. wxASSERT( aLatest->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) );
  1388. // Before we assemble the final line and run the optimizer, do a separate pass to clean up
  1389. // colinear segments that exist on non-line-corner joints, as these will prevent proper assembly
  1390. // of the line and won't get cleaned up by the optimizer.
  1391. NODE::ITEM_VECTOR removed, added;
  1392. aNode->GetUpdatedItems( removed, added );
  1393. std::set<ITEM*> cleanup;
  1394. auto processJoint =
  1395. [&]( const JOINT* aJoint, ITEM* aItem )
  1396. {
  1397. if( !aJoint || aJoint->IsLineCorner() )
  1398. return;
  1399. SEG refSeg = static_cast<SEGMENT*>( aItem )->Seg();
  1400. NODE::ITEM_VECTOR toRemove;
  1401. for( ITEM* neighbor : aJoint->CLinks().CItems() )
  1402. {
  1403. if( neighbor == aItem
  1404. || !neighbor->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T )
  1405. || !neighbor->LayersOverlap( aItem ) )
  1406. {
  1407. continue;
  1408. }
  1409. if( static_cast<const SEGMENT*>( neighbor )->Width()
  1410. != static_cast<const SEGMENT*>( aItem )->Width() )
  1411. {
  1412. continue;
  1413. }
  1414. const SEG& testSeg = static_cast<const SEGMENT*>( neighbor )->Seg();
  1415. if( refSeg.Contains( testSeg ) )
  1416. {
  1417. const JOINT* nA = aNode->FindJoint( neighbor->Anchor( 0 ), neighbor );
  1418. const JOINT* nB = aNode->FindJoint( neighbor->Anchor( 1 ), neighbor );
  1419. if( ( nA == aJoint && nB->LinkCount() == 1 ) ||
  1420. ( nB == aJoint && nA->LinkCount() == 1 ) )
  1421. {
  1422. cleanup.insert( neighbor );
  1423. }
  1424. }
  1425. }
  1426. };
  1427. for( ITEM* item : added )
  1428. {
  1429. if( !item->OfKind( ITEM::SEGMENT_T ) || cleanup.count( item ) )
  1430. continue;
  1431. const JOINT* jA = aNode->FindJoint( item->Anchor( 0 ), item );
  1432. const JOINT* jB = aNode->FindJoint( item->Anchor( 1 ), item );
  1433. processJoint( jA, item );
  1434. processJoint( jB, item );
  1435. }
  1436. for( ITEM* seg : cleanup )
  1437. aNode->Remove( seg );
  1438. // And now we can proceed with assembling the final line and optimizing it.
  1439. LINE l_orig = aNode->AssembleLine( aLatest );
  1440. LINE l( l_orig );
  1441. bool optimized = OPTIMIZER::Optimize( &l, OPTIMIZER::MERGE_COLINEAR, aNode );
  1442. SHAPE_LINE_CHAIN simplified( l.CLine() );
  1443. simplified.Simplify();
  1444. if( optimized || simplified.PointCount() != l.PointCount() )
  1445. {
  1446. aNode->Remove( l_orig );
  1447. l.SetShape( simplified );
  1448. aNode->Add( l );
  1449. PNS_DBG( Dbg(), AddItem, &l, RED, 100000, wxT("simplified"));
  1450. }
  1451. }
  1452. void LINE_PLACER::UpdateSizes( const SIZES_SETTINGS& aSizes )
  1453. {
  1454. m_sizes = aSizes;
  1455. if( !m_idle )
  1456. {
  1457. // If the track width continues from an existing track, we don't want to change the width.
  1458. // Disallow changing width after the first segment has been fixed because we don't want to
  1459. // go back and rip up tracks or allow DRC errors
  1460. if( m_sizes.TrackWidthIsExplicit()
  1461. || ( !HasPlacedAnything() && ( !m_startItem || m_startItem->Kind() != ITEM::SEGMENT_T ) ) )
  1462. {
  1463. m_head.SetWidth( m_sizes.TrackWidth() );
  1464. m_tail.SetWidth( m_sizes.TrackWidth() );
  1465. m_currentTrace.SetWidth( m_sizes.TrackWidth() );
  1466. }
  1467. if( m_head.EndsWithVia() )
  1468. {
  1469. m_head.SetViaDiameter( m_sizes.ViaDiameter() );
  1470. m_head.SetViaDrill( m_sizes.ViaDrill() );
  1471. }
  1472. }
  1473. }
  1474. void LINE_PLACER::updateLeadingRatLine()
  1475. {
  1476. LINE current = Trace();
  1477. SHAPE_LINE_CHAIN ratLine;
  1478. TOPOLOGY topo( m_lastNode );
  1479. if( topo.LeadingRatLine( &current, ratLine ) )
  1480. m_router->GetInterface()->DisplayRatline( ratLine, m_currentNet );
  1481. }
  1482. void LINE_PLACER::SetOrthoMode( bool aOrthoMode )
  1483. {
  1484. m_orthoMode = aOrthoMode;
  1485. }
  1486. bool LINE_PLACER::buildInitialLine( const VECTOR2I& aP, LINE& aHead, PNS::PNS_MODE aMode, bool aForceNoVia )
  1487. {
  1488. SHAPE_LINE_CHAIN l;
  1489. DIRECTION_45 guessedDir = m_mouseTrailTracer.GetPosture( aP );
  1490. PNS_DBG( Dbg(), Message, wxString::Format( wxT( "buildInitialLine: m_direction %s, guessedDir %s, tail points %d" ),
  1491. m_direction.Format(), guessedDir.Format(), m_tail.PointCount() ) );
  1492. DIRECTION_45::CORNER_MODE cornerMode = Settings().GetCornerMode();
  1493. // Rounded corners don't make sense when routing orthogonally (single track at a time)
  1494. if( m_orthoMode )
  1495. cornerMode = DIRECTION_45::CORNER_MODE::MITERED_45;
  1496. PNS_DBG( Dbg(), AddPoint, m_p_start, WHITE, 10000, wxT( "pstart [buildInitial]" ) );
  1497. if( m_p_start == aP )
  1498. {
  1499. l.Clear();
  1500. }
  1501. else
  1502. {
  1503. if( Settings().GetFreeAngleMode() && Settings().Mode() == RM_MarkObstacles )
  1504. {
  1505. l = SHAPE_LINE_CHAIN( { m_p_start, aP } );
  1506. }
  1507. else
  1508. {
  1509. if( !m_tail.PointCount() )
  1510. l = guessedDir.BuildInitialTrace( m_p_start, aP, false, cornerMode );
  1511. else
  1512. l = m_direction.BuildInitialTrace( m_p_start, aP, false, cornerMode );
  1513. }
  1514. if( l.SegmentCount() > 1 && m_orthoMode )
  1515. {
  1516. VECTOR2I newLast = l.CSegment( 0 ).LineProject( l.CPoint( -1 ) );
  1517. l.Remove( -1, -1 );
  1518. l.SetPoint( 1, newLast );
  1519. }
  1520. }
  1521. aHead.SetLayer( m_currentLayer );
  1522. aHead.SetShape( l );
  1523. PNS_DBG( Dbg(), AddItem, &aHead, CYAN, 10000, wxT( "initial-trace" ) );
  1524. if( !m_placingVia || aForceNoVia )
  1525. return true;
  1526. VIA v( makeVia( aP ) );
  1527. v.SetNet( aHead.Net() );
  1528. if( aMode == RM_MarkObstacles )
  1529. {
  1530. aHead.AppendVia( v );
  1531. return true;
  1532. }
  1533. const int collMask = ( aMode == RM_Walkaround ) ? ITEM::ANY_T : ITEM::SOLID_T;
  1534. const int iterLimit = Settings().ViaForcePropIterationLimit();
  1535. for( int attempt = 0; attempt < 2; attempt++)
  1536. {
  1537. VECTOR2I lead = aP - m_p_start;
  1538. VECTOR2I force;
  1539. if( attempt == 1 && m_last_p_end.has_value() )
  1540. lead = aP - m_last_p_end.value();
  1541. if( v.PushoutForce( m_currentNode, lead, force, collMask, iterLimit ) )
  1542. {
  1543. SHAPE_LINE_CHAIN line = guessedDir.BuildInitialTrace( m_p_start, aP + force, false, cornerMode );
  1544. aHead = LINE( aHead, line );
  1545. v.SetPos( v.Pos() + force );
  1546. aHead.AppendVia( v );
  1547. PNS_DBG( Dbg(), AddPoint, v.Pos(), GREEN, 1000000, "via-force-coll-2" );
  1548. return true;
  1549. }
  1550. }
  1551. return false; // via placement unsuccessful
  1552. }
  1553. void LINE_PLACER::GetModifiedNets( std::vector<NET_HANDLE>& aNets ) const
  1554. {
  1555. aNets.push_back( m_currentNet );
  1556. }
  1557. bool LINE_PLACER::AbortPlacement()
  1558. {
  1559. m_world->KillChildren();
  1560. m_lastNode = nullptr;
  1561. return true;
  1562. }
  1563. FIXED_TAIL::FIXED_TAIL( int aLineCount )
  1564. {
  1565. }
  1566. FIXED_TAIL::~FIXED_TAIL()
  1567. {
  1568. }
  1569. void FIXED_TAIL::Clear()
  1570. {
  1571. m_stages.clear();
  1572. }
  1573. void FIXED_TAIL::AddStage( const VECTOR2I& aStart, int aLayer, bool placingVias,
  1574. DIRECTION_45 direction, NODE* aNode )
  1575. {
  1576. STAGE st;
  1577. FIX_POINT pt;
  1578. pt.p = aStart;
  1579. pt.layer = aLayer;
  1580. pt.direction = direction;
  1581. pt.placingVias = placingVias;
  1582. st.pts.push_back(pt);
  1583. st.commit = aNode;
  1584. m_stages.push_back( st );
  1585. }
  1586. bool FIXED_TAIL::PopStage( FIXED_TAIL::STAGE& aStage )
  1587. {
  1588. if( !m_stages.size() )
  1589. return false;
  1590. aStage = m_stages.back();
  1591. if( m_stages.size() > 1 )
  1592. m_stages.pop_back();
  1593. return true;
  1594. }
  1595. int FIXED_TAIL::StageCount() const
  1596. {
  1597. return m_stages.size();
  1598. }
  1599. }