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.

1232 lines
32 KiB

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
10 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
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 <cmath>
  24. #include "pns_line.h"
  25. #include "pns_diff_pair.h"
  26. #include "pns_node.h"
  27. #include "pns_solid.h"
  28. #include "pns_optimizer.h"
  29. #include "../../include/geometry/shape_simple.h"
  30. #include "pns_utils.h"
  31. #include "pns_router.h"
  32. namespace PNS {
  33. /**
  34. * Cost Estimator Methods
  35. */
  36. int COST_ESTIMATOR::CornerCost( const SEG& aA, const SEG& aB )
  37. {
  38. DIRECTION_45 dir_a( aA ), dir_b( aB );
  39. switch( dir_a.Angle( dir_b ) )
  40. {
  41. case DIRECTION_45::ANG_OBTUSE:
  42. return 1;
  43. case DIRECTION_45::ANG_STRAIGHT:
  44. return 0;
  45. case DIRECTION_45::ANG_ACUTE:
  46. return 50;
  47. case DIRECTION_45::ANG_RIGHT:
  48. return 30;
  49. case DIRECTION_45::ANG_HALF_FULL:
  50. return 60;
  51. default:
  52. return 100;
  53. }
  54. }
  55. int COST_ESTIMATOR::CornerCost( const SHAPE_LINE_CHAIN& aLine )
  56. {
  57. int total = 0;
  58. for( int i = 0; i < aLine.SegmentCount() - 1; ++i )
  59. total += CornerCost( aLine.CSegment( i ), aLine.CSegment( i + 1 ) );
  60. return total;
  61. }
  62. int COST_ESTIMATOR::CornerCost( const LINE& aLine )
  63. {
  64. return CornerCost( aLine.CLine() );
  65. }
  66. void COST_ESTIMATOR::Add( LINE& aLine )
  67. {
  68. m_lengthCost += aLine.CLine().Length();
  69. m_cornerCost += CornerCost( aLine );
  70. }
  71. void COST_ESTIMATOR::Remove( LINE& aLine )
  72. {
  73. m_lengthCost -= aLine.CLine().Length();
  74. m_cornerCost -= CornerCost( aLine );
  75. }
  76. void COST_ESTIMATOR::Replace( LINE& aOldLine, LINE& aNewLine )
  77. {
  78. m_lengthCost -= aOldLine.CLine().Length();
  79. m_cornerCost -= CornerCost( aOldLine );
  80. m_lengthCost += aNewLine.CLine().Length();
  81. m_cornerCost += CornerCost( aNewLine );
  82. }
  83. bool COST_ESTIMATOR::IsBetter( COST_ESTIMATOR& aOther,
  84. double aLengthTolerance,
  85. double aCornerTolerance ) const
  86. {
  87. if( aOther.m_cornerCost < m_cornerCost && aOther.m_lengthCost < m_lengthCost )
  88. return true;
  89. else if( aOther.m_cornerCost < m_cornerCost * aCornerTolerance &&
  90. aOther.m_lengthCost < m_lengthCost * aLengthTolerance )
  91. return true;
  92. return false;
  93. }
  94. /**
  95. * Optimizer
  96. **/
  97. OPTIMIZER::OPTIMIZER( NODE* aWorld ) :
  98. m_world( aWorld ),
  99. m_collisionKindMask( ITEM::ANY_T ),
  100. m_effortLevel( MERGE_SEGMENTS ),
  101. m_keepPostures( false ),
  102. m_restrictAreaActive( false )
  103. {
  104. }
  105. OPTIMIZER::~OPTIMIZER()
  106. {
  107. }
  108. struct OPTIMIZER::CACHE_VISITOR
  109. {
  110. CACHE_VISITOR( const ITEM* aOurItem, NODE* aNode, int aMask ) :
  111. m_ourItem( aOurItem ),
  112. m_collidingItem( NULL ),
  113. m_node( aNode ),
  114. m_mask( aMask )
  115. {}
  116. bool operator()( ITEM* aOtherItem )
  117. {
  118. if( !( m_mask & aOtherItem->Kind() ) )
  119. return true;
  120. int clearance = m_node->GetClearance( aOtherItem, m_ourItem );
  121. if( !aOtherItem->Collide( m_ourItem, clearance, false, nullptr, m_node ) )
  122. return true;
  123. m_collidingItem = aOtherItem;
  124. return false;
  125. }
  126. const ITEM* m_ourItem;
  127. ITEM* m_collidingItem;
  128. NODE* m_node;
  129. int m_mask;
  130. };
  131. void OPTIMIZER::cacheAdd( ITEM* aItem, bool aIsStatic = false )
  132. {
  133. if( m_cacheTags.find( aItem ) != m_cacheTags.end() )
  134. return;
  135. m_cache.Add( aItem );
  136. m_cacheTags[aItem].m_hits = 1;
  137. m_cacheTags[aItem].m_isStatic = aIsStatic;
  138. }
  139. void OPTIMIZER::removeCachedSegments( LINE* aLine, int aStartVertex, int aEndVertex )
  140. {
  141. if( !aLine->IsLinked() ) return;
  142. LINE::SEGMENT_REFS& segs = aLine->LinkedSegments();
  143. if( aEndVertex < 0 )
  144. aEndVertex += aLine->PointCount();
  145. for( int i = aStartVertex; i < aEndVertex - 1; i++ )
  146. {
  147. SEGMENT* s = segs[i];
  148. m_cacheTags.erase( s );
  149. m_cache.Remove( s );
  150. }
  151. }
  152. void OPTIMIZER::CacheRemove( ITEM* aItem )
  153. {
  154. if( aItem->Kind() == ITEM::LINE_T )
  155. removeCachedSegments( static_cast<LINE*>( aItem ) );
  156. }
  157. void OPTIMIZER::CacheStaticItem( ITEM* aItem )
  158. {
  159. cacheAdd( aItem, true );
  160. }
  161. void OPTIMIZER::ClearCache( bool aStaticOnly )
  162. {
  163. if( !aStaticOnly )
  164. {
  165. m_cacheTags.clear();
  166. m_cache.Clear();
  167. return;
  168. }
  169. for( CachedItemTags::iterator i = m_cacheTags.begin(); i!= m_cacheTags.end(); ++i )
  170. {
  171. if( i->second.m_isStatic )
  172. {
  173. m_cache.Remove( i->first );
  174. m_cacheTags.erase( i->first );
  175. }
  176. }
  177. }
  178. class LINE_RESTRICTIONS
  179. {
  180. public:
  181. LINE_RESTRICTIONS() {};
  182. ~LINE_RESTRICTIONS() {};
  183. void Build( NODE* aWorld, LINE* aOriginLine, const SHAPE_LINE_CHAIN& aLine, const BOX2I& aRestrictedArea, bool aRestrictedAreaEnable );
  184. bool Check ( int aVertex1, int aVertex2, const SHAPE_LINE_CHAIN& aReplacement );
  185. void Dump();
  186. private:
  187. int allowedAngles( NODE* aWorld, const LINE* aLine, const VECTOR2I& aP, bool aFirst );
  188. struct RVERTEX
  189. {
  190. RVERTEX ( bool aRestricted, int aAllowedAngles ) :
  191. restricted( aRestricted ),
  192. allowedAngles( aAllowedAngles )
  193. {
  194. }
  195. bool restricted;
  196. int allowedAngles;
  197. };
  198. std::vector<RVERTEX> m_rs;
  199. };
  200. // fixme: use later
  201. int LINE_RESTRICTIONS::allowedAngles( NODE* aWorld, const LINE* aLine, const VECTOR2I& aP, bool aFirst )
  202. {
  203. JOINT* jt = aWorld->FindJoint( aP , aLine );
  204. if( !jt )
  205. return 0xff;
  206. DIRECTION_45 dirs [8];
  207. int n_dirs = 0;
  208. for( const ITEM* item : jt->Links().CItems() )
  209. {
  210. if( item->OfKind( ITEM::VIA_T ) || item->OfKind( ITEM::SOLID_T ) )
  211. return 0xff;
  212. else if( const SEGMENT* seg = dyn_cast<const SEGMENT*>( item ) )
  213. {
  214. SEG s = seg->Seg();
  215. if( s.A != aP )
  216. s.Reverse();
  217. if( n_dirs < 8 )
  218. dirs[n_dirs++] = aFirst ? DIRECTION_45( s ) : DIRECTION_45( s ).Opposite();
  219. }
  220. }
  221. const int angleMask = DIRECTION_45::ANG_OBTUSE | DIRECTION_45::ANG_HALF_FULL | DIRECTION_45::ANG_STRAIGHT;
  222. int outputMask = 0xff;
  223. for( int d = 0; d < 8; d++ )
  224. {
  225. DIRECTION_45 refDir( ( DIRECTION_45::Directions ) d );
  226. for( int i = 0; i < n_dirs; i++ )
  227. {
  228. if( !( refDir.Angle( dirs[i] ) & angleMask ) )
  229. outputMask &= ~refDir.Mask();
  230. }
  231. }
  232. //DrawDebugDirs( aP, outputMask, 3 );
  233. return 0xff;
  234. }
  235. void LINE_RESTRICTIONS::Build( NODE* aWorld, LINE* aOriginLine, const SHAPE_LINE_CHAIN& aLine, const BOX2I& aRestrictedArea, bool aRestrictedAreaEnable )
  236. {
  237. const SHAPE_LINE_CHAIN& l = aLine;
  238. VECTOR2I v_prev;
  239. int n = l.PointCount( );
  240. m_rs.reserve( n );
  241. for( int i = 0; i < n; i++ )
  242. {
  243. const VECTOR2I &v = l.CPoint( i );
  244. RVERTEX r( false, 0xff );
  245. if( aRestrictedAreaEnable )
  246. {
  247. bool exiting = ( i > 0 && aRestrictedArea.Contains( v_prev ) && !aRestrictedArea.Contains( v ) );
  248. bool entering = false;
  249. if( i != l.PointCount() - 1 )
  250. {
  251. const VECTOR2I& v_next = l.CPoint( i + 1 );
  252. entering = ( !aRestrictedArea.Contains( v ) && aRestrictedArea.Contains( v_next ) );
  253. }
  254. if( entering )
  255. {
  256. const SEG& sp = l.CSegment( i );
  257. r.allowedAngles = DIRECTION_45( sp ).Mask();
  258. }
  259. else if( exiting )
  260. {
  261. const SEG& sp = l.CSegment( i - 1 );
  262. r.allowedAngles = DIRECTION_45( sp ).Mask();
  263. }
  264. else
  265. {
  266. r.allowedAngles = ( !aRestrictedArea.Contains( v ) ) ? 0 : 0xff;
  267. r.restricted = r.allowedAngles ? false : true;
  268. }
  269. }
  270. v_prev = v;
  271. m_rs.push_back( r );
  272. }
  273. }
  274. void LINE_RESTRICTIONS::Dump()
  275. {
  276. }
  277. bool LINE_RESTRICTIONS::Check( int aVertex1, int aVertex2, const SHAPE_LINE_CHAIN& aReplacement )
  278. {
  279. if( m_rs.empty( ) )
  280. return true;
  281. for( int i = aVertex1; i <= aVertex2; i++ )
  282. if ( m_rs[i].restricted )
  283. return false;
  284. const RVERTEX& v1 = m_rs[ aVertex1 ];
  285. const RVERTEX& v2 = m_rs[ aVertex2 ];
  286. int m1 = DIRECTION_45( aReplacement.CSegment( 0 ) ).Mask();
  287. int m2;
  288. if( aReplacement.SegmentCount() == 1 )
  289. m2 = m1;
  290. else
  291. m2 = DIRECTION_45( aReplacement.CSegment( 1 ) ).Mask();
  292. return ( ( v1.allowedAngles & m1 ) != 0 ) &&
  293. ( ( v2.allowedAngles & m2 ) != 0 );
  294. }
  295. bool OPTIMIZER::checkColliding( ITEM* aItem, bool aUpdateCache )
  296. {
  297. CACHE_VISITOR v( aItem, m_world, m_collisionKindMask );
  298. return static_cast<bool>( m_world->CheckColliding( aItem ) );
  299. #if 0
  300. // something is wrong with the cache, need to investigate.
  301. m_cache.Query( aItem->Shape(), m_world->GetMaxClearance(), v, false );
  302. if( !v.m_collidingItem )
  303. {
  304. NODE::OPT_OBSTACLE obs = m_world->CheckColliding( aItem );
  305. if( obs )
  306. {
  307. if( aUpdateCache )
  308. cacheAdd( obs->m_item );
  309. return true;
  310. }
  311. }
  312. else
  313. {
  314. m_cacheTags[v.m_collidingItem].m_hits++;
  315. return true;
  316. }
  317. return false;
  318. #endif
  319. }
  320. bool OPTIMIZER::checkColliding( LINE* aLine, const SHAPE_LINE_CHAIN& aOptPath )
  321. {
  322. LINE tmp( *aLine, aOptPath );
  323. return checkColliding( &tmp );
  324. }
  325. bool OPTIMIZER::mergeObtuse( LINE* aLine )
  326. {
  327. SHAPE_LINE_CHAIN& line = aLine->Line();
  328. int step = line.PointCount() - 3;
  329. int iter = 0;
  330. int segs_pre = line.SegmentCount();
  331. if( step < 0 )
  332. return false;
  333. SHAPE_LINE_CHAIN current_path( line );
  334. while( 1 )
  335. {
  336. iter++;
  337. int n_segs = current_path.SegmentCount();
  338. int max_step = n_segs - 2;
  339. if( step > max_step )
  340. step = max_step;
  341. if( step < 2 )
  342. {
  343. line = current_path;
  344. return current_path.SegmentCount() < segs_pre;
  345. }
  346. bool found_anything = false;
  347. int n = 0;
  348. while( n < n_segs - step )
  349. {
  350. const SEG s1 = current_path.CSegment( n );
  351. const SEG s2 = current_path.CSegment( n + step );
  352. SEG s1opt, s2opt;
  353. if( DIRECTION_45( s1 ).IsObtuse( DIRECTION_45( s2 ) ) )
  354. {
  355. VECTOR2I ip = *s1.IntersectLines( s2 );
  356. if( s1.Distance( ip ) <= 1 || s2.Distance( ip ) <= 1 )
  357. {
  358. s1opt = SEG( s1.A, ip );
  359. s2opt = SEG( ip, s2.B );
  360. }
  361. else
  362. {
  363. s1opt = SEG( s1.A, ip );
  364. s2opt = SEG( ip, s2.B );
  365. }
  366. if( DIRECTION_45( s1opt ).IsObtuse( DIRECTION_45( s2opt ) ) )
  367. {
  368. SHAPE_LINE_CHAIN opt_path;
  369. opt_path.Append( s1opt.A );
  370. opt_path.Append( s1opt.B );
  371. opt_path.Append( s2opt.B );
  372. LINE opt_track( *aLine, opt_path );
  373. if( !checkColliding( &opt_track ) )
  374. {
  375. current_path.Replace( s1.Index() + 1, s2.Index(), ip );
  376. // removeCachedSegments(aLine, s1.Index(), s2.Index());
  377. n_segs = current_path.SegmentCount();
  378. found_anything = true;
  379. break;
  380. }
  381. }
  382. }
  383. n++;
  384. }
  385. if( !found_anything )
  386. {
  387. if( step <= 2 )
  388. {
  389. line = current_path;
  390. return line.SegmentCount() < segs_pre;
  391. }
  392. step--;
  393. }
  394. }
  395. return line.SegmentCount() < segs_pre;
  396. }
  397. bool OPTIMIZER::mergeFull( LINE* aLine )
  398. {
  399. SHAPE_LINE_CHAIN& line = aLine->Line();
  400. int step = line.SegmentCount() - 1;
  401. int segs_pre = line.SegmentCount();
  402. line.Simplify();
  403. if( step < 0 )
  404. return false;
  405. SHAPE_LINE_CHAIN current_path( line );
  406. while( 1 )
  407. {
  408. int n_segs = current_path.SegmentCount();
  409. int max_step = n_segs - 2;
  410. if( step > max_step )
  411. step = max_step;
  412. if( step < 1 )
  413. break;
  414. bool found_anything = mergeStep( aLine, current_path, step );
  415. if( !found_anything )
  416. step--;
  417. }
  418. aLine->SetShape( current_path );
  419. return current_path.SegmentCount() < segs_pre;
  420. }
  421. bool OPTIMIZER::Optimize( LINE* aLine, LINE* aResult )
  422. {
  423. if( !aResult )
  424. aResult = aLine;
  425. else
  426. *aResult = *aLine;
  427. m_keepPostures = false;
  428. bool rv = false;
  429. if( m_effortLevel & MERGE_SEGMENTS )
  430. rv |= mergeFull( aResult );
  431. if( m_effortLevel & MERGE_OBTUSE )
  432. rv |= mergeObtuse( aResult );
  433. if( m_effortLevel & SMART_PADS )
  434. rv |= runSmartPads( aResult );
  435. if( m_effortLevel & FANOUT_CLEANUP )
  436. rv |= fanoutCleanup( aResult );
  437. return rv;
  438. }
  439. bool OPTIMIZER::mergeStep( LINE* aLine, SHAPE_LINE_CHAIN& aCurrentPath, int step )
  440. {
  441. int n = 0;
  442. int n_segs = aCurrentPath.SegmentCount();
  443. int cost_orig = COST_ESTIMATOR::CornerCost( aCurrentPath );
  444. LINE_RESTRICTIONS restr;
  445. if( aLine->SegmentCount() < 4 )
  446. return false;
  447. DIRECTION_45 orig_start( aLine->CSegment( 0 ) );
  448. DIRECTION_45 orig_end( aLine->CSegment( -1 ) );
  449. restr.Build( m_world, aLine, aCurrentPath, m_restrictArea, m_restrictAreaActive );
  450. while( n < n_segs - step )
  451. {
  452. const SEG s1 = aCurrentPath.CSegment( n );
  453. const SEG s2 = aCurrentPath.CSegment( n + step );
  454. SHAPE_LINE_CHAIN path[2];
  455. SHAPE_LINE_CHAIN* picked = NULL;
  456. int cost[2];
  457. for( int i = 0; i < 2; i++ )
  458. {
  459. bool postureMatch = true;
  460. SHAPE_LINE_CHAIN bypass = DIRECTION_45().BuildInitialTrace( s1.A, s2.B, i );
  461. cost[i] = INT_MAX;
  462. bool restrictionsOK = restr.Check ( n, n + step + 1, bypass );
  463. if( n == 0 && orig_start != DIRECTION_45( bypass.CSegment( 0 ) ) )
  464. postureMatch = false;
  465. else if( n == n_segs - step && orig_end != DIRECTION_45( bypass.CSegment( -1 ) ) )
  466. postureMatch = false;
  467. if( restrictionsOK && (postureMatch || !m_keepPostures) && !checkColliding( aLine, bypass ) )
  468. {
  469. path[i] = aCurrentPath;
  470. path[i].Replace( s1.Index(), s2.Index(), bypass );
  471. path[i].Simplify();
  472. cost[i] = COST_ESTIMATOR::CornerCost( path[i] );
  473. }
  474. }
  475. if( cost[0] < cost_orig && cost[0] < cost[1] )
  476. picked = &path[0];
  477. else if( cost[1] < cost_orig )
  478. picked = &path[1];
  479. if( picked )
  480. {
  481. n_segs = aCurrentPath.SegmentCount();
  482. aCurrentPath = *picked;
  483. return true;
  484. }
  485. n++;
  486. }
  487. return false;
  488. }
  489. OPTIMIZER::BREAKOUT_LIST OPTIMIZER::circleBreakouts( int aWidth,
  490. const SHAPE* aShape, bool aPermitDiagonal ) const
  491. {
  492. BREAKOUT_LIST breakouts;
  493. for( int angle = 0; angle < 360; angle += 45 )
  494. {
  495. const SHAPE_CIRCLE* cir = static_cast<const SHAPE_CIRCLE*>( aShape );
  496. SHAPE_LINE_CHAIN l;
  497. VECTOR2I p0 = cir->GetCenter();
  498. VECTOR2I v0( cir->GetRadius() * M_SQRT2, 0 );
  499. l.Append( p0 );
  500. l.Append( p0 + v0.Rotate( angle * M_PI / 180.0 ) );
  501. breakouts.push_back( l );
  502. }
  503. return breakouts;
  504. }
  505. OPTIMIZER::BREAKOUT_LIST OPTIMIZER::customBreakouts( int aWidth,
  506. const ITEM* aItem, bool aPermitDiagonal ) const
  507. {
  508. BREAKOUT_LIST breakouts;
  509. const SHAPE_SIMPLE* convex = static_cast<const SHAPE_SIMPLE*>( aItem->Shape() );
  510. BOX2I bbox = convex->BBox( 0 );
  511. VECTOR2I p0 = static_cast<const SOLID*>( aItem )->Pos();
  512. // must be large enough to guarantee intersecting the convex polygon
  513. int length = bbox.GetSize().EuclideanNorm() / 2 + 5;
  514. for( int angle = 0; angle < 360; angle += ( aPermitDiagonal ? 45 : 90 ) )
  515. {
  516. SHAPE_LINE_CHAIN l;
  517. VECTOR2I v0( p0 + VECTOR2I( length, 0 ).Rotate( angle * M_PI / 180.0 ) );
  518. SHAPE_LINE_CHAIN::INTERSECTIONS intersections;
  519. int n = convex->Vertices().Intersect( SEG( p0, v0 ), intersections );
  520. // if n == 1 intersected a segment
  521. // if n == 2 intersected the common point of 2 segments
  522. // n == 0 can not happen I think, but...
  523. if( n > 0 )
  524. {
  525. l.Append( p0 );
  526. // for a breakout distance relative to the distance between
  527. // center and polygon edge
  528. //l.Append( intersections[0].p + (v0 - p0).Resize( (intersections[0].p - p0).EuclideanNorm() * 0.4 ) );
  529. // for an absolute breakout distance, e.g. 0.1 mm
  530. l.Append( intersections[0].p + (v0 - p0).Resize( 100000 ) );
  531. // for the breakout right on the polygon edge
  532. //l.Append( intersections[0].p );
  533. breakouts.push_back( l );
  534. }
  535. }
  536. return breakouts;
  537. }
  538. OPTIMIZER::BREAKOUT_LIST OPTIMIZER::rectBreakouts( int aWidth,
  539. const SHAPE* aShape, bool aPermitDiagonal ) const
  540. {
  541. const SHAPE_RECT* rect = static_cast<const SHAPE_RECT*>(aShape);
  542. VECTOR2I s = rect->GetSize(), c = rect->GetPosition() + VECTOR2I( s.x / 2, s.y / 2 );
  543. BREAKOUT_LIST breakouts;
  544. VECTOR2I d_offset;
  545. d_offset.x = ( s.x > s.y ) ? ( s.x - s.y ) / 2 : 0;
  546. d_offset.y = ( s.x < s.y ) ? ( s.y - s.x ) / 2 : 0;
  547. VECTOR2I d_vert = VECTOR2I( 0, s.y / 2 + aWidth );
  548. VECTOR2I d_horiz = VECTOR2I( s.x / 2 + aWidth, 0 );
  549. breakouts.push_back( SHAPE_LINE_CHAIN( c, c + d_horiz ) );
  550. breakouts.push_back( SHAPE_LINE_CHAIN( c, c - d_horiz ) );
  551. breakouts.push_back( SHAPE_LINE_CHAIN( c, c + d_vert ) );
  552. breakouts.push_back( SHAPE_LINE_CHAIN( c, c - d_vert ) );
  553. if( aPermitDiagonal )
  554. {
  555. int l = aWidth + std::min( s.x, s.y ) / 2;
  556. VECTOR2I d_diag;
  557. if( s.x >= s.y )
  558. {
  559. breakouts.push_back( SHAPE_LINE_CHAIN( c, c + d_offset,
  560. c + d_offset + VECTOR2I( l, l ) ) );
  561. breakouts.push_back( SHAPE_LINE_CHAIN( c, c + d_offset,
  562. c + d_offset - VECTOR2I( -l, l ) ) );
  563. breakouts.push_back( SHAPE_LINE_CHAIN( c, c - d_offset,
  564. c - d_offset + VECTOR2I( -l, l ) ) );
  565. breakouts.push_back( SHAPE_LINE_CHAIN( c, c - d_offset,
  566. c - d_offset - VECTOR2I( l, l ) ) );
  567. }
  568. else
  569. {
  570. // fixme: this could be done more efficiently
  571. breakouts.push_back( SHAPE_LINE_CHAIN( c, c + d_offset,
  572. c + d_offset + VECTOR2I( l, l ) ) );
  573. breakouts.push_back( SHAPE_LINE_CHAIN( c, c - d_offset,
  574. c - d_offset - VECTOR2I( -l, l ) ) );
  575. breakouts.push_back( SHAPE_LINE_CHAIN( c, c + d_offset,
  576. c + d_offset + VECTOR2I( -l, l ) ) );
  577. breakouts.push_back( SHAPE_LINE_CHAIN( c, c - d_offset,
  578. c - d_offset - VECTOR2I( l, l ) ) );
  579. }
  580. }
  581. return breakouts;
  582. }
  583. OPTIMIZER::BREAKOUT_LIST OPTIMIZER::computeBreakouts( int aWidth,
  584. const ITEM* aItem, bool aPermitDiagonal ) const
  585. {
  586. switch( aItem->Kind() )
  587. {
  588. case ITEM::VIA_T:
  589. {
  590. const VIA* via = static_cast<const VIA*>( aItem );
  591. return circleBreakouts( aWidth, via->Shape(), aPermitDiagonal );
  592. }
  593. case ITEM::SOLID_T:
  594. {
  595. const SHAPE* shape = aItem->Shape();
  596. switch( shape->Type() )
  597. {
  598. case SH_RECT:
  599. return rectBreakouts( aWidth, shape, aPermitDiagonal );
  600. case SH_SEGMENT:
  601. {
  602. const SHAPE_SEGMENT* seg = static_cast<const SHAPE_SEGMENT*> (shape);
  603. const SHAPE_RECT rect = ApproximateSegmentAsRect ( *seg );
  604. return rectBreakouts( aWidth, &rect, aPermitDiagonal );
  605. }
  606. case SH_CIRCLE:
  607. return circleBreakouts( aWidth, shape, aPermitDiagonal );
  608. case SH_SIMPLE:
  609. return customBreakouts( aWidth, aItem, aPermitDiagonal );
  610. default:
  611. break;
  612. }
  613. break;
  614. }
  615. default:
  616. break;
  617. }
  618. return BREAKOUT_LIST();
  619. }
  620. ITEM* OPTIMIZER::findPadOrVia( int aLayer, int aNet, const VECTOR2I& aP ) const
  621. {
  622. JOINT* jt = m_world->FindJoint( aP, aLayer, aNet );
  623. if( !jt )
  624. return NULL;
  625. for( ITEM* item : jt->LinkList() )
  626. {
  627. if( item->OfKind( ITEM::VIA_T | ITEM::SOLID_T ) )
  628. return item;
  629. }
  630. return NULL;
  631. }
  632. int OPTIMIZER::smartPadsSingle( LINE* aLine, ITEM* aPad, bool aEnd, int aEndVertex )
  633. {
  634. int min_cost = INT_MAX; // COST_ESTIMATOR::CornerCost( line );
  635. int min_len = INT_MAX;
  636. DIRECTION_45 dir;
  637. const int ForbiddenAngles = DIRECTION_45::ANG_ACUTE | DIRECTION_45::ANG_RIGHT |
  638. DIRECTION_45::ANG_HALF_FULL | DIRECTION_45::ANG_UNDEFINED;
  639. typedef std::pair<int, SHAPE_LINE_CHAIN> RtVariant;
  640. std::vector<RtVariant> variants;
  641. SOLID* solid = dyn_cast<SOLID*>( aPad );
  642. // don't do optimized connections for offset pads
  643. if( solid && solid->Offset() != VECTOR2I( 0, 0 ) )
  644. return -1;
  645. BREAKOUT_LIST breakouts = computeBreakouts( aLine->Width(), aPad, true );
  646. SHAPE_LINE_CHAIN line = ( aEnd ? aLine->CLine().Reverse() : aLine->CLine() );
  647. int p_end = std::min( aEndVertex, std::min( 3, line.PointCount() - 1 ) );
  648. for( int p = 1; p <= p_end; p++ )
  649. {
  650. for( SHAPE_LINE_CHAIN & l : breakouts ) {
  651. for( int diag = 0; diag < 2; diag++ )
  652. {
  653. SHAPE_LINE_CHAIN v;
  654. SHAPE_LINE_CHAIN connect = dir.BuildInitialTrace( l.CPoint( -1 ),
  655. line.CPoint( p ), diag == 0 );
  656. DIRECTION_45 dir_bkout( l.CSegment( -1 ) );
  657. if(!connect.SegmentCount())
  658. continue;
  659. int ang1 = dir_bkout.Angle( DIRECTION_45( connect.CSegment( 0 ) ) );
  660. int ang2 = 0;
  661. if( (ang1 | ang2) & ForbiddenAngles )
  662. continue;
  663. if( l.Length() > line.Length() )
  664. continue;
  665. v = l;
  666. v.Append( connect );
  667. for( int i = p + 1; i < line.PointCount(); i++ )
  668. v.Append( line.CPoint( i ) );
  669. LINE tmp( *aLine, v );
  670. int cc = tmp.CountCorners( ForbiddenAngles );
  671. if( cc == 0 )
  672. {
  673. RtVariant vp;
  674. vp.first = p;
  675. vp.second = aEnd ? v.Reverse() : v;
  676. vp.second.Simplify();
  677. variants.push_back( vp );
  678. }
  679. }
  680. }
  681. }
  682. SHAPE_LINE_CHAIN l_best;
  683. bool found = false;
  684. int p_best = -1;
  685. for( RtVariant& vp : variants )
  686. {
  687. LINE tmp( *aLine, vp.second );
  688. int cost = COST_ESTIMATOR::CornerCost( vp.second );
  689. int len = vp.second.Length();
  690. if( !checkColliding( &tmp ) )
  691. {
  692. if( cost < min_cost || ( cost == min_cost && len < min_len ) )
  693. {
  694. l_best = vp.second;
  695. p_best = vp.first;
  696. found = true;
  697. if( cost == min_cost )
  698. min_len = std::min( len, min_len );
  699. min_cost = std::min( cost, min_cost );
  700. }
  701. }
  702. }
  703. if( found )
  704. {
  705. aLine->SetShape( l_best );
  706. return p_best;
  707. }
  708. return -1;
  709. }
  710. bool OPTIMIZER::runSmartPads( LINE* aLine )
  711. {
  712. SHAPE_LINE_CHAIN& line = aLine->Line();
  713. if( line.PointCount() < 3 )
  714. return false;
  715. VECTOR2I p_start = line.CPoint( 0 ), p_end = line.CPoint( -1 );
  716. ITEM* startPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_start );
  717. ITEM* endPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_end );
  718. int vtx = -1;
  719. if( startPad )
  720. vtx = smartPadsSingle( aLine, startPad, false, 3 );
  721. if( endPad )
  722. smartPadsSingle( aLine, endPad, true,
  723. vtx < 0 ? line.PointCount() - 1 : line.PointCount() - 1 - vtx );
  724. aLine->Line().Simplify();
  725. return true;
  726. }
  727. bool OPTIMIZER::Optimize( LINE* aLine, int aEffortLevel, NODE* aWorld )
  728. {
  729. OPTIMIZER opt( aWorld );
  730. opt.SetEffortLevel( aEffortLevel );
  731. opt.SetCollisionMask( -1 );
  732. return opt.Optimize( aLine );
  733. }
  734. bool OPTIMIZER::fanoutCleanup( LINE* aLine )
  735. {
  736. if( aLine->PointCount() < 3 )
  737. return false;
  738. VECTOR2I p_start = aLine->CPoint( 0 ), p_end = aLine->CPoint( -1 );
  739. ITEM* startPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_start );
  740. ITEM* endPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_end );
  741. int thr = aLine->Width() * 10;
  742. int len = aLine->CLine().Length();
  743. if( !startPad )
  744. return false;
  745. bool startMatch = startPad->OfKind( ITEM::VIA_T | ITEM::SOLID_T );
  746. bool endMatch = false;
  747. if(endPad)
  748. {
  749. endMatch = endPad->OfKind( ITEM::VIA_T | ITEM::SOLID_T );
  750. }
  751. else
  752. {
  753. endMatch = aLine->EndsWithVia();
  754. }
  755. if( startMatch && endMatch && len < thr )
  756. {
  757. for( int i = 0; i < 2; i++ )
  758. {
  759. SHAPE_LINE_CHAIN l2 = DIRECTION_45().BuildInitialTrace( p_start, p_end, i );
  760. LINE repl;
  761. repl = LINE( *aLine, l2 );
  762. if( !m_world->CheckColliding( &repl ) )
  763. {
  764. aLine->SetShape( repl.CLine() );
  765. return true;
  766. }
  767. }
  768. }
  769. return false;
  770. }
  771. int findCoupledVertices( const VECTOR2I& aVertex, const SEG& aOrigSeg, const SHAPE_LINE_CHAIN& aCoupled, DIFF_PAIR* aPair, int* aIndices )
  772. {
  773. int count = 0;
  774. for ( int i = 0; i < aCoupled.SegmentCount(); i++ )
  775. {
  776. SEG s = aCoupled.CSegment( i );
  777. VECTOR2I projOverCoupled = s.LineProject ( aVertex );
  778. if( s.ApproxParallel ( aOrigSeg ) )
  779. {
  780. int64_t dist = ( projOverCoupled - aVertex ).EuclideanNorm() - aPair->Width();
  781. if( aPair->GapConstraint().Matches( dist ) )
  782. {
  783. *aIndices++ = i;
  784. count++;
  785. }
  786. }
  787. }
  788. return count;
  789. }
  790. bool verifyDpBypass( NODE* aNode, DIFF_PAIR* aPair, bool aRefIsP, const SHAPE_LINE_CHAIN& aNewRef, const SHAPE_LINE_CHAIN& aNewCoupled )
  791. {
  792. LINE refLine ( aRefIsP ? aPair->PLine() : aPair->NLine(), aNewRef );
  793. LINE coupledLine ( aRefIsP ? aPair->NLine() : aPair->PLine(), aNewCoupled );
  794. if( aNode->CheckColliding( &refLine, &coupledLine, ITEM::ANY_T, aPair->Gap() - 10 ) )
  795. return false;
  796. if( aNode->CheckColliding ( &refLine ) )
  797. return false;
  798. if( aNode->CheckColliding ( &coupledLine ) )
  799. return false;
  800. return true;
  801. }
  802. bool coupledBypass( NODE* aNode, DIFF_PAIR* aPair, bool aRefIsP, const SHAPE_LINE_CHAIN& aRef, const SHAPE_LINE_CHAIN& aRefBypass, const SHAPE_LINE_CHAIN& aCoupled, SHAPE_LINE_CHAIN& aNewCoupled )
  803. {
  804. int vStartIdx[1024]; // fixme: possible overflow
  805. int nStarts = findCoupledVertices( aRefBypass.CPoint( 0 ), aRefBypass.CSegment( 0 ), aCoupled, aPair, vStartIdx );
  806. DIRECTION_45 dir( aRefBypass.CSegment( 0 ) );
  807. int64_t bestLength = -1;
  808. bool found = false;
  809. SHAPE_LINE_CHAIN bestBypass;
  810. int si, ei;
  811. for( int i=0; i< nStarts; i++ )
  812. {
  813. for( int j = 1; j < aCoupled.PointCount() - 1; j++ )
  814. {
  815. int delta = std::abs ( vStartIdx[i] - j );
  816. if( delta > 1 )
  817. {
  818. const VECTOR2I& vs = aCoupled.CPoint( vStartIdx[i] );
  819. SHAPE_LINE_CHAIN bypass = dir.BuildInitialTrace( vs, aCoupled.CPoint(j), dir.IsDiagonal() );
  820. int64_t coupledLength = aPair->CoupledLength( aRef, bypass );
  821. SHAPE_LINE_CHAIN newCoupled = aCoupled;
  822. si = vStartIdx[i];
  823. ei = j;
  824. if(si < ei)
  825. newCoupled.Replace( si, ei, bypass );
  826. else
  827. newCoupled.Replace( ei, si, bypass.Reverse() );
  828. if(coupledLength > bestLength && verifyDpBypass( aNode, aPair, aRefIsP, aRef, newCoupled) )
  829. {
  830. bestBypass = newCoupled;
  831. bestLength = coupledLength;
  832. found = true;
  833. }
  834. }
  835. }
  836. }
  837. if( found )
  838. aNewCoupled = bestBypass;
  839. return found;
  840. }
  841. bool checkDpColliding( NODE* aNode, DIFF_PAIR* aPair, bool aIsP, const SHAPE_LINE_CHAIN& aPath )
  842. {
  843. LINE tmp ( aIsP ? aPair->PLine() : aPair->NLine(), aPath );
  844. return static_cast<bool>( aNode->CheckColliding( &tmp ) );
  845. }
  846. bool OPTIMIZER::mergeDpStep( DIFF_PAIR* aPair, bool aTryP, int step )
  847. {
  848. int n = 1;
  849. SHAPE_LINE_CHAIN currentPath = aTryP ? aPair->CP() : aPair->CN();
  850. SHAPE_LINE_CHAIN coupledPath = aTryP ? aPair->CN() : aPair->CP();
  851. int n_segs = currentPath.SegmentCount() - 1;
  852. int64_t clenPre = aPair->CoupledLength( currentPath, coupledPath );
  853. int64_t budget = clenPre / 10; // fixme: come up with somethig more intelligent here...
  854. while( n < n_segs - step )
  855. {
  856. const SEG s1 = currentPath.CSegment( n );
  857. const SEG s2 = currentPath.CSegment( n + step );
  858. DIRECTION_45 dir1( s1 );
  859. DIRECTION_45 dir2( s2 );
  860. if( dir1.IsObtuse( dir2 ) )
  861. {
  862. SHAPE_LINE_CHAIN bypass = DIRECTION_45().BuildInitialTrace( s1.A, s2.B, dir1.IsDiagonal() );
  863. SHAPE_LINE_CHAIN newRef;
  864. SHAPE_LINE_CHAIN newCoup;
  865. int64_t deltaCoupled = -1, deltaUni = -1;
  866. newRef = currentPath;
  867. newRef.Replace( s1.Index(), s2.Index(), bypass );
  868. deltaUni = aPair->CoupledLength ( newRef, coupledPath ) - clenPre + budget;
  869. if ( coupledBypass( m_world, aPair, aTryP, newRef, bypass, coupledPath, newCoup ) )
  870. {
  871. deltaCoupled = aPair->CoupledLength( newRef, newCoup ) - clenPre + budget;
  872. if( deltaCoupled >= 0 )
  873. {
  874. newRef.Simplify();
  875. newCoup.Simplify();
  876. aPair->SetShape( newRef, newCoup, !aTryP );
  877. return true;
  878. }
  879. }
  880. else if( deltaUni >= 0 && verifyDpBypass ( m_world, aPair, aTryP, newRef, coupledPath ) )
  881. {
  882. newRef.Simplify();
  883. coupledPath.Simplify();
  884. aPair->SetShape( newRef, coupledPath, !aTryP );
  885. return true;
  886. }
  887. }
  888. n++;
  889. }
  890. return false;
  891. }
  892. bool OPTIMIZER::mergeDpSegments( DIFF_PAIR* aPair )
  893. {
  894. int step_p = aPair->CP().SegmentCount() - 2;
  895. int step_n = aPair->CN().SegmentCount() - 2;
  896. while( 1 )
  897. {
  898. int n_segs_p = aPair->CP().SegmentCount();
  899. int n_segs_n = aPair->CN().SegmentCount();
  900. int max_step_p = n_segs_p - 2;
  901. int max_step_n = n_segs_n - 2;
  902. if( step_p > max_step_p )
  903. step_p = max_step_p;
  904. if( step_n > max_step_n )
  905. step_n = max_step_n;
  906. if( step_p < 1 && step_n < 1)
  907. break;
  908. bool found_anything_p = false;
  909. bool found_anything_n = false;
  910. if( step_p > 1 )
  911. found_anything_p = mergeDpStep( aPair, true, step_p );
  912. if( step_n > 1 )
  913. found_anything_n = mergeDpStep( aPair, false, step_n );
  914. if( !found_anything_n && !found_anything_p )
  915. {
  916. step_n--;
  917. step_p--;
  918. }
  919. }
  920. return true;
  921. }
  922. bool OPTIMIZER::Optimize( DIFF_PAIR* aPair )
  923. {
  924. return mergeDpSegments( aPair );
  925. }
  926. }