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.

1550 lines
42 KiB

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