diff --git a/pcbnew/router/pns_optimizer.cpp b/pcbnew/router/pns_optimizer.cpp index fc41d48473..361650304e 100644 --- a/pcbnew/router/pns_optimizer.cpp +++ b/pcbnew/router/pns_optimizer.cpp @@ -33,9 +33,13 @@ #include "pns_utils.h" #include "pns_router.h" +#include "pns_debug_decorator.h" + namespace PNS { + +static DEBUG_DECORATOR *dbg; /** * Cost Estimator Methods */ @@ -230,194 +234,7 @@ void OPTIMIZER::ClearCache( bool aStaticOnly ) } } - -class LINE_RESTRICTIONS -{ - public: - LINE_RESTRICTIONS() {}; - ~LINE_RESTRICTIONS() {}; - - void Build( NODE* aWorld, LINE* aOriginLine, const SHAPE_LINE_CHAIN& aLine, const BOX2I& aRestrictedArea, bool aRestrictedAreaEnable ); - bool Check ( int aVertex1, int aVertex2, const SHAPE_LINE_CHAIN& aReplacement ); - void Dump(); - - private: - int allowedAngles( NODE* aWorld, const LINE* aLine, const VECTOR2I& aP, bool aFirst ); - - struct RVERTEX - { - RVERTEX ( bool aRestricted, int aAllowedAngles ) : - restricted( aRestricted ), - allowedAngles( aAllowedAngles ) - { - } - - bool restricted; - int allowedAngles; - }; - - std::vector m_rs; -}; - - -// fixme: use later -int LINE_RESTRICTIONS::allowedAngles( NODE* aWorld, const LINE* aLine, const VECTOR2I& aP, bool aFirst ) -{ - JOINT* jt = aWorld->FindJoint( aP , aLine ); - - if( !jt ) - return 0xff; - - DIRECTION_45 dirs [8]; - - int n_dirs = 0; - - for( const ITEM* item : jt->Links().CItems() ) - { - if( item->OfKind( ITEM::VIA_T | ITEM::SOLID_T ) ) - return 0xff; - else if( auto segment = dynamic_cast( item ) ) - { - SEG s( segment->Seg() ); - if( s.A != aP ) - s.Reverse(); - - dirs[n_dirs] = aFirst ? DIRECTION_45( s ) : DIRECTION_45( s ).Opposite(); - } - - if( ++n_dirs >= 8 ) - break; - } - - const int angleMask = DIRECTION_45::ANG_OBTUSE | DIRECTION_45::ANG_HALF_FULL | DIRECTION_45::ANG_STRAIGHT; - int outputMask = 0xff; - - for( int d = 0; d < 8; d++ ) - { - DIRECTION_45 refDir( ( DIRECTION_45::Directions ) d ); - - for( int i = 0; i < n_dirs; i++ ) - { - if( !( refDir.Angle( dirs[i] ) & angleMask ) ) - outputMask &= ~refDir.Mask(); - } - } - - //DrawDebugDirs( aP, outputMask, 3 ); - return 0xff; -} - - -void LINE_RESTRICTIONS::Build( NODE* aWorld, LINE* aOriginLine, const SHAPE_LINE_CHAIN& aLine, const BOX2I& aRestrictedArea, bool aRestrictedAreaEnable ) -{ - const SHAPE_LINE_CHAIN& l = aLine; - VECTOR2I v_prev; - int n = l.PointCount( ); - - m_rs.reserve( n ); - - for( int i = 0; i < n; i++ ) - { - const VECTOR2I &v = l.CPoint( i ); - RVERTEX r( false, 0xff ); - - if( aRestrictedAreaEnable ) - { - bool exiting = ( i > 0 && aRestrictedArea.Contains( v_prev ) && !aRestrictedArea.Contains( v ) ); - bool entering = false; - - if( i != l.PointCount() - 1 ) - { - const VECTOR2I& v_next = l.CPoint( i + 1 ); - entering = ( !aRestrictedArea.Contains( v ) && aRestrictedArea.Contains( v_next ) ); - } - - if( entering ) - { - const SEG& sp = l.CSegment( i ); - r.allowedAngles = DIRECTION_45( sp ).Mask(); - } - else if( exiting ) - { - const SEG& sp = l.CSegment( i - 1 ); - r.allowedAngles = DIRECTION_45( sp ).Mask(); - } - else - { - r.allowedAngles = ( !aRestrictedArea.Contains( v ) ) ? 0 : 0xff; - r.restricted = r.allowedAngles ? false : true; - } - } - - v_prev = v; - m_rs.push_back( r ); - } -} - - -void LINE_RESTRICTIONS::Dump() -{ -} - - -bool LINE_RESTRICTIONS::Check( int aVertex1, int aVertex2, const SHAPE_LINE_CHAIN& aReplacement ) -{ - if( m_rs.empty( ) ) - return true; - - for( int i = aVertex1; i <= aVertex2; i++ ) - if ( m_rs[i].restricted ) - return false; - - const RVERTEX& v1 = m_rs[ aVertex1 ]; - const RVERTEX& v2 = m_rs[ aVertex2 ]; - - int m1 = DIRECTION_45( aReplacement.CSegment( 0 ) ).Mask(); - int m2; - - if( aReplacement.SegmentCount() == 1 ) - m2 = m1; - else - m2 = DIRECTION_45( aReplacement.CSegment( 1 ) ).Mask(); - - return ( ( v1.allowedAngles & m1 ) != 0 ) && - ( ( v2.allowedAngles & m2 ) != 0 ); -} - - -bool OPTIMIZER::checkColliding( ITEM* aItem, bool aUpdateCache ) -{ - CACHE_VISITOR v( aItem, m_world, m_collisionKindMask ); - - return static_cast( m_world->CheckColliding( aItem ) ); - -#if 0 - // something is wrong with the cache, need to investigate. - m_cache.Query( aItem->Shape(), m_world->GetMaxClearance(), v, false ); - - if( !v.m_collidingItem ) - { - NODE::OPT_OBSTACLE obs = m_world->CheckColliding( aItem ); - - if( obs ) - { - if( aUpdateCache ) - cacheAdd( obs->m_item ); - - return true; - } - } - else - { - m_cacheTags[v.m_collidingItem].m_hits++; - return true; - } - - return false; -#endif -} - - +Pejo bool OPTIMIZER::checkColliding( LINE* aLine, const SHAPE_LINE_CHAIN& aOptPath ) { LINE tmp( *aLine, aOptPath ); @@ -458,10 +275,6 @@ bool OPTIMIZER::mergeObtuse( LINE* aLine ) for( int n = 0; n < n_segs - step; n++ ) { - // Don't try to optimize the arc segments - if( current_path.isArc( n ) || current_path.isArc( n + step ) ) - continue; - const SEG s1 = current_path.CSegment( n ); const SEG s2 = current_path.CSegment( n + step ); SEG s1opt, s2opt; @@ -547,6 +360,9 @@ bool OPTIMIZER::mergeFull( LINE* aLine ) if( !found_anything ) step--; + + if( !step ) + break; } aLine->SetShape( current_path ); @@ -588,15 +404,12 @@ bool OPTIMIZER::mergeStep( LINE* aLine, SHAPE_LINE_CHAIN& aCurrentPath, int step int cost_orig = COST_ESTIMATOR::CornerCost( aCurrentPath ); - LINE_RESTRICTIONS restr; - - if( aLine->SegmentCount() < 4 ) + if( aLine->SegmentCount() < 2 ) return false; DIRECTION_45 orig_start( aLine->CSegment( 0 ) ); DIRECTION_45 orig_end( aLine->CSegment( -1 ) ); - restr.Build( m_world, aLine, aCurrentPath, m_restrictArea, m_restrictAreaActive ); for( int n = 0; n < n_segs - step; n++ ) { @@ -617,14 +430,14 @@ bool OPTIMIZER::mergeStep( LINE* aLine, SHAPE_LINE_CHAIN& aCurrentPath, int step SHAPE_LINE_CHAIN bypass = DIRECTION_45().BuildInitialTrace( s1.A, s2.B, i ); cost[i] = INT_MAX; - bool restrictionsOK = restr.Check ( n, n + step + 1, bypass ); - if( n == 0 && orig_start != DIRECTION_45( bypass.CSegment( 0 ) ) ) - postureMatch = false; - else if( n == n_segs - step && orig_end != DIRECTION_45( bypass.CSegment( -1 ) ) ) - postureMatch = false; + bool ok = false; + if ( !checkColliding( aLine, bypass ) ) + { + ok = checkConstraints ( n, n + step + 1, aLine, bypass ); + } - if( restrictionsOK && (postureMatch || !m_keepPostures) && !checkColliding( aLine, bypass ) ) + if( ok ) { path[i] = aCurrentPath; path[i].Replace( s1.Index(), s2.Index(), bypass ); @@ -973,12 +786,25 @@ bool OPTIMIZER::runSmartPads( LINE* aLine ) } -bool OPTIMIZER::Optimize( LINE* aLine, int aEffortLevel, NODE* aWorld ) +bool OPTIMIZER::Optimize( LINE* aLine, int aEffortLevel, NODE* aWorld, const VECTOR2I aV ) { OPTIMIZER opt( aWorld ); opt.SetEffortLevel( aEffortLevel ); opt.SetCollisionMask( -1 ); + + if ( aEffortLevel & KEEP_TOPOLOGY ) + { + auto c = new KEEP_TOPOLOGY_CONSTRAINT( aWorld ); + opt.AddConstraint( c ); + } + + if ( aEffortLevel & PRESERVE_VERTEX ) + { + auto c = new PRESERVE_VERTEX_CONSTRAINT( aWorld, aV ); + opt.AddConstraint( c ); + //printf("pres-v %d %d\n", aV.x, aV.y ); + } return opt.Optimize( aLine ); } @@ -1242,4 +1068,189 @@ bool OPTIMIZER::Optimize( DIFF_PAIR* aPair ) return mergeDpSegments( aPair ); } +static int64_t shovedArea( const SHAPE_LINE_CHAIN& aOld, const SHAPE_LINE_CHAIN& aNew ) +{ + int64_t area = 0; + const int oc = aOld.PointCount(); + const int nc = aNew.PointCount(); + const int total = oc + nc; + + for(int i = 0; i < total; i++) + { + int i_next = (i + 1 == total ? 0 : i + 1); + + const VECTOR2I &v0 = ( i < oc ? aOld.CPoint(i) : aNew.CPoint( nc - 1 - (i - oc) ) ); + const VECTOR2I &v1 = ( i_next < oc ? aOld.CPoint ( i_next ) : aNew.CPoint( nc - 1 - (i_next - oc) ) ); + area += -(int64_t) v0.y * v1.x + (int64_t) v0.x * v1.y; + } + + return std::abs(area / 2); +} + +bool tightenSegment( bool dir, NODE *aNode, LINE cur, SHAPE_LINE_CHAIN in, SHAPE_LINE_CHAIN& out ) +{ + SEG a = in.CSegment(0); + SEG center = in.CSegment(1); + SEG b = in.CSegment(2); + + DIRECTION_45 dirA ( a ); + DIRECTION_45 dirCenter ( center ); + DIRECTION_45 dirB ( b ); + + if (!dirA.IsObtuse( dirCenter) || !dirCenter.IsObtuse(dirB)) + return false; + + //printf("Tighten!"); + + VECTOR2I perp = (center.B - center.A).Perpendicular(); + + auto rA = SEG( center.A, center.A + perp).LineProject( a.A ); + auto rB = SEG( center.B, center.B + perp).LineProject( b.B ); + + VECTOR2I guideA, guideB ; + + //SEG ga (center.A, rA); + //SEG gb (center.B, rB); + + SEG guide; + int initial; + + auto dbg = ROUTER::GetInstance()->GetInterface()->GetDebugDecorator(); + if ( dirA.Angle ( dirB ) != DIRECTION_45::ANG_RIGHT ) + return false; + + { + auto rC = *a.IntersectLines( b ); + dbg->AddSegment ( SEG( center.A, rC ), 1 ); + dbg->AddSegment ( SEG( center.B, rC ), 2 ); + /* + auto perp = dirCenter.Left().Left(); + + SEG sperp ( center.A, center.A + perp.ToVector() ); + + auto vpc = sperp.LineProject( rC ); + auto vpa = sperp.LineProject( a.A ); + auto vpb = sperp.LineProject( b.B ); + + auto da = (vpc - vpa).EuclideanNorm(); + auto db = (vpc - vpb).EuclideanNorm(); + + auto vp = (da < db) ? vpa : vpb; + dbg->AddSegment ( SEG( vpc, vp ), 5 ); + + + guide = SEG ( vpc, vp );*/ + + + } + + int da = a.Length(); + int db = b.Length(); + + if ( da < db ) + guide = a; + else + guide = b; + + + initial = guide.Length(); + + int step = initial; + int current = step; + //printf("step %d\n", step); + SHAPE_LINE_CHAIN snew; + + while (step > 1) + { + LINE l ( cur ); + + + //printf("current %d l %d\n", current, guide.Length() ); + snew.Clear(); + snew.Append( a.A ); + snew.Append( a.B + (a.A - a.B).Resize( current ) ); + snew.Append( b.A + (b.B - b.A).Resize( current ) ); + snew.Append( b.B ); + + step /= 2; + + l.SetShape(snew); + if( aNode->CheckColliding(&l) ) + { + current -= step; + } else if ( current + step >= initial ) { + current = initial; + } else { + current += step; + } + + + //dbg->AddSegment ( SEG( center.A , a.LineProject( center.A + gr ) ), 3 ); + //dbg->AddSegment ( SEG( center.A , center.A + guideA ), 3 ); + //dbg->AddSegment ( SEG( center.B , center.B + guideB ), 4 ); + + + if ( current == initial ) + break; + + + } + out = snew; + + //dbg->AddLine ( snew, 3, 100000 ); + + + return true; + + +} + +void Tighten( NODE *aNode, SHAPE_LINE_CHAIN& aOldLine, LINE& aNewLine, LINE& aOptimized ) +{ + LINE tmp; + + printf("shovedArea : %lld %d\n", shovedArea(aOldLine, aNewLine.CLine() ), aNewLine.SegmentCount() ); + + + if ( aNewLine.SegmentCount() < 3 ) + return; + + SHAPE_LINE_CHAIN current ( aNewLine.CLine() ); + + for (int step = 0; step < 3; step++) + { + current.Simplify(); + + for ( int i = 0; i <= current.SegmentCount() - 3; i++) + { + SHAPE_LINE_CHAIN l_in, l_out; + + l_in = current.Slice(i, i+3); + for (int dir = 0; dir < 1; dir++) + { + if( tightenSegment( dir ? true : false, aNode, aNewLine, l_in, l_out ) ) + { + SHAPE_LINE_CHAIN opt = current; + opt.Replace(i, i+3, l_out); + auto optArea = std::abs(shovedArea( aOldLine, opt )); + auto prevArea = std::abss(shovedArea( aOldLine, current )); + + if( optArea < prevArea ) + { + current = opt; + } + break; + } + + } + } + + } + + aOptimized = LINE(aNewLine, current); + + auto dbg = ROUTER::GetInstance()->GetInterface()->GetDebugDecorator(); + //dbg->AddLine ( current, 4, 100000 ); +} + } diff --git a/pcbnew/router/pns_optimizer.h b/pcbnew/router/pns_optimizer.h index 9292dbc7d2..eb8750205f 100644 --- a/pcbnew/router/pns_optimizer.h +++ b/pcbnew/router/pns_optimizer.h @@ -36,6 +36,8 @@ class NODE; class ROUTER; class LINE; class DIFF_PAIR; +class ITEM; +class JOINT; /** * COST_ESTIMATOR @@ -76,6 +78,8 @@ private: int m_cornerCost; }; +class OPT_CONSTRAINT; + /** * OPTIMIZER * @@ -95,15 +99,17 @@ public: MERGE_SEGMENTS = 0x01, SMART_PADS = 0x02, MERGE_OBTUSE = 0x04, - FANOUT_CLEANUP = 0x08 + FANOUT_CLEANUP = 0x08, + KEEP_TOPOLOGY = 0x10, + PRESERVE_VERTEX = 0x20 }; OPTIMIZER( NODE* aWorld ); ~OPTIMIZER(); ///> a quick shortcut to optmize a line without creating and setting up an optimizer - static bool Optimize( LINE* aLine, int aEffortLevel, NODE* aWorld); - + static bool Optimize( LINE* aLine, int aEffortLevel, NODE* aWorld, const VECTOR2I aV = VECTOR2I(0, 0) ); + bool Optimize( LINE* aLine, LINE* aResult = NULL ); bool Optimize( DIFF_PAIR* aPair ); @@ -130,6 +136,12 @@ public: m_restrictAreaActive = true; } + void ClearConstraints(); + void AddConstraint ( OPT_CONSTRAINT *aConstraint ); + + bool extractPadGrids( std::vector& aPadJoints ); + void BuildPadGrids(); + private: static const int MaxCachedItems = 256; @@ -158,6 +170,10 @@ private: void cacheAdd( ITEM* aItem, bool aIsStatic ); void removeCachedSegments( LINE* aLine, int aStartVertex = 0, int aEndVertex = -1 ); + bool checkConstraints( int aVertex1, int aVertex2, LINE* aOriginLine, const SHAPE_LINE_CHAIN& aReplacement ); + + + BREAKOUT_LIST circleBreakouts( int aWidth, const SHAPE* aShape, bool aPermitDiagonal ) const; BREAKOUT_LIST rectBreakouts( int aWidth, const SHAPE* aShape, bool aPermitDiagonal ) const; BREAKOUT_LIST ovalBreakouts( int aWidth, const SHAPE* aShape, bool aPermitDiagonal ) const; @@ -170,6 +186,7 @@ private: SHAPE_INDEX_LIST m_cache; + std::vector m_constraints; typedef std::unordered_map CachedItemTags; CachedItemTags m_cacheTags; NODE* m_world; @@ -181,6 +198,93 @@ private: bool m_restrictAreaActive; }; -} +class OPT_CONSTRAINT +{ +public: + OPT_CONSTRAINT( NODE* aWorld ) : + m_world( aWorld ) + { + m_priority = 0; + }; + + virtual ~OPT_CONSTRAINT() {}; + + virtual bool Check ( int aVertex1, int aVertex2, LINE* aOriginLine, const SHAPE_LINE_CHAIN& aReplacement ) = 0; + + int GetPriority() const + { + return m_priority; + } + + void SetPriority( int aPriority ) + { + m_priority = aPriority; + } + +protected: + NODE* m_world; + int m_priority; +}; + +class ANGLE_CONSTRAINT_45: public OPT_CONSTRAINT +{ +public: + ANGLE_CONSTRAINT_45( NODE* aWorld, int aEntryDirectionMask = -1, int aExitDirectionMask = -1 ) : + OPT_CONSTRAINT( aWorld ), + m_entryDirectionMask( aEntryDirectionMask ), + m_exitDirectionMask( aExitDirectionMask ) + { + + } + + virtual ~ANGLE_CONSTRAINT_45() {}; + + virtual bool Check ( int aVertex1, int aVertex2, LINE* aOriginLine, const SHAPE_LINE_CHAIN& aReplacement ) override; + +private: + int m_entryDirectionMask; + int m_exitDirectionMask; +}; + +class AREA_CONSTRAINT : public OPT_CONSTRAINT +{ +public: + AREA_CONSTRAINT( NODE* aWorld, const BOX2I& aAllowedArea ) : + OPT_CONSTRAINT( aWorld ), + m_allowedArea ( aAllowedArea ) {}; + + virtual bool Check ( int aVertex1, int aVertex2, LINE* aOriginLine, const SHAPE_LINE_CHAIN& aReplacement ) override; + +private: + BOX2I m_allowedArea; + +}; + +class KEEP_TOPOLOGY_CONSTRAINT: public OPT_CONSTRAINT +{ +public: + KEEP_TOPOLOGY_CONSTRAINT( NODE* aWorld ) : + OPT_CONSTRAINT( aWorld ) + {}; + + virtual bool Check ( int aVertex1, int aVertex2, LINE* aOriginLine, const SHAPE_LINE_CHAIN& aReplacement ) override; +}; + +class PRESERVE_VERTEX_CONSTRAINT: public OPT_CONSTRAINT +{ +public: + PRESERVE_VERTEX_CONSTRAINT( NODE* aWorld, const VECTOR2I& aV ) : + OPT_CONSTRAINT( aWorld ), + m_v( aV ) + {}; + + virtual bool Check ( int aVertex1, int aVertex2, LINE* aOriginLine, const SHAPE_LINE_CHAIN& aReplacement ) override; +private: + + VECTOR2I m_v; +}; + + +}; #endif