router: initial support for user-provided constraints for the optimizer

This commit is contained in:
Tomasz Wlostowski 2020-02-19 18:40:51 +01:00
parent a42b86d3ac
commit 6b767d50c3
2 changed files with 322 additions and 207 deletions

View File

@ -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<RVERTEX> 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<const SEGMENT*>( 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<bool>( 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 );
}
}

View File

@ -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<JOINT*>& 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<ITEM*> m_cache;
std::vector<OPT_CONSTRAINT*> m_constraints;
typedef std::unordered_map<ITEM*, CACHED_ITEM> 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