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.

1468 lines
40 KiB

11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
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
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
11 years ago
  1. /*
  2. * KiRouter - a push-and-(sometimes-)shove PCB router
  3. *
  4. * Copyright (C) 2013-2014 CERN
  5. * Copyright (C) 2016 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 <geometry/shape_line_chain.h>
  22. #include <geometry/shape_rect.h>
  23. #include <geometry/shape_simple.h>
  24. #include <geometry/shape_file_io.h>
  25. #include <cmath>
  26. #include "pns_arc.h"
  27. #include "pns_line.h"
  28. #include "pns_diff_pair.h"
  29. #include "pns_node.h"
  30. #include "pns_solid.h"
  31. #include "pns_optimizer.h"
  32. #include "pns_utils.h"
  33. #include "pns_router.h"
  34. #include "pns_debug_decorator.h"
  35. namespace PNS {
  36. static DEBUG_DECORATOR *g_dbg;
  37. /**
  38. * Cost Estimator Methods
  39. */
  40. int COST_ESTIMATOR::CornerCost( const SEG& aA, const SEG& aB )
  41. {
  42. DIRECTION_45 dir_a( aA ), dir_b( aB );
  43. switch( dir_a.Angle( dir_b ) )
  44. {
  45. case DIRECTION_45::ANG_OBTUSE: return 10;
  46. case DIRECTION_45::ANG_STRAIGHT: return 5;
  47. case DIRECTION_45::ANG_ACUTE: return 50;
  48. case DIRECTION_45::ANG_RIGHT: return 30;
  49. case DIRECTION_45::ANG_HALF_FULL: return 60;
  50. default: return 100;
  51. }
  52. }
  53. int COST_ESTIMATOR::CornerCost( const SHAPE_LINE_CHAIN& aLine )
  54. {
  55. int total = 0;
  56. for( int i = 0; i < aLine.SegmentCount() - 1; ++i )
  57. total += CornerCost( aLine.CSegment( i ), aLine.CSegment( i + 1 ) );
  58. return total;
  59. }
  60. int COST_ESTIMATOR::CornerCost( const LINE& aLine )
  61. {
  62. return CornerCost( aLine.CLine() );
  63. }
  64. void COST_ESTIMATOR::Add( const LINE& aLine )
  65. {
  66. m_lengthCost += aLine.CLine().Length();
  67. m_cornerCost += CornerCost( aLine );
  68. }
  69. void COST_ESTIMATOR::Remove( const LINE& aLine )
  70. {
  71. m_lengthCost -= aLine.CLine().Length();
  72. m_cornerCost -= CornerCost( aLine );
  73. }
  74. void COST_ESTIMATOR::Replace( const LINE& aOldLine, const LINE& aNewLine )
  75. {
  76. m_lengthCost -= aOldLine.CLine().Length();
  77. m_cornerCost -= CornerCost( aOldLine );
  78. m_lengthCost += aNewLine.CLine().Length();
  79. m_cornerCost += CornerCost( aNewLine );
  80. }
  81. bool COST_ESTIMATOR::IsBetter( const COST_ESTIMATOR& aOther, double aLengthTolerance,
  82. double aCornerTolerance ) const
  83. {
  84. if( aOther.m_cornerCost < m_cornerCost && aOther.m_lengthCost < m_lengthCost )
  85. return true;
  86. else if( aOther.m_cornerCost < m_cornerCost * aCornerTolerance &&
  87. aOther.m_lengthCost < m_lengthCost * aLengthTolerance )
  88. return true;
  89. return false;
  90. }
  91. /**
  92. * Optimizer
  93. **/
  94. OPTIMIZER::OPTIMIZER( NODE* aWorld ) :
  95. m_world( aWorld ),
  96. m_collisionKindMask( ITEM::ANY_T ),
  97. m_effortLevel( MERGE_SEGMENTS )
  98. {
  99. }
  100. OPTIMIZER::~OPTIMIZER()
  101. {
  102. }
  103. struct OPTIMIZER::CACHE_VISITOR
  104. {
  105. CACHE_VISITOR( const ITEM* aOurItem, NODE* aNode, int aMask ) :
  106. m_ourItem( aOurItem ),
  107. m_collidingItem( NULL ),
  108. m_node( aNode ),
  109. m_mask( aMask )
  110. {}
  111. bool operator()( ITEM* aOtherItem )
  112. {
  113. if( !( m_mask & aOtherItem->Kind() ) )
  114. return true;
  115. if( !aOtherItem->Collide( m_ourItem, m_node ) )
  116. return true;
  117. m_collidingItem = aOtherItem;
  118. return false;
  119. }
  120. const ITEM* m_ourItem;
  121. ITEM* m_collidingItem;
  122. NODE* m_node;
  123. int m_mask;
  124. };
  125. void OPTIMIZER::cacheAdd( ITEM* aItem, bool aIsStatic = false )
  126. {
  127. if( m_cacheTags.find( aItem ) != m_cacheTags.end() )
  128. return;
  129. m_cache.Add( aItem );
  130. m_cacheTags[aItem].m_hits = 1;
  131. m_cacheTags[aItem].m_isStatic = aIsStatic;
  132. }
  133. void OPTIMIZER::removeCachedSegments( LINE* aLine, int aStartVertex, int aEndVertex )
  134. {
  135. if( !aLine->IsLinked() ) return;
  136. auto links = aLine->Links();
  137. if( aEndVertex < 0 )
  138. aEndVertex += aLine->PointCount();
  139. for( int i = aStartVertex; i < aEndVertex - 1; i++ )
  140. {
  141. LINKED_ITEM* s = links[i];
  142. m_cacheTags.erase( s );
  143. m_cache.Remove( s );
  144. }
  145. }
  146. void OPTIMIZER::CacheRemove( ITEM* aItem )
  147. {
  148. if( aItem->Kind() == ITEM::LINE_T )
  149. removeCachedSegments( static_cast<LINE*>( aItem ) );
  150. }
  151. void OPTIMIZER::ClearCache( bool aStaticOnly )
  152. {
  153. if( !aStaticOnly )
  154. {
  155. m_cacheTags.clear();
  156. m_cache.Clear();
  157. return;
  158. }
  159. for( auto i = m_cacheTags.begin(); i!= m_cacheTags.end(); ++i )
  160. {
  161. if( i->second.m_isStatic )
  162. {
  163. m_cache.Remove( i->first );
  164. m_cacheTags.erase( i->first );
  165. }
  166. }
  167. }
  168. bool AREA_CONSTRAINT::Check( int aVertex1, int aVertex2, const LINE* aOriginLine,
  169. const SHAPE_LINE_CHAIN& aCurrentPath,
  170. const SHAPE_LINE_CHAIN& aReplacement )
  171. {
  172. const VECTOR2I& p1 = aOriginLine->CPoint( aVertex1 );
  173. const VECTOR2I& p2 = aOriginLine->CPoint( aVertex2 );
  174. bool p1_in = m_allowedArea.Contains( p1 );
  175. bool p2_in = m_allowedArea.Contains( p2 );
  176. return p1_in || p2_in;
  177. }
  178. bool PRESERVE_VERTEX_CONSTRAINT::Check( int aVertex1, int aVertex2, const LINE* aOriginLine,
  179. const SHAPE_LINE_CHAIN& aCurrentPath,
  180. const SHAPE_LINE_CHAIN& aReplacement )
  181. {
  182. bool cv = false;
  183. for( int i = aVertex1; i < aVertex2; i++ )
  184. {
  185. SEG::ecoord dist = aCurrentPath.CSegment(i).SquaredDistance( m_v );
  186. if ( dist <= 1 )
  187. {
  188. cv = true;
  189. break;
  190. }
  191. }
  192. if( !cv )
  193. return true;
  194. for( int i = 0; i < aReplacement.SegmentCount(); i++ )
  195. {
  196. SEG::ecoord dist = aReplacement.CSegment(i).SquaredDistance( m_v );
  197. if ( dist <= 1 )
  198. return true;
  199. }
  200. return false;
  201. }
  202. bool RESTRICT_VERTEX_RANGE_CONSTRAINT::Check( int aVertex1, int aVertex2, const LINE* aOriginLine,
  203. const SHAPE_LINE_CHAIN& aCurrentPath,
  204. const SHAPE_LINE_CHAIN& aReplacement )
  205. {
  206. return true;
  207. }
  208. // fixme: integrate into SHAPE_LINE_CHAIN, check corner cases against current PointInside implementation
  209. static bool pointInside2( const SHAPE_LINE_CHAIN& aL, const VECTOR2I& aP )
  210. {
  211. if( !aL.IsClosed() || aL.SegmentCount() < 3 )
  212. return false;
  213. // returns 0 if false, +1 if true, -1 if pt ON polygon boundary
  214. int result = 0;
  215. size_t cnt = aL.PointCount();
  216. VECTOR2I ip = aL.CPoint( 0 );
  217. for( size_t i = 1; i <= cnt; ++i )
  218. {
  219. VECTOR2I ipNext = (i == cnt ? aL.CPoint( 0 ) : aL.CPoint( i ));
  220. if( ipNext.y == aP.y )
  221. {
  222. if( (ipNext.x ==aP.x) || ( ip.y == aP.y && ( (ipNext.x >aP.x) == (ip.x <aP.x) ) ) )
  223. return -1;
  224. }
  225. if( (ip.y <aP.y) != (ipNext.y <aP.y) )
  226. {
  227. if( ip.x >=aP.x )
  228. {
  229. if( ipNext.x >aP.x )
  230. result = 1 - result;
  231. else
  232. {
  233. double d = static_cast<double>( ip.x - aP.x ) *
  234. static_cast<double>( ipNext.y - aP.y ) -
  235. static_cast<double>( ipNext.x - aP.x ) *
  236. static_cast<double>( ip.y - aP.y );
  237. if( !d )
  238. return -1;
  239. if( (d > 0) == (ipNext.y > ip.y) )
  240. result = 1 - result;
  241. }
  242. }
  243. else
  244. {
  245. if( ipNext.x >aP.x )
  246. {
  247. double d = ((double)ip.x -aP.x) * ((double)ipNext.y -aP.y) -
  248. ((double)ipNext.x -aP.x) * ((double)ip.y -aP.y);
  249. if( !d )
  250. return -1;
  251. if( (d > 0) == (ipNext.y > ip.y) )
  252. result = 1 - result;
  253. }
  254. }
  255. }
  256. ip = ipNext;
  257. }
  258. return result > 0;
  259. }
  260. bool KEEP_TOPOLOGY_CONSTRAINT::Check( int aVertex1, int aVertex2, const LINE* aOriginLine,
  261. const SHAPE_LINE_CHAIN& aCurrentPath,
  262. const SHAPE_LINE_CHAIN& aReplacement )
  263. {
  264. SHAPE_LINE_CHAIN encPoly = aOriginLine->CLine().Slice( aVertex1, aVertex2 );
  265. // fixme: this is a remarkably shitty implementation...
  266. encPoly.Append( aReplacement.Reverse() );
  267. encPoly.SetClosed( true );
  268. BOX2I bb = encPoly.BBox();
  269. std::vector<JOINT*> joints;
  270. int cnt = m_world->QueryJoints( bb, joints, aOriginLine->Layers(), ITEM::SOLID_T );
  271. if( !cnt )
  272. return true;
  273. for( JOINT* j : joints )
  274. {
  275. if( j->Net() == aOriginLine->Net() )
  276. continue;
  277. if( pointInside2( encPoly, j->Pos() ) )
  278. {
  279. bool falsePositive = false;
  280. for( int k = 0; k < encPoly.PointCount(); k++ )
  281. {
  282. if( encPoly.CPoint(k) == j->Pos() )
  283. {
  284. falsePositive = true;
  285. break;
  286. }
  287. }
  288. if( !falsePositive )
  289. {
  290. //dbg->AddPoint(j->Pos(), 5);
  291. return false;
  292. }
  293. }
  294. }
  295. return true;
  296. }
  297. bool OPTIMIZER::checkColliding( ITEM* aItem, bool aUpdateCache )
  298. {
  299. CACHE_VISITOR v( aItem, m_world, m_collisionKindMask );
  300. return static_cast<bool>( m_world->CheckColliding( aItem ) );
  301. }
  302. void OPTIMIZER::ClearConstraints()
  303. {
  304. for( OPT_CONSTRAINT* c : m_constraints )
  305. delete c;
  306. m_constraints.clear();
  307. }
  308. void OPTIMIZER::AddConstraint ( OPT_CONSTRAINT *aConstraint )
  309. {
  310. m_constraints.push_back( aConstraint );
  311. }
  312. bool OPTIMIZER::checkConstraints( int aVertex1, int aVertex2, LINE* aOriginLine,
  313. const SHAPE_LINE_CHAIN& aCurrentPath,
  314. const SHAPE_LINE_CHAIN& aReplacement )
  315. {
  316. for( OPT_CONSTRAINT* c : m_constraints )
  317. {
  318. if( !c->Check( aVertex1, aVertex2, aOriginLine, aCurrentPath, aReplacement ) )
  319. return false;
  320. }
  321. return true;
  322. }
  323. bool OPTIMIZER::checkColliding( LINE* aLine, const SHAPE_LINE_CHAIN& aOptPath )
  324. {
  325. LINE tmp( *aLine, aOptPath );
  326. return checkColliding( &tmp );
  327. }
  328. bool OPTIMIZER::mergeObtuse( LINE* aLine )
  329. {
  330. SHAPE_LINE_CHAIN& line = aLine->Line();
  331. int step = line.PointCount() - 3;
  332. int iter = 0;
  333. int segs_pre = line.SegmentCount();
  334. if( step < 0 )
  335. return false;
  336. SHAPE_LINE_CHAIN current_path( line );
  337. while( 1 )
  338. {
  339. iter++;
  340. int n_segs = current_path.SegmentCount();
  341. int max_step = n_segs - 2;
  342. if( step > max_step )
  343. step = max_step;
  344. if( step < 2 )
  345. {
  346. line = current_path;
  347. return current_path.SegmentCount() < segs_pre;
  348. }
  349. bool found_anything = false;
  350. for( int n = 0; n < n_segs - step; n++ )
  351. {
  352. const SEG s1 = current_path.CSegment( n );
  353. const SEG s2 = current_path.CSegment( n + step );
  354. SEG s1opt, s2opt;
  355. if( DIRECTION_45( s1 ).IsObtuse( DIRECTION_45( s2 ) ) )
  356. {
  357. VECTOR2I ip = *s1.IntersectLines( s2 );
  358. s1opt = SEG( s1.A, ip );
  359. s2opt = SEG( ip, s2.B );
  360. if( DIRECTION_45( s1opt ).IsObtuse( DIRECTION_45( s2opt ) ) )
  361. {
  362. SHAPE_LINE_CHAIN opt_path;
  363. opt_path.Append( s1opt.A );
  364. opt_path.Append( s1opt.B );
  365. opt_path.Append( s2opt.B );
  366. LINE opt_track( *aLine, opt_path );
  367. if( !checkColliding( &opt_track ) )
  368. {
  369. current_path.Replace( s1.Index() + 1, s2.Index(), ip );
  370. // removeCachedSegments(aLine, s1.Index(), s2.Index());
  371. n_segs = current_path.SegmentCount();
  372. found_anything = true;
  373. break;
  374. }
  375. }
  376. }
  377. }
  378. if( !found_anything )
  379. {
  380. if( step <= 2 )
  381. {
  382. line = current_path;
  383. return line.SegmentCount() < segs_pre;
  384. }
  385. step--;
  386. }
  387. }
  388. return line.SegmentCount() < segs_pre;
  389. }
  390. bool OPTIMIZER::mergeFull( LINE* aLine )
  391. {
  392. SHAPE_LINE_CHAIN& line = aLine->Line();
  393. int step = line.SegmentCount() - 1;
  394. int segs_pre = line.SegmentCount();
  395. line.Simplify();
  396. if( step < 0 )
  397. return false;
  398. SHAPE_LINE_CHAIN current_path( line );
  399. while( 1 )
  400. {
  401. int n_segs = current_path.SegmentCount();
  402. int max_step = n_segs - 2;
  403. if( step > max_step )
  404. step = max_step;
  405. if( step < 1 )
  406. break;
  407. bool found_anything = mergeStep( aLine, current_path, step );
  408. if( !found_anything )
  409. step--;
  410. if( !step )
  411. break;
  412. }
  413. aLine->SetShape( current_path );
  414. return current_path.SegmentCount() < segs_pre;
  415. }
  416. bool OPTIMIZER::mergeColinear( LINE* aLine )
  417. {
  418. SHAPE_LINE_CHAIN& line = aLine->Line();
  419. int nSegs = line.SegmentCount();
  420. for( int segIdx = 0; segIdx < line.SegmentCount() - 1; ++segIdx )
  421. {
  422. SEG s1 = line.CSegment( segIdx );
  423. SEG s2 = line.CSegment( segIdx + 1 );
  424. if( s1.Collinear( s2 ) )
  425. line.Replace( segIdx, segIdx + 1, s1.A );
  426. }
  427. return line.SegmentCount() < nSegs;
  428. }
  429. bool OPTIMIZER::Optimize( LINE* aLine, LINE* aResult )
  430. {
  431. if( !aResult )
  432. {
  433. aResult = aLine;
  434. }
  435. else
  436. {
  437. *aResult = *aLine;
  438. aResult->ClearLinks();
  439. }
  440. bool hasArcs = aLine->ArcCount();
  441. bool rv = false;
  442. if( m_effortLevel & PRESERVE_VERTEX )
  443. {
  444. auto c = new PRESERVE_VERTEX_CONSTRAINT( m_world, m_preservedVertex );
  445. AddConstraint( c );
  446. }
  447. if( m_effortLevel & RESTRICT_VERTEX_RANGE )
  448. {
  449. auto c = new RESTRICT_VERTEX_RANGE_CONSTRAINT( m_world, m_restrictedVertexRange.first,
  450. m_restrictedVertexRange.second );
  451. AddConstraint( c );
  452. }
  453. if( m_effortLevel & KEEP_TOPOLOGY )
  454. {
  455. auto c = new KEEP_TOPOLOGY_CONSTRAINT( m_world );
  456. AddConstraint( c );
  457. }
  458. // TODO: Fix for arcs
  459. if( !hasArcs && m_effortLevel & MERGE_SEGMENTS )
  460. rv |= mergeFull( aResult );
  461. // TODO: Fix for arcs
  462. if( !hasArcs && m_effortLevel & MERGE_OBTUSE )
  463. rv |= mergeObtuse( aResult );
  464. if( m_effortLevel & MERGE_COLINEAR )
  465. rv |= mergeColinear( aResult );
  466. // TODO: Fix for arcs
  467. if( !hasArcs && m_effortLevel & SMART_PADS )
  468. rv |= runSmartPads( aResult );
  469. // TODO: Fix for arcs
  470. if( !hasArcs && m_effortLevel & FANOUT_CLEANUP )
  471. rv |= fanoutCleanup( aResult );
  472. return rv;
  473. }
  474. bool OPTIMIZER::mergeStep( LINE* aLine, SHAPE_LINE_CHAIN& aCurrentPath, int step )
  475. {
  476. int n_segs = aCurrentPath.SegmentCount();
  477. int cost_orig = COST_ESTIMATOR::CornerCost( aCurrentPath );
  478. if( aLine->SegmentCount() < 2 )
  479. return false;
  480. DIRECTION_45 orig_start( aLine->CSegment( 0 ) );
  481. DIRECTION_45 orig_end( aLine->CSegment( -1 ) );
  482. for( int n = 0; n < n_segs - step; n++ )
  483. {
  484. // Do not attempt to merge false segments that are part of an arc
  485. if( aCurrentPath.isArc( n ) || aCurrentPath.isArc( static_cast<std::size_t>( n ) + step ) )
  486. continue;
  487. const SEG s1 = aCurrentPath.CSegment( n );
  488. const SEG s2 = aCurrentPath.CSegment( n + step );
  489. SHAPE_LINE_CHAIN path[2];
  490. SHAPE_LINE_CHAIN* picked = NULL;
  491. int cost[2];
  492. for( int i = 0; i < 2; i++ )
  493. {
  494. SHAPE_LINE_CHAIN bypass = DIRECTION_45().BuildInitialTrace( s1.A, s2.B, i );
  495. cost[i] = INT_MAX;
  496. bool ok = false;
  497. if( !checkColliding( aLine, bypass ) )
  498. {
  499. ok = checkConstraints ( n, n + step + 1, aLine, aCurrentPath, bypass );
  500. }
  501. if( ok )
  502. {
  503. path[i] = aCurrentPath;
  504. path[i].Replace( s1.Index(), s2.Index(), bypass );
  505. path[i].Simplify();
  506. cost[i] = COST_ESTIMATOR::CornerCost( path[i] );
  507. }
  508. }
  509. if( cost[0] < cost_orig && cost[0] < cost[1] )
  510. picked = &path[0];
  511. else if( cost[1] < cost_orig )
  512. picked = &path[1];
  513. if( picked )
  514. {
  515. n_segs = aCurrentPath.SegmentCount();
  516. aCurrentPath = *picked;
  517. return true;
  518. }
  519. }
  520. return false;
  521. }
  522. OPTIMIZER::BREAKOUT_LIST OPTIMIZER::circleBreakouts( int aWidth, const SHAPE* aShape,
  523. bool aPermitDiagonal ) const
  524. {
  525. BREAKOUT_LIST breakouts;
  526. for( int angle = 0; angle < 360; angle += 45 )
  527. {
  528. const SHAPE_CIRCLE* cir = static_cast<const SHAPE_CIRCLE*>( aShape );
  529. SHAPE_LINE_CHAIN l;
  530. VECTOR2I p0 = cir->GetCenter();
  531. VECTOR2I v0( cir->GetRadius() * M_SQRT2, 0 );
  532. l.Append( p0 );
  533. l.Append( p0 + v0.Rotate( angle * M_PI / 180.0 ) );
  534. breakouts.push_back( l );
  535. }
  536. return breakouts;
  537. }
  538. OPTIMIZER::BREAKOUT_LIST OPTIMIZER::customBreakouts( int aWidth, const ITEM* aItem,
  539. bool aPermitDiagonal ) const
  540. {
  541. BREAKOUT_LIST breakouts;
  542. const SHAPE_SIMPLE* convex = static_cast<const SHAPE_SIMPLE*>( aItem->Shape() );
  543. BOX2I bbox = convex->BBox( 0 );
  544. VECTOR2I p0 = static_cast<const SOLID*>( aItem )->Pos();
  545. // must be large enough to guarantee intersecting the convex polygon
  546. int length = std::max( bbox.GetWidth(), bbox.GetHeight() ) / 2 + 5;
  547. for( int angle = 0; angle < 360; angle += ( aPermitDiagonal ? 45 : 90 ) )
  548. {
  549. SHAPE_LINE_CHAIN l;
  550. VECTOR2I v0( p0 + VECTOR2I( length, 0 ).Rotate( angle * M_PI / 180.0 ) );
  551. SHAPE_LINE_CHAIN::INTERSECTIONS intersections;
  552. int n = convex->Vertices().Intersect( SEG( p0, v0 ), intersections );
  553. // if n == 1 intersected a segment
  554. // if n == 2 intersected the common point of 2 segments
  555. // n == 0 can not happen I think, but...
  556. if( n > 0 )
  557. {
  558. l.Append( p0 );
  559. // for a breakout distance relative to the distance between
  560. // center and polygon edge
  561. //l.Append( intersections[0].p + (v0 - p0).Resize( (intersections[0].p - p0).EuclideanNorm() * 0.4 ) );
  562. // for an absolute breakout distance, e.g. 0.1 mm
  563. //l.Append( intersections[0].p + (v0 - p0).Resize( 100000 ) );
  564. // for the breakout right on the polygon edge
  565. l.Append( intersections[0].p );
  566. breakouts.push_back( l );
  567. }
  568. }
  569. return breakouts;
  570. }
  571. OPTIMIZER::BREAKOUT_LIST OPTIMIZER::rectBreakouts( int aWidth, const SHAPE* aShape,
  572. bool aPermitDiagonal ) const
  573. {
  574. const SHAPE_RECT* rect = static_cast<const SHAPE_RECT*>(aShape);
  575. VECTOR2I s = rect->GetSize();
  576. VECTOR2I c = rect->GetPosition() + VECTOR2I( s.x / 2, s.y / 2 );
  577. BREAKOUT_LIST breakouts;
  578. VECTOR2I d_offset;
  579. d_offset.x = ( s.x > s.y ) ? ( s.x - s.y ) / 2 : 0;
  580. d_offset.y = ( s.x < s.y ) ? ( s.y - s.x ) / 2 : 0;
  581. VECTOR2I d_vert = VECTOR2I( 0, s.y / 2 + aWidth );
  582. VECTOR2I d_horiz = VECTOR2I( s.x / 2 + aWidth, 0 );
  583. breakouts.push_back( SHAPE_LINE_CHAIN( { c, c + d_horiz } ) );
  584. breakouts.push_back( SHAPE_LINE_CHAIN( { c, c - d_horiz } ) );
  585. breakouts.push_back( SHAPE_LINE_CHAIN( { c, c + d_vert } ) );
  586. breakouts.push_back( SHAPE_LINE_CHAIN( { c, c - d_vert } ) );
  587. if( aPermitDiagonal )
  588. {
  589. int l = aWidth + std::min( s.x, s.y ) / 2;
  590. VECTOR2I d_diag;
  591. if( s.x >= s.y )
  592. {
  593. breakouts.push_back(
  594. SHAPE_LINE_CHAIN( { c, c + d_offset, c + d_offset + VECTOR2I( l, l ) } ) );
  595. breakouts.push_back(
  596. SHAPE_LINE_CHAIN( { c, c + d_offset, c + d_offset - VECTOR2I( -l, l ) } ) );
  597. breakouts.push_back(
  598. SHAPE_LINE_CHAIN( { c, c - d_offset, c - d_offset + VECTOR2I( -l, l ) } ) );
  599. breakouts.push_back(
  600. SHAPE_LINE_CHAIN( { c, c - d_offset, c - d_offset - VECTOR2I( l, l ) } ) );
  601. }
  602. else
  603. {
  604. // fixme: this could be done more efficiently
  605. breakouts.push_back(
  606. SHAPE_LINE_CHAIN( { c, c + d_offset, c + d_offset + VECTOR2I( l, l ) } ) );
  607. breakouts.push_back(
  608. SHAPE_LINE_CHAIN( { c, c - d_offset, c - d_offset - VECTOR2I( -l, l ) } ) );
  609. breakouts.push_back(
  610. SHAPE_LINE_CHAIN( { c, c + d_offset, c + d_offset + VECTOR2I( -l, l ) } ) );
  611. breakouts.push_back(
  612. SHAPE_LINE_CHAIN( { c, c - d_offset, c - d_offset - VECTOR2I( l, l ) } ) );
  613. }
  614. }
  615. return breakouts;
  616. }
  617. OPTIMIZER::BREAKOUT_LIST OPTIMIZER::computeBreakouts( int aWidth, const ITEM* aItem,
  618. bool aPermitDiagonal ) const
  619. {
  620. switch( aItem->Kind() )
  621. {
  622. case ITEM::VIA_T:
  623. {
  624. const VIA* via = static_cast<const VIA*>( aItem );
  625. return circleBreakouts( aWidth, via->Shape(), aPermitDiagonal );
  626. }
  627. case ITEM::SOLID_T:
  628. {
  629. const SHAPE* shape = aItem->Shape();
  630. switch( shape->Type() )
  631. {
  632. case SH_RECT:
  633. return rectBreakouts( aWidth, shape, aPermitDiagonal );
  634. case SH_SEGMENT:
  635. {
  636. const SHAPE_SEGMENT* seg = static_cast<const SHAPE_SEGMENT*> (shape);
  637. const SHAPE_RECT rect = ApproximateSegmentAsRect ( *seg );
  638. return rectBreakouts( aWidth, &rect, aPermitDiagonal );
  639. }
  640. case SH_CIRCLE:
  641. return circleBreakouts( aWidth, shape, aPermitDiagonal );
  642. case SH_SIMPLE:
  643. return customBreakouts( aWidth, aItem, aPermitDiagonal );
  644. default:
  645. break;
  646. }
  647. break;
  648. }
  649. default:
  650. break;
  651. }
  652. return BREAKOUT_LIST();
  653. }
  654. ITEM* OPTIMIZER::findPadOrVia( int aLayer, int aNet, const VECTOR2I& aP ) const
  655. {
  656. JOINT* jt = m_world->FindJoint( aP, aLayer, aNet );
  657. if( !jt )
  658. return NULL;
  659. for( ITEM* item : jt->LinkList() )
  660. {
  661. if( item->OfKind( ITEM::VIA_T | ITEM::SOLID_T ) )
  662. return item;
  663. }
  664. return NULL;
  665. }
  666. int OPTIMIZER::smartPadsSingle( LINE* aLine, ITEM* aPad, bool aEnd, int aEndVertex )
  667. {
  668. DIRECTION_45 dir;
  669. const int ForbiddenAngles = DIRECTION_45::ANG_ACUTE | DIRECTION_45::ANG_RIGHT |
  670. DIRECTION_45::ANG_HALF_FULL | DIRECTION_45::ANG_UNDEFINED;
  671. typedef std::tuple<int, long long int, SHAPE_LINE_CHAIN> RtVariant;
  672. std::vector<RtVariant> variants;
  673. SOLID* solid = dyn_cast<SOLID*>( aPad );
  674. // don't do optimized connections for offset pads
  675. if( solid && solid->Offset() != VECTOR2I( 0, 0 ) )
  676. return -1;
  677. // don't do optimization on vias, they are always round at the moment and the optimizer
  678. // will possibly mess up an intended via exit posture
  679. if( aPad->Kind() == ITEM::VIA_T )
  680. return -1;
  681. BREAKOUT_LIST breakouts = computeBreakouts( aLine->Width(), aPad, true );
  682. SHAPE_LINE_CHAIN line = ( aEnd ? aLine->CLine().Reverse() : aLine->CLine() );
  683. int p_end = std::min( aEndVertex, std::min( 3, line.PointCount() - 1 ) );
  684. // Start at 1 to find a potentially better breakout (0 is the pad connection)
  685. for( int p = 1; p <= p_end; p++ )
  686. {
  687. // If the line is contained inside the pad, don't optimize
  688. if( solid && solid->Shape() && !solid->Shape()->Collide(
  689. SEG( line.CPoint( 0 ), line.CPoint( p ) ), aLine->Width() / 2 ) )
  690. {
  691. continue;
  692. }
  693. for( SHAPE_LINE_CHAIN & breakout : breakouts )
  694. {
  695. for( int diag = 0; diag < 2; diag++ )
  696. {
  697. SHAPE_LINE_CHAIN v;
  698. SHAPE_LINE_CHAIN connect = dir.BuildInitialTrace(
  699. breakout.CPoint( -1 ), line.CPoint( p ), diag == 0 );
  700. DIRECTION_45 dir_bkout( breakout.CSegment( -1 ) );
  701. if( !connect.SegmentCount() )
  702. continue;
  703. int ang1 = dir_bkout.Angle( DIRECTION_45( connect.CSegment( 0 ) ) );
  704. if( ang1 & ForbiddenAngles )
  705. continue;
  706. if( breakout.Length() > line.Length() )
  707. continue;
  708. v = breakout;
  709. v.Append( connect );
  710. for( int i = p + 1; i < line.PointCount(); i++ )
  711. v.Append( line.CPoint( i ) );
  712. LINE tmp( *aLine, v );
  713. int cc = tmp.CountCorners( ForbiddenAngles );
  714. if( cc == 0 )
  715. {
  716. RtVariant vp;
  717. std::get<0>( vp ) = p;
  718. std::get<1>( vp ) = breakout.Length();
  719. std::get<2>( vp ) = aEnd ? v.Reverse() : v;
  720. std::get<2>( vp ).Simplify();
  721. variants.push_back( vp );
  722. }
  723. }
  724. }
  725. }
  726. // We attempt to minimize the corner cost (minimizes the segments and types of corners)
  727. // but given two, equally valid costs, we want to pick the longer pad exit. The logic
  728. // here is that if the pad is oblong, the track should not exit the shorter side and parallel
  729. // the pad but should follow the pad's preferential direction before exiting.
  730. // The baseline guess is to start with the existing line the user has drawn.
  731. int min_cost = COST_ESTIMATOR::CornerCost( *aLine );
  732. long long int max_length = 0;
  733. bool found = false;
  734. int p_best = -1;
  735. SHAPE_LINE_CHAIN l_best;
  736. for( RtVariant& vp : variants )
  737. {
  738. LINE tmp( *aLine, std::get<2>( vp ) );
  739. int cost = COST_ESTIMATOR::CornerCost( std::get<2>( vp ) );
  740. long long int len = std::get<1>( vp );
  741. if( !checkColliding( &tmp ) )
  742. {
  743. if( cost < min_cost || ( cost == min_cost && len > max_length ) )
  744. {
  745. l_best = std::get<2>( vp );
  746. p_best = std::get<0>( vp );
  747. found = true;
  748. if( cost <= min_cost )
  749. max_length = std::max<int>( len, max_length );
  750. min_cost = std::min( cost, min_cost );
  751. }
  752. }
  753. }
  754. if( found )
  755. {
  756. aLine->SetShape( l_best );
  757. return p_best;
  758. }
  759. return -1;
  760. }
  761. bool OPTIMIZER::runSmartPads( LINE* aLine )
  762. {
  763. SHAPE_LINE_CHAIN& line = aLine->Line();
  764. if( line.PointCount() < 3 )
  765. return false;
  766. VECTOR2I p_start = line.CPoint( 0 ), p_end = line.CPoint( -1 );
  767. ITEM* startPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_start );
  768. ITEM* endPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_end );
  769. int vtx = -1;
  770. if( startPad )
  771. vtx = smartPadsSingle( aLine, startPad, false, 3 );
  772. if( endPad )
  773. smartPadsSingle( aLine, endPad, true,
  774. vtx < 0 ? line.PointCount() - 1 : line.PointCount() - 1 - vtx );
  775. aLine->Line().Simplify();
  776. return true;
  777. }
  778. bool OPTIMIZER::Optimize( LINE* aLine, int aEffortLevel, NODE* aWorld, const VECTOR2I aV )
  779. {
  780. OPTIMIZER opt( aWorld );
  781. g_dbg = ROUTER::GetInstance()->GetInterface()->GetDebugDecorator();
  782. opt.SetEffortLevel( aEffortLevel );
  783. opt.SetCollisionMask( -1 );
  784. if( aEffortLevel & OPTIMIZER::PRESERVE_VERTEX )
  785. opt.SetPreserveVertex( aV );
  786. return opt.Optimize( aLine );
  787. }
  788. bool OPTIMIZER::fanoutCleanup( LINE* aLine )
  789. {
  790. if( aLine->PointCount() < 3 )
  791. return false;
  792. VECTOR2I p_start = aLine->CPoint( 0 ), p_end = aLine->CPoint( -1 );
  793. ITEM* startPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_start );
  794. ITEM* endPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_end );
  795. int thr = aLine->Width() * 10;
  796. int len = aLine->CLine().Length();
  797. if( !startPad )
  798. return false;
  799. bool startMatch = startPad->OfKind( ITEM::VIA_T | ITEM::SOLID_T );
  800. bool endMatch = false;
  801. if(endPad)
  802. {
  803. endMatch = endPad->OfKind( ITEM::VIA_T | ITEM::SOLID_T );
  804. }
  805. else
  806. {
  807. endMatch = aLine->EndsWithVia();
  808. }
  809. if( startMatch && endMatch && len < thr )
  810. {
  811. for( int i = 0; i < 2; i++ )
  812. {
  813. SHAPE_LINE_CHAIN l2 = DIRECTION_45().BuildInitialTrace( p_start, p_end, i );
  814. LINE repl;
  815. repl = LINE( *aLine, l2 );
  816. if( !m_world->CheckColliding( &repl ) )
  817. {
  818. aLine->SetShape( repl.CLine() );
  819. return true;
  820. }
  821. }
  822. }
  823. return false;
  824. }
  825. int findCoupledVertices( const VECTOR2I& aVertex, const SEG& aOrigSeg,
  826. const SHAPE_LINE_CHAIN& aCoupled, DIFF_PAIR* aPair, int* aIndices )
  827. {
  828. int count = 0;
  829. for( int i = 0; i < aCoupled.SegmentCount(); i++ )
  830. {
  831. SEG s = aCoupled.CSegment( i );
  832. VECTOR2I projOverCoupled = s.LineProject ( aVertex );
  833. if( s.ApproxParallel( aOrigSeg ) )
  834. {
  835. int64_t dist = int64_t{(( projOverCoupled - aVertex ).EuclideanNorm())} - aPair->Width();
  836. if( aPair->GapConstraint().Matches( dist ) )
  837. {
  838. *aIndices++ = i;
  839. count++;
  840. }
  841. }
  842. }
  843. return count;
  844. }
  845. bool verifyDpBypass( NODE* aNode, DIFF_PAIR* aPair, bool aRefIsP, const SHAPE_LINE_CHAIN& aNewRef,
  846. const SHAPE_LINE_CHAIN& aNewCoupled )
  847. {
  848. LINE refLine ( aRefIsP ? aPair->PLine() : aPair->NLine(), aNewRef );
  849. LINE coupledLine ( aRefIsP ? aPair->NLine() : aPair->PLine(), aNewCoupled );
  850. if( refLine.Collide( &coupledLine, aNode ) )
  851. return false;
  852. if( aNode->CheckColliding ( &refLine ) )
  853. return false;
  854. if( aNode->CheckColliding ( &coupledLine ) )
  855. return false;
  856. return true;
  857. }
  858. bool coupledBypass( NODE* aNode, DIFF_PAIR* aPair, bool aRefIsP, const SHAPE_LINE_CHAIN& aRef,
  859. const SHAPE_LINE_CHAIN& aRefBypass, const SHAPE_LINE_CHAIN& aCoupled,
  860. SHAPE_LINE_CHAIN& aNewCoupled )
  861. {
  862. int vStartIdx[1024]; // fixme: possible overflow
  863. int nStarts = findCoupledVertices( aRefBypass.CPoint( 0 ), aRefBypass.CSegment( 0 ),
  864. aCoupled, aPair, vStartIdx );
  865. DIRECTION_45 dir( aRefBypass.CSegment( 0 ) );
  866. int64_t bestLength = -1;
  867. bool found = false;
  868. SHAPE_LINE_CHAIN bestBypass;
  869. int si, ei;
  870. for( int i=0; i< nStarts; i++ )
  871. {
  872. for( int j = 1; j < aCoupled.PointCount() - 1; j++ )
  873. {
  874. int delta = std::abs ( vStartIdx[i] - j );
  875. if( delta > 1 )
  876. {
  877. const VECTOR2I& vs = aCoupled.CPoint( vStartIdx[i] );
  878. SHAPE_LINE_CHAIN bypass = dir.BuildInitialTrace( vs, aCoupled.CPoint(j),
  879. dir.IsDiagonal() );
  880. int64_t coupledLength = aPair->CoupledLength( aRef, bypass );
  881. SHAPE_LINE_CHAIN newCoupled = aCoupled;
  882. si = vStartIdx[i];
  883. ei = j;
  884. if(si < ei)
  885. newCoupled.Replace( si, ei, bypass );
  886. else
  887. newCoupled.Replace( ei, si, bypass.Reverse() );
  888. if( coupledLength > bestLength && verifyDpBypass( aNode, aPair, aRefIsP, aRef,
  889. newCoupled) )
  890. {
  891. bestBypass = newCoupled;
  892. bestLength = coupledLength;
  893. found = true;
  894. }
  895. }
  896. }
  897. }
  898. if( found )
  899. aNewCoupled = bestBypass;
  900. return found;
  901. }
  902. bool checkDpColliding( NODE* aNode, DIFF_PAIR* aPair, bool aIsP, const SHAPE_LINE_CHAIN& aPath )
  903. {
  904. LINE tmp ( aIsP ? aPair->PLine() : aPair->NLine(), aPath );
  905. return static_cast<bool>( aNode->CheckColliding( &tmp ) );
  906. }
  907. bool OPTIMIZER::mergeDpStep( DIFF_PAIR* aPair, bool aTryP, int step )
  908. {
  909. int n = 1;
  910. SHAPE_LINE_CHAIN currentPath = aTryP ? aPair->CP() : aPair->CN();
  911. SHAPE_LINE_CHAIN coupledPath = aTryP ? aPair->CN() : aPair->CP();
  912. int n_segs = currentPath.SegmentCount() - 1;
  913. int64_t clenPre = aPair->CoupledLength( currentPath, coupledPath );
  914. int64_t budget = clenPre / 10; // fixme: come up with somethig more intelligent here...
  915. while( n < n_segs - step )
  916. {
  917. const SEG s1 = currentPath.CSegment( n );
  918. const SEG s2 = currentPath.CSegment( n + step );
  919. DIRECTION_45 dir1( s1 );
  920. DIRECTION_45 dir2( s2 );
  921. if( dir1.IsObtuse( dir2 ) )
  922. {
  923. SHAPE_LINE_CHAIN bypass = DIRECTION_45().BuildInitialTrace( s1.A, s2.B, dir1.IsDiagonal() );
  924. SHAPE_LINE_CHAIN newRef;
  925. SHAPE_LINE_CHAIN newCoup;
  926. int64_t deltaCoupled = -1, deltaUni = -1;
  927. newRef = currentPath;
  928. newRef.Replace( s1.Index(), s2.Index(), bypass );
  929. deltaUni = aPair->CoupledLength ( newRef, coupledPath ) - clenPre + budget;
  930. if( coupledBypass( m_world, aPair, aTryP, newRef, bypass, coupledPath, newCoup ) )
  931. {
  932. deltaCoupled = aPair->CoupledLength( newRef, newCoup ) - clenPre + budget;
  933. if( deltaCoupled >= 0 )
  934. {
  935. newRef.Simplify();
  936. newCoup.Simplify();
  937. aPair->SetShape( newRef, newCoup, !aTryP );
  938. return true;
  939. }
  940. }
  941. else if( deltaUni >= 0 && verifyDpBypass( m_world, aPair, aTryP, newRef, coupledPath ) )
  942. {
  943. newRef.Simplify();
  944. coupledPath.Simplify();
  945. aPair->SetShape( newRef, coupledPath, !aTryP );
  946. return true;
  947. }
  948. }
  949. n++;
  950. }
  951. return false;
  952. }
  953. bool OPTIMIZER::mergeDpSegments( DIFF_PAIR* aPair )
  954. {
  955. int step_p = aPair->CP().SegmentCount() - 2;
  956. int step_n = aPair->CN().SegmentCount() - 2;
  957. while( 1 )
  958. {
  959. int n_segs_p = aPair->CP().SegmentCount();
  960. int n_segs_n = aPair->CN().SegmentCount();
  961. int max_step_p = n_segs_p - 2;
  962. int max_step_n = n_segs_n - 2;
  963. if( step_p > max_step_p )
  964. step_p = max_step_p;
  965. if( step_n > max_step_n )
  966. step_n = max_step_n;
  967. if( step_p < 1 && step_n < 1)
  968. break;
  969. bool found_anything_p = false;
  970. bool found_anything_n = false;
  971. if( step_p > 1 )
  972. found_anything_p = mergeDpStep( aPair, true, step_p );
  973. if( step_n > 1 )
  974. found_anything_n = mergeDpStep( aPair, false, step_n );
  975. if( !found_anything_n && !found_anything_p )
  976. {
  977. step_n--;
  978. step_p--;
  979. }
  980. }
  981. return true;
  982. }
  983. bool OPTIMIZER::Optimize( DIFF_PAIR* aPair )
  984. {
  985. return mergeDpSegments( aPair );
  986. }
  987. static int64_t shovedArea( const SHAPE_LINE_CHAIN& aOld, const SHAPE_LINE_CHAIN& aNew )
  988. {
  989. int64_t area = 0;
  990. const int oc = aOld.PointCount();
  991. const int nc = aNew.PointCount();
  992. const int total = oc + nc;
  993. for(int i = 0; i < total; i++)
  994. {
  995. int i_next = (i + 1 == total ? 0 : i + 1);
  996. const VECTOR2I &v0 = i < oc ? aOld.CPoint(i)
  997. : aNew.CPoint( nc - 1 - (i - oc) );
  998. const VECTOR2I &v1 = i_next < oc ? aOld.CPoint ( i_next )
  999. : aNew.CPoint( nc - 1 - (i_next - oc) );
  1000. area += -(int64_t) v0.y * v1.x + (int64_t) v0.x * v1.y;
  1001. }
  1002. return std::abs(area / 2);
  1003. }
  1004. bool tightenSegment( bool dir, NODE *aNode, const LINE& cur, const SHAPE_LINE_CHAIN& in,
  1005. SHAPE_LINE_CHAIN& out )
  1006. {
  1007. SEG a = in.CSegment(0);
  1008. SEG center = in.CSegment(1);
  1009. SEG b = in.CSegment(2);
  1010. DIRECTION_45 dirA ( a );
  1011. DIRECTION_45 dirCenter ( center );
  1012. DIRECTION_45 dirB ( b );
  1013. if (!dirA.IsObtuse( dirCenter) || !dirCenter.IsObtuse(dirB))
  1014. return false;
  1015. //VECTOR2I perp = (center.B - center.A).Perpendicular();
  1016. VECTOR2I guideA, guideB ;
  1017. SEG guide;
  1018. int initial;
  1019. //auto dbg = ROUTER::GetInstance()->GetInterface()->GetDebugDecorator();
  1020. if ( dirA.Angle ( dirB ) != DIRECTION_45::ANG_RIGHT )
  1021. return false;
  1022. {
  1023. /*
  1024. auto rC = *a.IntersectLines( b );
  1025. dbg->AddSegment ( SEG( center.A, rC ), 1 );
  1026. dbg->AddSegment ( SEG( center.B, rC ), 2 );
  1027. auto perp = dirCenter.Left().Left();
  1028. SEG sperp ( center.A, center.A + perp.ToVector() );
  1029. auto vpc = sperp.LineProject( rC );
  1030. auto vpa = sperp.LineProject( a.A );
  1031. auto vpb = sperp.LineProject( b.B );
  1032. auto da = (vpc - vpa).EuclideanNorm();
  1033. auto db = (vpc - vpb).EuclideanNorm();
  1034. auto vp = (da < db) ? vpa : vpb;
  1035. dbg->AddSegment ( SEG( vpc, vp ), 5 );
  1036. guide = SEG ( vpc, vp );
  1037. */
  1038. }
  1039. int da = a.Length();
  1040. int db = b.Length();
  1041. if( da < db )
  1042. guide = a;
  1043. else
  1044. guide = b;
  1045. initial = guide.Length();
  1046. int step = initial;
  1047. int current = step;
  1048. SHAPE_LINE_CHAIN snew;
  1049. while( step > 1 )
  1050. {
  1051. LINE l( cur );
  1052. snew.Clear();
  1053. snew.Append( a.A );
  1054. snew.Append( a.B + (a.A - a.B).Resize( current ) );
  1055. snew.Append( b.A + (b.B - b.A).Resize( current ) );
  1056. snew.Append( b.B );
  1057. step /= 2;
  1058. l.SetShape(snew);
  1059. if( aNode->CheckColliding(&l) )
  1060. current -= step;
  1061. else if ( current + step >= initial )
  1062. current = initial;
  1063. else
  1064. current += step;
  1065. //dbg->AddSegment ( SEG( center.A , a.LineProject( center.A + gr ) ), 3 );
  1066. //dbg->AddSegment ( SEG( center.A , center.A + guideA ), 3 );
  1067. //dbg->AddSegment ( SEG( center.B , center.B + guideB ), 4 );
  1068. if ( current == initial )
  1069. break;
  1070. }
  1071. out = snew;
  1072. //dbg->AddLine ( snew, 3, 100000 );
  1073. return true;
  1074. }
  1075. void Tighten( NODE *aNode, const SHAPE_LINE_CHAIN& aOldLine, const LINE& aNewLine, LINE& aOptimized )
  1076. {
  1077. LINE tmp;
  1078. if( aNewLine.SegmentCount() < 3 )
  1079. return;
  1080. SHAPE_LINE_CHAIN current ( aNewLine.CLine() );
  1081. for( int step = 0; step < 3; step++)
  1082. {
  1083. current.Simplify();
  1084. for( int i = 0; i <= current.SegmentCount() - 3; i++)
  1085. {
  1086. SHAPE_LINE_CHAIN l_in, l_out;
  1087. l_in = current.Slice(i, i+3);
  1088. for( int dir = 0; dir < 1; dir++)
  1089. {
  1090. if( tightenSegment( dir ? true : false, aNode, aNewLine, l_in, l_out ) )
  1091. {
  1092. SHAPE_LINE_CHAIN opt = current;
  1093. opt.Replace(i, i + 3, l_out);
  1094. auto optArea = std::abs(shovedArea( aOldLine, opt ));
  1095. auto prevArea = std::abs(shovedArea( aOldLine, current ));
  1096. if( optArea < prevArea )
  1097. current = opt;
  1098. break;
  1099. }
  1100. }
  1101. }
  1102. }
  1103. aOptimized = LINE(aNewLine, current);
  1104. //auto dbg = ROUTER::GetInstance()->GetInterface()->GetDebugDecorator();
  1105. //dbg->AddLine ( current, 4, 100000 );
  1106. }
  1107. }