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.

1356 lines
34 KiB

9 months ago
10 years ago
10 years ago
10 years ago
11 years ago
11 years ago
9 years ago
11 years ago
9 years ago
9 years ago
3 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
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
9 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 <math/box2.h>
  23. #include <math/vector2d.h>
  24. #include "pns_line.h"
  25. #include "pns_node.h"
  26. #include "pns_via.h"
  27. #include "pns_utils.h"
  28. #include "pns_router.h"
  29. #include "pns_debug_decorator.h"
  30. #include <geometry/shape_rect.h>
  31. namespace PNS {
  32. LINE::LINE( const LINE& aOther ) :
  33. LINK_HOLDER( aOther ),
  34. m_line( aOther.m_line ),
  35. m_width( aOther.m_width ),
  36. m_snapThreshhold( aOther.m_snapThreshhold )
  37. {
  38. m_net = aOther.m_net;
  39. m_movable = aOther.m_movable;
  40. m_layers = aOther.m_layers;
  41. m_via = nullptr;
  42. if( aOther.m_via )
  43. {
  44. if( aOther.m_via->BelongsTo( &aOther ) )
  45. {
  46. m_via = aOther.m_via->Clone();
  47. m_via->SetOwner( this );
  48. m_via->SetNet( m_net );
  49. }
  50. else
  51. {
  52. m_via = aOther.m_via;
  53. }
  54. }
  55. m_marker = aOther.m_marker;
  56. m_rank = aOther.m_rank;
  57. m_blockingObstacle = aOther.m_blockingObstacle;
  58. copyLinks( &aOther );
  59. }
  60. LINE::~LINE()
  61. {
  62. if( m_via && m_via->BelongsTo( this ) )
  63. delete m_via;
  64. }
  65. LINE& LINE::operator=( const LINE& aOther )
  66. {
  67. m_parent = aOther.m_parent;
  68. m_sourceItem = aOther.m_sourceItem;
  69. m_line = aOther.m_line;
  70. m_width = aOther.m_width;
  71. m_net = aOther.m_net;
  72. m_movable = aOther.m_movable;
  73. m_layers = aOther.m_layers;
  74. m_via = nullptr;
  75. if( aOther.m_via )
  76. {
  77. if( aOther.m_via->BelongsTo( &aOther ) )
  78. {
  79. m_via = aOther.m_via->Clone();
  80. m_via->SetOwner( this );
  81. m_via->SetNet( m_net );
  82. }
  83. else
  84. {
  85. m_via = aOther.m_via;
  86. }
  87. }
  88. m_marker = aOther.m_marker;
  89. m_rank = aOther.m_rank;
  90. m_routable = aOther.m_routable;
  91. m_owner = aOther.m_owner;
  92. m_snapThreshhold = aOther.m_snapThreshhold;
  93. m_blockingObstacle = aOther.m_blockingObstacle;
  94. copyLinks( &aOther );
  95. return *this;
  96. }
  97. LINE* LINE::Clone() const
  98. {
  99. LINE* l = new LINE( *this );
  100. return l;
  101. }
  102. void LINE::Mark( int aMarker ) const
  103. {
  104. m_marker = aMarker;
  105. for( const LINKED_ITEM* s : m_links )
  106. s->Mark( aMarker );
  107. }
  108. void LINE::Unmark( int aMarker ) const
  109. {
  110. for( const LINKED_ITEM* s : m_links )
  111. s->Unmark( aMarker );
  112. m_marker = 0;
  113. }
  114. int LINE::Marker() const
  115. {
  116. int marker = m_marker;
  117. for( LINKED_ITEM* s : m_links )
  118. marker |= s->Marker();
  119. return marker;
  120. }
  121. SEGMENT* SEGMENT::Clone() const
  122. {
  123. SEGMENT* s = new SEGMENT( *this );
  124. s->m_seg = m_seg;
  125. s->m_net = m_net;
  126. s->m_layers = m_layers;
  127. s->m_marker = m_marker;
  128. s->m_rank = m_rank;
  129. return s;
  130. }
  131. int LINE::CountCorners( int aAngles ) const
  132. {
  133. int count = 0;
  134. for( int i = 0; i < m_line.SegmentCount() - 1; i++ )
  135. {
  136. const SEG seg1 = m_line.CSegment( i );
  137. const SEG seg2 = m_line.CSegment( i + 1 );
  138. const DIRECTION_45 dir1( seg1 );
  139. const DIRECTION_45 dir2( seg2 );
  140. DIRECTION_45::AngleType a = dir1.Angle( dir2 );
  141. if( a & aAngles )
  142. count++;
  143. }
  144. return count;
  145. }
  146. static int areNeighbours( int x, int y, int max = 0 )
  147. {
  148. if( x > 0 && x - 1 == y )
  149. return true;
  150. if( x < max - 1 && x + 1 == y )
  151. return true;
  152. return false;
  153. }
  154. #ifdef TOM_EXTRA_DEBUG
  155. SHAPE_LINE_CHAIN g_pnew, g_hnew;
  156. #endif
  157. bool LINE::Walkaround( const SHAPE_LINE_CHAIN& aObstacle, SHAPE_LINE_CHAIN& aPath, bool aCw ) const
  158. {
  159. const SHAPE_LINE_CHAIN& line( CLine() );
  160. if( line.SegmentCount() < 1 )
  161. {
  162. return false;
  163. }
  164. const VECTOR2I pFirst = line.CPoint(0);
  165. bool inFirst = aObstacle.PointInside( pFirst ) && !aObstacle.PointOnEdge( pFirst );
  166. // We can't really walk around if the beginning of the path lies inside the obstacle hull.
  167. // Double check if it's not on the hull itself as this triggers many unroutable corner cases.
  168. if( inFirst )
  169. {
  170. return false;
  171. }
  172. enum VERTEX_TYPE { INSIDE = 0, OUTSIDE, ON_EDGE };
  173. // Represents an entry in directed graph of hull/path vertices. Scanning this graph
  174. // starting from the path's first point results (if possible) with a correct walkaround path
  175. struct VERTEX
  176. {
  177. // vertex classification (inside/outside/exactly on the hull)
  178. VERTEX_TYPE type;
  179. // true = vertex coming from the hull primitive
  180. bool isHull;
  181. // position
  182. VECTOR2I pos;
  183. // list of neighboring vertices
  184. std::vector<VERTEX*> neighbours;
  185. // index of this vertex in path (pnew)
  186. int indexp = -1;
  187. // index of this vertex in the hull (hnew)
  188. int indexh = -1;
  189. // visited indicator (for BFS search)
  190. bool visited = false;
  191. };
  192. SHAPE_LINE_CHAIN::INTERSECTIONS ips;
  193. HullIntersection( aObstacle, line, ips );
  194. SHAPE_LINE_CHAIN pnew( CLine() ), hnew( aObstacle );
  195. std::vector<VERTEX> vts;
  196. auto findVertex =
  197. [&]( const VECTOR2I& pos ) -> VERTEX*
  198. {
  199. for( VERTEX& v : vts )
  200. {
  201. if( v.pos == pos )
  202. return &v;
  203. }
  204. return nullptr;
  205. };
  206. // corner case for loopy tracks: insert the end loop point back into the hull
  207. if( const std::optional<SHAPE_LINE_CHAIN::INTERSECTION> isect = pnew.SelfIntersecting() )
  208. {
  209. if( isect->p != pnew.CPoint( -1 ) )
  210. pnew.Split( isect->p );
  211. }
  212. // insert all intersections found into the new hull/path SLCs
  213. for( SHAPE_LINE_CHAIN::INTERSECTION& ip : ips )
  214. {
  215. if( pnew.Find( ip.p, 1 ) < 0)
  216. pnew.Split(ip.p);
  217. if( hnew.Find( ip.p, 1 ) < 0 )
  218. hnew.Split(ip.p);
  219. }
  220. for( int i = 0; i < pnew.PointCount(); i++ )
  221. {
  222. const VECTOR2I& p = pnew.CPoint( i );
  223. bool onEdge = hnew.PointOnEdge( p );
  224. if ( !onEdge )
  225. continue;
  226. int idx = hnew.Find( p );
  227. if(idx < 0 )
  228. hnew.Split( p );
  229. }
  230. #ifdef TOM_EXTRA_DEBUG
  231. for( auto& ip : ips )
  232. {
  233. printf("Chk: %d %d\n", pnew.Find( ip.p ), hnew.Find(ip.p) );
  234. }
  235. #endif
  236. // we assume the default orientation of the hulls is clockwise, so just reverse the vertex
  237. // order if the caller wants a counter-clockwise walkaround
  238. if ( !aCw )
  239. hnew = hnew.Reverse();
  240. vts.reserve( 2 * ( hnew.PointCount() + pnew.PointCount() ) );
  241. // create a graph of hull/path vertices and classify them (inside/on edge/outside the hull)
  242. for( int i = 0; i < pnew.PointCount(); i++ )
  243. {
  244. const VECTOR2I& p = pnew.CPoint(i);
  245. bool onEdge = hnew.PointOnEdge( p );
  246. bool inside = hnew.PointInside( p );
  247. #ifdef TOM_EXTRA_DEBUG
  248. printf("pnew %d inside %d onedge %d\n", i, !!inside, !!onEdge );
  249. #endif
  250. VERTEX v;
  251. v.indexp = i;
  252. v.isHull = false;
  253. v.pos = p;
  254. v.type = inside && !onEdge ? INSIDE : onEdge ? ON_EDGE : OUTSIDE;
  255. vts.push_back( v );
  256. }
  257. #ifdef TOM_EXTRA_DEBUG
  258. g_pnew = pnew;
  259. g_hnew = hnew;
  260. #endif
  261. // each path vertex neighbour list points for sure to the next vertex in the path
  262. for( int i = 0; i < pnew.PointCount() - 1; i++ )
  263. {
  264. vts[i].neighbours.push_back( &vts[ i+1 ] );
  265. }
  266. // each path vertex neighbour list points for sure to the next vertex in the path
  267. for( int i = 1; i < pnew.PointCount() ; i++ )
  268. {
  269. vts[i].neighbours.push_back( &vts[ i-1 ] );
  270. }
  271. // insert hull vertices into the graph
  272. for( int i = 0; i < hnew.PointCount(); i++ )
  273. {
  274. const VECTOR2I& hp = hnew.CPoint( i );
  275. VERTEX* vn = findVertex( hp );
  276. // if vertex already present (it's very likely that in recursive shoving hull and path vertices will overlap)
  277. // just mark it as a path vertex that also belongs to the hull
  278. if( vn )
  279. {
  280. vn->isHull = true;
  281. vn->indexh = i;
  282. }
  283. else // new hull vertex
  284. {
  285. VERTEX v;
  286. v.pos = hp;
  287. v.type = ON_EDGE;
  288. v.indexh = i;
  289. v.isHull = true;
  290. vts.push_back( v );
  291. }
  292. }
  293. // go around the hull and fix up the neighbour link lists
  294. for( int i = 0; i < hnew.PointCount(); i++ )
  295. {
  296. VERTEX* vc = findVertex( hnew.CPoint( i ) );
  297. VERTEX* vnext = findVertex( hnew.CPoint( i+1 ) );
  298. if( vc && vnext )
  299. vc->neighbours.push_back( vnext );
  300. }
  301. // In the case that the initial path ends *inside* the current obstacle (i.e. the mouse cursor
  302. // is somewhere inside the hull for the current obstacle) we want to end the walkaround at the
  303. // point closest to the cursor
  304. bool inLast = aObstacle.PointInside( CPoint( -1 ) ) && !aObstacle.PointOnEdge( CPoint( -1 ) );
  305. bool appendV = true;
  306. int lastDst = INT_MAX;
  307. int i = 0;
  308. #ifdef TOM_EXTRA_DEBUG
  309. for( VERTEX* &v: vts )
  310. {
  311. if( v.indexh < 0 && v.type == ON_EDGE )
  312. v.type = OUTSIDE; // hack
  313. printf("V %d pos %d %d ip %d ih %d type %d\n", i++, v.pos.x, v.pos.y, v.indexp, v.indexh, v.type );
  314. }
  315. #endif
  316. // vts[0] = start point
  317. VERTEX* v = &vts[0];
  318. VERTEX* v_prev = nullptr;
  319. SHAPE_LINE_CHAIN out;
  320. int iterLimit = 1000;
  321. // keep scanning the graph until we reach the end point of the path
  322. while( v->indexp != ( pnew.PointCount() - 1 ) )
  323. {
  324. iterLimit--;
  325. // I'm not 100% sure this algorithm doesn't have bugs that may cause it to freeze,
  326. // so here's a temporary iteration limit
  327. if( iterLimit == 0 )
  328. return false;
  329. if( v->visited )
  330. {
  331. // loop found? stop walking
  332. break;
  333. }
  334. #ifdef TOM_EXTRA_DEBUG
  335. printf("---\nvisit ip %d ih %d type %d outs %d neig %d\n", v->indexp, v->indexh, v->type, out.PointCount(), v->neighbours.size() );
  336. #endif
  337. out.Append( v->pos );
  338. VERTEX* v_next = nullptr;
  339. if( v->type == OUTSIDE )
  340. {
  341. // current vertex is outside? first look for any vertex further down the path
  342. // that is not inside the hull
  343. out.Append( v->pos );
  344. VERTEX* v_next_fallback = nullptr;
  345. for( VERTEX* vn : v->neighbours )
  346. {
  347. if( areNeighbours( vn->indexp , v->indexp, pnew.PointCount() )
  348. && vn->type != INSIDE )
  349. {
  350. if( !vn->visited )
  351. {
  352. v_next = vn;
  353. break;
  354. }
  355. else if( vn != v_prev )
  356. {
  357. v_next_fallback = vn;
  358. }
  359. }
  360. }
  361. if( !v_next )
  362. v_next = v_next_fallback;
  363. // such a vertex must always be present, if not, bummer.
  364. if( !v_next )
  365. {
  366. #ifdef TOM_EXTRA_DEBUG
  367. printf("FAIL VN fallback %p\n", v_next_fallback );
  368. #endif
  369. return false;
  370. }
  371. }
  372. else if( v->type == ON_EDGE )
  373. {
  374. // look first for the first vertex outside the hull
  375. for( VERTEX* vn : v->neighbours )
  376. {
  377. #ifdef TOM_EXTRA_DEBUG
  378. printf( "- OUT scan ip %d ih %d type %d\n", vn->indexp, vn->indexh, vn->type );
  379. #endif
  380. if( vn->type == OUTSIDE && !vn->visited )
  381. {
  382. v_next = vn;
  383. break;
  384. }
  385. }
  386. // no outside vertices found? continue traversing the hull
  387. if( !v_next )
  388. {
  389. for( VERTEX* vn : v->neighbours )
  390. {
  391. #ifdef TOM_EXTRA_DEBUG
  392. printf("- scan ip %d ih %d type %d\n", vn->indexp, vn->indexh, vn->type );
  393. #endif
  394. if( vn->type == ON_EDGE && !vn->isHull &&
  395. areNeighbours( vn->indexp, v->indexp, pnew.PointCount() ) &&
  396. ( vn->indexh == ( ( v->indexh + 1 ) % hnew.PointCount() ) ) )
  397. {
  398. v_next = vn;
  399. break;
  400. }
  401. }
  402. }
  403. // still nothing found? try to find the next (index-wise) point on the hull. I guess
  404. // we should never reach this part of the code, but who really knows?
  405. if( !v_next )
  406. {
  407. #ifdef TOM_EXTRA_DEBUG
  408. printf("still no v_next\n");
  409. #endif
  410. for( VERTEX* vn : v->neighbours )
  411. {
  412. if( vn->type == ON_EDGE )
  413. {
  414. if( vn->indexh == ( ( v->indexh + 1 ) % hnew.PointCount() ) )
  415. {
  416. v_next = vn;
  417. break;
  418. }
  419. }
  420. }
  421. if( v_next )
  422. {
  423. for( VERTEX &vt : vts )
  424. {
  425. if( vt.isHull )
  426. vt.visited = false;
  427. }
  428. }
  429. #ifdef TOM_EXTRA_DEBUG
  430. printf("v_next %p\n", v_next);
  431. #endif
  432. // Did we get the next hull point but the end of the line is inside? Instead of walking
  433. // around the hull some more (which will just end up taking us back to the start), lets
  434. // just project the normal of the endpoint onto this next segment and call it quits.
  435. if( inLast && v_next )
  436. {
  437. int d = ( v_next->pos - CPoint( -1 ) ).SquaredEuclideanNorm();
  438. if( d < lastDst )
  439. {
  440. lastDst = d;
  441. }
  442. else
  443. {
  444. VECTOR2I proj = SEG( v->pos, v_next->pos ).NearestPoint( CPoint( -1 ) );
  445. out.Append( proj );
  446. appendV = false;
  447. break;
  448. }
  449. }
  450. }
  451. }
  452. v->visited = true;
  453. v_prev = v;
  454. v = v_next;
  455. if( !v )
  456. return false;
  457. }
  458. if( appendV )
  459. out.Append( v->pos );
  460. aPath = out;
  461. return true;
  462. }
  463. const SHAPE_LINE_CHAIN SEGMENT::Hull( int aClearance, int aWalkaroundThickness, int aLayer ) const
  464. {
  465. /*DEBUG_DECORATOR* debugDecorator = ROUTER::GetInstance()->GetInterface()->GetDebugDecorator();
  466. PNS_DBG( debugDecorator, Message, wxString::Format( wxT( "seghull %d %d" ), aWalkaroundThickness, aClearance ) );
  467. PNS_DBG(debugDecorator, AddShape, &m_seg, RED, 0, wxT("theseg") );
  468. */
  469. return SegmentHull( m_seg, aClearance, aWalkaroundThickness );
  470. }
  471. const LINE LINE::ClipToNearestObstacle( NODE* aNode ) const
  472. {
  473. const int IterationLimit = 5;
  474. int i;
  475. LINE l( *this );
  476. for( i = 0; i < IterationLimit; i++ )
  477. {
  478. NODE::OPT_OBSTACLE obs = aNode->NearestObstacle( &l );
  479. if( obs )
  480. {
  481. l.RemoveVia();
  482. VECTOR2I collisionPoint = obs->m_ipFirst;
  483. int segIdx = l.Line().NearestSegment( collisionPoint );
  484. if( l.Line().IsArcSegment( segIdx ) )
  485. {
  486. // Don't clip at arcs, start again
  487. l.Line().Clear();
  488. }
  489. else
  490. {
  491. SEG nearestSegment = l.Line().CSegment( segIdx );
  492. VECTOR2I nearestPt = nearestSegment.NearestPoint( collisionPoint );
  493. int p = l.Line().Split( nearestPt );
  494. l.Line().Remove( p + 1, -1 );
  495. }
  496. }
  497. else
  498. {
  499. break;
  500. }
  501. }
  502. if( i == IterationLimit )
  503. l.Line().Clear();
  504. return l;
  505. }
  506. SHAPE_LINE_CHAIN dragCornerInternal( const SHAPE_LINE_CHAIN& aOrigin, const VECTOR2I& aP, DIRECTION_45 aPreferredEndingDirection = DIRECTION_45() )
  507. {
  508. std::optional<SHAPE_LINE_CHAIN> picked;
  509. int i;
  510. int d = 2;
  511. wxASSERT( aOrigin.PointCount() > 0 );
  512. if( aOrigin.PointCount() == 1 )
  513. {
  514. return DIRECTION_45().BuildInitialTrace( aOrigin.CPoint( 0 ), aP );
  515. }
  516. else if( aOrigin.SegmentCount() == 1 )
  517. {
  518. DIRECTION_45 dir( aOrigin.CPoint( 0 ) - aOrigin.CPoint( 1 ) );
  519. return DIRECTION_45().BuildInitialTrace( aOrigin.CPoint( 0 ), aP, dir.IsDiagonal() );
  520. }
  521. //if( aOrigin.CSegment( -1 ).Length() > 100000 * 30 ) // fixme: constant/parameter?
  522. d = 1;
  523. for( i = aOrigin.SegmentCount() - d; i >= 0; i-- )
  524. {
  525. DIRECTION_45 d_start( aOrigin.CSegment( i ) );
  526. const VECTOR2I& p_start = aOrigin.CPoint( i );
  527. SHAPE_LINE_CHAIN paths[2];
  528. DIRECTION_45 dirs[2];
  529. DIRECTION_45 d_prev = ( i > 0 ? DIRECTION_45( aOrigin.CSegment( i-1 ) )
  530. : DIRECTION_45() );
  531. int dirCount = 0;
  532. for( int j = 0; j < 2; j++ )
  533. {
  534. paths[j] = d_start.BuildInitialTrace( p_start, aP, j );
  535. if( paths[j].SegmentCount() < 1 )
  536. continue;
  537. assert( dirCount < int( sizeof( dirs ) / sizeof( dirs[0] ) ) );
  538. dirs[dirCount] = DIRECTION_45( paths[j].CSegment( 0 ) );
  539. ++dirCount;
  540. }
  541. if( aPreferredEndingDirection != DIRECTION_45::UNDEFINED )
  542. {
  543. for( int j = 0; j < dirCount; j++ )
  544. {
  545. DIRECTION_45 endingDir( paths[j].CSegment(-1) );
  546. if( endingDir == aPreferredEndingDirection )
  547. {
  548. picked = paths[j];
  549. break;
  550. }
  551. }
  552. }
  553. if( !picked )
  554. {
  555. for( int j = 0; j < dirCount; j++ )
  556. {
  557. if( dirs[j] == d_start )
  558. {
  559. picked = paths[j];
  560. break;
  561. }
  562. }
  563. }
  564. if( picked )
  565. break;
  566. for( int j = 0; j < dirCount; j++ )
  567. {
  568. if( dirs[j].IsObtuse( d_prev ) )
  569. {
  570. picked = paths[j];
  571. break;
  572. }
  573. }
  574. if( picked )
  575. break;
  576. }
  577. if( picked )
  578. {
  579. SHAPE_LINE_CHAIN path = aOrigin.Slice( 0, i );
  580. path.Append( *picked );
  581. return path;
  582. }
  583. DIRECTION_45 dir( aOrigin.CPoint( -1 ) - aOrigin.CPoint( -2 ) );
  584. return DIRECTION_45().BuildInitialTrace( aOrigin.CPoint( 0 ), aP, dir.IsDiagonal() );
  585. }
  586. void LINE::dragCorner45( const VECTOR2I& aP, int aIndex, DIRECTION_45 aPreferredEndingDirection )
  587. {
  588. SHAPE_LINE_CHAIN path;
  589. int width = m_line.Width();
  590. VECTOR2I snapped = snapDraggedCorner( m_line, aP, aIndex );
  591. if( aIndex == 0 )
  592. {
  593. path = dragCornerInternal( m_line.Reverse(), snapped, aPreferredEndingDirection ).Reverse();
  594. }
  595. else if( aIndex == m_line.SegmentCount() )
  596. {
  597. path = dragCornerInternal( m_line, snapped, aPreferredEndingDirection );
  598. }
  599. else
  600. {
  601. // Are we next to an arc? Insert a new point so we slice correctly
  602. if( m_line.IsPtOnArc( static_cast<size_t>( aIndex ) + 1 ) )
  603. m_line.Insert( aIndex + 1, m_line.CPoint( aIndex + 1 ) );
  604. // fixme: awkward behaviour for "outwards" drags
  605. path = dragCornerInternal( m_line.Slice( 0, aIndex ), snapped, aPreferredEndingDirection );
  606. SHAPE_LINE_CHAIN path_rev =
  607. dragCornerInternal( m_line.Slice( aIndex, -1 ).Reverse(), snapped, aPreferredEndingDirection ).Reverse();
  608. path.Append( path_rev );
  609. }
  610. path.Simplify();
  611. path.SetWidth( width );
  612. m_line = path;
  613. }
  614. void LINE::dragCornerFree( const VECTOR2I& aP, int aIndex )
  615. {
  616. ssize_t idx = static_cast<ssize_t>( aIndex );
  617. ssize_t numpts = static_cast<ssize_t>( m_line.PointCount() );
  618. // If we're asked to drag the end of an arc, insert a new vertex to drag instead
  619. if( m_line.IsPtOnArc( idx ) )
  620. {
  621. if( idx == 0 || ( idx > 0 && !m_line.IsPtOnArc( idx - 1 ) ) )
  622. {
  623. m_line.Insert( idx, m_line.GetPoint( idx ) );
  624. }
  625. else if( ( idx == numpts - 1 ) || ( idx < numpts - 1 && !m_line.IsArcSegment( idx ) ) )
  626. {
  627. idx++;
  628. m_line.Insert( idx, m_line.GetPoint( idx ) );
  629. }
  630. else
  631. {
  632. wxASSERT_MSG( false, wxT( "Attempt to dragCornerFree in the middle of an arc!" ) );
  633. }
  634. }
  635. m_line.SetPoint( idx, aP );
  636. m_line.Simplify();
  637. }
  638. void LINE::DragCorner( const VECTOR2I& aP, int aIndex, bool aFreeAngle, DIRECTION_45 aPreferredEndingDirection )
  639. {
  640. wxCHECK_RET( aIndex >= 0, wxT( "Negative index passed to LINE::DragCorner" ) );
  641. if( aFreeAngle )
  642. {
  643. dragCornerFree( aP, aIndex );
  644. }
  645. else
  646. {
  647. dragCorner45( aP, aIndex, aPreferredEndingDirection );
  648. }
  649. }
  650. void LINE::DragSegment( const VECTOR2I& aP, int aIndex, bool aFreeAngle )
  651. {
  652. if( aFreeAngle )
  653. {
  654. assert( false );
  655. }
  656. else
  657. {
  658. dragSegment45( aP, aIndex );
  659. }
  660. }
  661. VECTOR2I LINE::snapDraggedCorner(
  662. const SHAPE_LINE_CHAIN& aPath, const VECTOR2I& aP, int aIndex ) const
  663. {
  664. int s_start = std::max( aIndex - 2, 0 );
  665. int s_end = std::min( aIndex + 2, aPath.SegmentCount() - 1 );
  666. int i, j;
  667. int best_dist = INT_MAX;
  668. VECTOR2I best_snap = aP;
  669. if( m_snapThreshhold <= 0 )
  670. return aP;
  671. for( i = s_start; i <= s_end; i++ )
  672. {
  673. const SEG& a = aPath.CSegment( i );
  674. for( j = s_start; j < i; j++ )
  675. {
  676. const SEG& b = aPath.CSegment( j );
  677. if( !( DIRECTION_45( a ).IsObtuse( DIRECTION_45( b ) ) ) )
  678. continue;
  679. OPT_VECTOR2I ip = a.IntersectLines( b );
  680. if( ip )
  681. {
  682. int dist = ( *ip - aP ).EuclideanNorm();
  683. if( dist < m_snapThreshhold && dist < best_dist )
  684. {
  685. best_dist = dist;
  686. best_snap = *ip;
  687. }
  688. }
  689. }
  690. }
  691. return best_snap;
  692. }
  693. VECTOR2I LINE::snapToNeighbourSegments(
  694. const SHAPE_LINE_CHAIN& aPath, const VECTOR2I& aP, int aIndex ) const
  695. {
  696. VECTOR2I snap_p[2];
  697. DIRECTION_45 dragDir( aPath.CSegment( aIndex ) );
  698. int snap_d[2] = { -1, -1 };
  699. if( m_snapThreshhold == 0 )
  700. return aP;
  701. if( aIndex >= 2 )
  702. {
  703. SEG s = aPath.CSegment( aIndex - 2 );
  704. if( DIRECTION_45( s ) == dragDir )
  705. snap_d[0] = s.LineDistance( aP );
  706. snap_p[0] = s.A;
  707. }
  708. if( aIndex < aPath.SegmentCount() - 2 )
  709. {
  710. SEG s = aPath.CSegment( aIndex + 2 );
  711. if( DIRECTION_45( s ) == dragDir )
  712. snap_d[1] = s.LineDistance( aP );
  713. snap_p[1] = s.A;
  714. }
  715. VECTOR2I best = aP;
  716. int minDist = INT_MAX;
  717. for( int i = 0; i < 2; i++ )
  718. {
  719. if( snap_d[i] >= 0 && snap_d[i] < minDist && snap_d[i] <= m_snapThreshhold )
  720. {
  721. minDist = snap_d[i];
  722. best = snap_p[i];
  723. }
  724. }
  725. return best;
  726. }
  727. void LINE::dragSegment45( const VECTOR2I& aP, int aIndex )
  728. {
  729. SHAPE_LINE_CHAIN path( m_line );
  730. VECTOR2I target( aP );
  731. wxASSERT( aIndex < m_line.PointCount() );
  732. SEG guideA[2], guideB[2];
  733. int index = aIndex;
  734. target = snapToNeighbourSegments( path, aP, aIndex );
  735. // We require a valid s_prev and s_next. If we are at the start or end of the line, we insert
  736. // a new point at the start or end so there is a zero-length segment for prev or next (we will
  737. // resize it as part of the drag operation). If we are next to an arc, we do this also, as we
  738. // cannot drag away one of the arc's points.
  739. if( index == 0 || path.IsPtOnArc( index ) )
  740. {
  741. path.Insert( index > 0 ? index + 1 : 0, path.CPoint( index ) );
  742. index++;
  743. }
  744. if( index == path.SegmentCount() - 1 )
  745. {
  746. path.Insert( path.PointCount() - 1, path.CPoint( -1 ) );
  747. }
  748. else if( path.IsPtOnArc( index + 1 ) )
  749. {
  750. path.Insert( index + 1, path.CPoint( index + 1 ) );
  751. }
  752. SEG dragged = path.CSegment( index );
  753. DIRECTION_45 drag_dir( dragged );
  754. SEG s_prev = path.CSegment( index - 1 );
  755. SEG s_next = path.CSegment( index + 1 );
  756. DIRECTION_45 dir_prev( s_prev );
  757. DIRECTION_45 dir_next( s_next );
  758. if( dir_prev == drag_dir )
  759. {
  760. dir_prev = dir_prev.Left();
  761. path.Insert( index, path.CPoint( index ) );
  762. index++;
  763. }
  764. else if( dir_prev == DIRECTION_45::UNDEFINED )
  765. {
  766. dir_prev = drag_dir.Left();
  767. }
  768. if( dir_next == drag_dir )
  769. {
  770. dir_next = dir_next.Right();
  771. path.Insert( index + 1, path.CPoint( index + 1 ) );
  772. }
  773. else if( dir_next == DIRECTION_45::UNDEFINED )
  774. {
  775. dir_next = drag_dir.Right();
  776. }
  777. s_prev = path.CSegment( index - 1 );
  778. s_next = path.CSegment( index + 1 );
  779. dragged = path.CSegment( index );
  780. if( aIndex == 0 )
  781. {
  782. guideA[0] = SEG( dragged.A, dragged.A + drag_dir.Right().ToVector() );
  783. guideA[1] = SEG( dragged.A, dragged.A + drag_dir.Left().ToVector() );
  784. }
  785. else
  786. {
  787. if( dir_prev.Angle( drag_dir )
  788. & ( DIRECTION_45::ANG_OBTUSE | DIRECTION_45::ANG_HALF_FULL ) )
  789. {
  790. guideA[0] = SEG( s_prev.A, s_prev.A + drag_dir.Left().ToVector() );
  791. guideA[1] = SEG( s_prev.A, s_prev.A + drag_dir.Right().ToVector() );
  792. }
  793. else
  794. guideA[0] = guideA[1] = SEG( dragged.A, dragged.A + dir_prev.ToVector() );
  795. }
  796. if( aIndex == m_line.SegmentCount() - 1 )
  797. {
  798. guideB[0] = SEG( dragged.B, dragged.B + drag_dir.Right().ToVector() );
  799. guideB[1] = SEG( dragged.B, dragged.B + drag_dir.Left().ToVector() );
  800. }
  801. else
  802. {
  803. if( dir_next.Angle( drag_dir )
  804. & ( DIRECTION_45::ANG_OBTUSE | DIRECTION_45::ANG_HALF_FULL ) )
  805. {
  806. guideB[0] = SEG( s_next.B, s_next.B + drag_dir.Left().ToVector() );
  807. guideB[1] = SEG( s_next.B, s_next.B + drag_dir.Right().ToVector() );
  808. }
  809. else
  810. guideB[0] = guideB[1] = SEG( dragged.B, dragged.B + dir_next.ToVector() );
  811. }
  812. SEG s_current( target, target + drag_dir.ToVector() );
  813. int best_len = INT_MAX;
  814. SHAPE_LINE_CHAIN best;
  815. for( int i = 0; i < 2; i++ )
  816. {
  817. for( int j = 0; j < 2; j++ )
  818. {
  819. OPT_VECTOR2I ip1 = s_current.IntersectLines( guideA[i] );
  820. OPT_VECTOR2I ip2 = s_current.IntersectLines( guideB[j] );
  821. SHAPE_LINE_CHAIN np;
  822. if( !ip1 || !ip2 )
  823. continue;
  824. SEG s1( s_prev.A, *ip1 );
  825. SEG s2( *ip1, *ip2 );
  826. SEG s3( *ip2, s_next.B );
  827. OPT_VECTOR2I ip;
  828. if( ( ip = s1.Intersect( s_next ) ) )
  829. {
  830. np.Append( s1.A );
  831. np.Append( *ip );
  832. np.Append( s_next.B );
  833. }
  834. else if( ( ip = s3.Intersect( s_prev ) ) )
  835. {
  836. np.Append( s_prev.A );
  837. np.Append( *ip );
  838. np.Append( s3.B );
  839. }
  840. else if( ( ip = s1.Intersect( s3 ) ) )
  841. {
  842. np.Append( s_prev.A );
  843. np.Append( *ip );
  844. np.Append( s_next.B );
  845. }
  846. else
  847. {
  848. np.Append( s_prev.A );
  849. np.Append( *ip1 );
  850. np.Append( *ip2 );
  851. np.Append( s_next.B );
  852. }
  853. if( np.Length() < best_len )
  854. {
  855. best_len = np.Length();
  856. best = np;
  857. }
  858. }
  859. }
  860. if( m_line.PointCount() == 1 )
  861. m_line = best;
  862. else if( aIndex == 0 )
  863. m_line.Replace( 0, 1, best );
  864. else if( aIndex == m_line.SegmentCount() - 1 )
  865. m_line.Replace( -2, -1, best );
  866. else
  867. m_line.Replace( aIndex, aIndex + 1, best );
  868. m_line.Simplify();
  869. }
  870. bool LINE::CompareGeometry( const LINE& aOther )
  871. {
  872. return m_line.CompareGeometry( aOther.m_line );
  873. }
  874. void LINE::Reverse()
  875. {
  876. m_line = m_line.Reverse();
  877. std::reverse( m_links.begin(), m_links.end() );
  878. }
  879. void LINE::AppendVia( const VIA& aVia )
  880. {
  881. if( m_line.PointCount() > 1 && aVia.Pos() == m_line.CPoint( 0 ) )
  882. {
  883. Reverse();
  884. }
  885. m_via = aVia.Clone();
  886. m_via->SetOwner( this );
  887. m_via->SetNet( m_net );
  888. }
  889. void LINE::LinkVia( VIA* aVia )
  890. {
  891. if( m_line.PointCount() > 1 && aVia->Pos() == m_line.CPoint( 0 ) )
  892. {
  893. Reverse();
  894. }
  895. m_via = aVia;
  896. Link( aVia );
  897. }
  898. void LINE::SetRank( int aRank )
  899. {
  900. m_rank = aRank;
  901. for( auto s : m_links )
  902. s->SetRank( aRank );
  903. }
  904. int LINE::Rank() const
  905. {
  906. int min_rank = INT_MAX;
  907. if( IsLinked() )
  908. {
  909. for( const LINKED_ITEM* item : m_links )
  910. min_rank = std::min( min_rank, item->Rank() );
  911. }
  912. else
  913. {
  914. min_rank = m_rank;
  915. }
  916. int rank = ( min_rank == INT_MAX ) ? -1 : min_rank;
  917. return rank;
  918. }
  919. void LINE::ClipVertexRange( int aStart, int aEnd )
  920. {
  921. /**
  922. * We need to figure out which joints to keep after the clip operation, because arcs will have
  923. * multiple vertices. It is assumed that anything calling this method will have determined the
  924. * vertex range to clip based on joints, meaning we will never clip in the middle of an arc.
  925. * Clipping in the middle of an arc would break this and various other things...
  926. */
  927. int firstLink = 0;
  928. int lastLink = std::max( 0, static_cast<int>( m_links.size() ) - 1 );
  929. int linkIdx = 0;
  930. int numPoints = static_cast<int>( m_line.PointCount() );
  931. for( int i = 0; i >= 0 && i < m_line.PointCount(); i = m_line.NextShape( i ) )
  932. {
  933. if( i <= aStart )
  934. firstLink = linkIdx;
  935. if( i < 0 || i >= aEnd - 1 || linkIdx >= lastLink )
  936. {
  937. lastLink = linkIdx;
  938. break;
  939. }
  940. linkIdx++;
  941. }
  942. wxASSERT( lastLink >= firstLink );
  943. m_line = m_line.Slice( aStart, aEnd );
  944. if( IsLinked() )
  945. {
  946. wxASSERT( m_links.size() < INT_MAX );
  947. wxASSERT( static_cast<int>( m_links.size() ) >= ( lastLink - firstLink ) );
  948. // Note: The range includes aEnd, but we have n-1 segments.
  949. std::rotate(
  950. m_links.begin(),
  951. m_links.begin() + firstLink,
  952. m_links.begin() + lastLink
  953. );
  954. m_links.resize( lastLink - firstLink + 1 );
  955. }
  956. }
  957. bool LINE::HasLoops() const
  958. {
  959. for( int i = 0; i < PointCount(); i++ )
  960. {
  961. for( int j = i + 2; j < PointCount(); j++ )
  962. {
  963. if( CPoint( i ) == CPoint( j ) )
  964. return true;
  965. }
  966. }
  967. return false;
  968. }
  969. static void extendBox( BOX2I& aBox, bool& aDefined, const VECTOR2I& aP )
  970. {
  971. if( aDefined )
  972. {
  973. aBox.Merge( aP );
  974. }
  975. else
  976. {
  977. aBox = BOX2I( aP, VECTOR2I( 0, 0 ) );
  978. aDefined = true;
  979. }
  980. }
  981. OPT_BOX2I LINE::ChangedArea( const LINE* aOther ) const
  982. {
  983. BOX2I area;
  984. bool areaDefined = false;
  985. int i_start = -1;
  986. int i_end_self = -1, i_end_other = -1;
  987. SHAPE_LINE_CHAIN self( m_line );
  988. self.Simplify();
  989. SHAPE_LINE_CHAIN other( aOther->m_line );
  990. other.Simplify();
  991. int np_self = self.PointCount();
  992. int np_other = other.PointCount();
  993. int n = std::min( np_self, np_other );
  994. for( int i = 0; i < n; i++ )
  995. {
  996. const VECTOR2I p1 = self.CPoint( i );
  997. const VECTOR2I p2 = other.CPoint( i );
  998. if( p1 != p2 )
  999. {
  1000. if( i != n - 1 )
  1001. {
  1002. SEG s = self.CSegment( i );
  1003. if( !s.Contains( p2 ) )
  1004. {
  1005. i_start = i;
  1006. break;
  1007. }
  1008. }
  1009. else
  1010. {
  1011. i_start = i;
  1012. break;
  1013. }
  1014. }
  1015. }
  1016. for( int i = 0; i < n; i++ )
  1017. {
  1018. const VECTOR2I p1 = self.CPoint( np_self - 1 - i );
  1019. const VECTOR2I p2 = other.CPoint( np_other - 1 - i );
  1020. if( p1 != p2 )
  1021. {
  1022. i_end_self = np_self - 1 - i;
  1023. i_end_other = np_other - 1 - i;
  1024. break;
  1025. }
  1026. }
  1027. if( i_start < 0 )
  1028. i_start = n;
  1029. if( i_end_self < 0 )
  1030. i_end_self = np_self - 1;
  1031. if( i_end_other < 0 )
  1032. i_end_other = np_other - 1;
  1033. for( int i = i_start; i <= i_end_self; i++ )
  1034. extendBox( area, areaDefined, self.CPoint( i ) );
  1035. for( int i = i_start; i <= i_end_other; i++ )
  1036. extendBox( area, areaDefined, other.CPoint( i ) );
  1037. if( areaDefined )
  1038. {
  1039. area.Inflate( std::max( Width(), aOther->Width() ) );
  1040. return area;
  1041. }
  1042. return OPT_BOX2I();
  1043. }
  1044. bool LINE::HasLockedSegments() const
  1045. {
  1046. for( const auto seg : m_links )
  1047. {
  1048. if( seg->Marker() & MK_LOCKED )
  1049. return true;
  1050. }
  1051. return false;
  1052. }
  1053. void LINE::Clear()
  1054. {
  1055. ClearLinks();
  1056. RemoveVia();
  1057. m_line.Clear();
  1058. }
  1059. void LINE::RemoveVia()
  1060. {
  1061. if( m_via )
  1062. {
  1063. if( ContainsLink( m_via ) )
  1064. Unlink( m_via );
  1065. if( m_via->BelongsTo( this ) )
  1066. delete m_via;
  1067. }
  1068. m_via = nullptr;
  1069. }
  1070. const std::string SEGMENT::Format( ) const
  1071. {
  1072. std::stringstream ss;
  1073. ss << ITEM::Format() << " ";
  1074. ss << m_seg.Format( false );
  1075. return ss.str();
  1076. }
  1077. int LINE::FindSegment( const SEGMENT* aSeg ) const
  1078. {
  1079. for( int i = 0; i < m_line.SegmentCount(); i++)
  1080. {
  1081. const SEG&s = m_line.CSegment(i);
  1082. if( s == aSeg->Seg() )
  1083. return i;
  1084. }
  1085. return -1;
  1086. }
  1087. }