router: improve the feel of the walkaround algorithm

Makes walkaround more 'huggy' - the trace being routed sticks to obstacles
instead of jumping around in uncontrollable way.
This commit is contained in:
Tomasz Wlostowski 2021-05-28 00:08:31 +02:00
parent 8148b5d81f
commit e66bbfd1ec
5 changed files with 207 additions and 179 deletions

View File

@ -426,14 +426,106 @@ VECTOR2I closestProjectedPoint( const SHAPE_LINE_CHAIN& line, const VECTOR2I& p
}
static bool cursorDistMinimum( const SHAPE_LINE_CHAIN& aL, const VECTOR2I& aCursor, double lengthThreshold, int& theDist, VECTOR2I& aNearest )
{
std::vector<int> dists;
std::vector<VECTOR2I> pts;
if( aL.PointCount() == 0 )
return false;
VECTOR2I lastP = aL.CPoint(-1);
int accumulatedDist = 0;
dists.reserve( 2 * aL.PointCount() );
for( int i = 0; i < aL.SegmentCount(); i++ )
{
const SEG& s = aL.CSegment( i );
dists.push_back( ( aCursor - s.A ).EuclideanNorm() );
pts.push_back( s.A );
auto pn = s.NearestPoint( aCursor );
if( pn != s.A && pn != s.B )
{
dists.push_back( ( pn - aCursor ).EuclideanNorm() );
pts.push_back( pn );
}
accumulatedDist += s.Length();
if ( accumulatedDist > lengthThreshold )
{
lastP = s.B;
break;
}
}
dists.push_back( ( aCursor - lastP ).EuclideanNorm() );
pts.push_back( lastP );
int minDistLoc = std::numeric_limits<int>::max();
int minPLoc = -1;
int minDistGlob = std::numeric_limits<int>::max();
int minPGlob = -1;
for( int i = 0; i < dists.size() - 3; i++ )
{
if( dists[i + 2] > dists[i + 1] && dists[i] > dists[i + 1] )
{
int d = dists[i + 1];
if( d < minDistLoc )
{
minDistLoc = d;
minPLoc = i + 1;
}
}
}
if ( dists.back() < minDistLoc && minPLoc >= 0 )
{
minDistLoc = dists.back();
minPLoc = dists.size() - 1;
}
for( int i = 0; i < dists.size(); i++ )
{
int d = dists[i];
if( d < minDistGlob )
{
minDistGlob = d;
minPGlob = i;
}
}
// fixme: I didn't make my mind yet if local or global minimum feels better. I'm leaving both
// in the code, enabling the global one by default
minPLoc = -1;
if( minPLoc < 0 )
{
theDist = minDistGlob;
aNearest = pts[minPGlob];
return true;
}
else
{
theDist = minDistLoc;
aNearest = pts[minPLoc];
return true;
}
}
bool LINE_PLACER::rhWalkOnly( const VECTOR2I& aP, LINE& aNewHead )
{
LINE initTrack( m_head );
LINE walkFull( m_head );
int effort = 0;
bool rv = true, viaOk;
bool viaOk = false;
viaOk = buildInitialLine( aP, initTrack );
VECTOR2I walkP = aP;
WALKAROUND walkaround( m_currentNode, Router() );
@ -442,37 +534,101 @@ bool LINE_PLACER::rhWalkOnly( const VECTOR2I& aP, LINE& aNewHead )
walkaround.SetLogger( Logger() );
walkaround.SetIterationLimit( Settings().WalkaroundIterationLimit() );
WALKAROUND::RESULT wr = walkaround.Route( initTrack );
//WALKAROUND::WALKAROUND_STATUS wf = walkaround.Route( initTrack, walkFull, false );
int round = 0;
SHAPE_LINE_CHAIN l_cw = wr.lineCw.CLine();
SHAPE_LINE_CHAIN l_ccw = wr.lineCcw.CLine();
do {
viaOk = buildInitialLine( walkP, initTrack, round == 0 );
printf("round %d vok %d\n", round, viaOk?1:0);
double initialLength = initTrack.CLine().Length();
double hugThresholdLength = initialLength * Settings().WalkaroundHugLengthThreshold();
if( wr.statusCcw == WALKAROUND::ALMOST_DONE || wr.statusCw == WALKAROUND::ALMOST_DONE )
{
WALKAROUND::RESULT wr = walkaround.Route( initTrack );
VECTOR2I p_cw = closestProjectedPoint( l_cw, aP );
VECTOR2I p_ccw = closestProjectedPoint( l_ccw, aP );
SHAPE_LINE_CHAIN l_cw = wr.lineCw.CLine();
SHAPE_LINE_CHAIN l_ccw = wr.lineCcw.CLine();
int idx_cw = l_cw.Split( p_cw );
int idx_ccw = l_ccw.Split( p_ccw );
if( wr.statusCcw == WALKAROUND::DONE || wr.statusCw == WALKAROUND::DONE )
{
int len_cw = wr.statusCw == WALKAROUND::DONE ? l_cw.Length() : INT_MAX;
int len_ccw = wr.statusCcw == WALKAROUND::DONE ? l_ccw.Length() : INT_MAX;
l_cw = l_cw.Slice( 0, idx_cw );
l_ccw = l_ccw.Slice( 0, idx_ccw );
Dbg()->AddLine( wr.lineCw.CLine(), 6, 10000, "wf-result-cw" );
Dbg()->AddLine( wr.lineCcw.CLine(), 5, 20000, "wf-result-ccw" );
//Dbg()->AddLine( wr.lineCw.CLine(), 3, 40000 );
int bestLength = len_cw < len_ccw ? len_cw : len_ccw;
//Dbg()->AddPoint( p_cw, 4 );
//Dbg()->AddPoint( p_ccw, 5 );
if( bestLength > hugThresholdLength )
{
wr.statusCw = WALKAROUND::ALMOST_DONE;
wr.statusCcw = WALKAROUND::ALMOST_DONE;
}
Dbg()->AddLine( wr.lineCw.CLine(), 4, 1000 );
Dbg()->AddLine( wr.lineCcw.CLine(), 5, 1000 );
SHAPE_LINE_CHAIN& bestLine = len_cw < len_ccw ? l_cw : l_ccw;
walkFull.SetShape( bestLine );
}
}
if( wr.statusCcw == WALKAROUND::ALMOST_DONE || wr.statusCw == WALKAROUND::ALMOST_DONE )
{
bool valid_cw = false, valid_ccw = false;
VECTOR2I p_cw, p_ccw;
int dist_ccw, dist_cw;
if( wr.statusCcw == WALKAROUND::ALMOST_DONE )
{
valid_ccw = cursorDistMinimum( l_ccw, aP, hugThresholdLength, dist_ccw, p_ccw );
if( valid_ccw )
{
int idx_ccw = l_ccw.Split( p_ccw );
l_ccw = l_ccw.Slice( 0, idx_ccw );
Dbg()->AddPoint( p_ccw, 5, 500000, "hug-target-ccw" );
// Dbg()->AddLine( l_ccw, 5, 200000, "wh-result-ccw" );
walkFull.SetShape( l_ccw.Length() < l_cw.Length() ? l_ccw : l_cw );
Dbg()->AddLine( walkFull.CLine(), 2, 100000, "walk-full" );
}
}
if( wr.statusCw == WALKAROUND::ALMOST_DONE )
{
valid_cw = cursorDistMinimum( l_cw, aP, hugThresholdLength, dist_cw, p_cw );
if( valid_cw )
{
int idx_cw = l_cw.Split( p_cw );
l_cw = l_cw.Slice( 0, idx_cw );
Dbg()->AddPoint( p_cw, 4, 500000, "hug-target-cw" );
// Dbg()->AddLine( l_cw, 6, 200000, "wh-result-cw" );
}
}
// return false;
if( dist_cw < dist_ccw && valid_cw )
{
walkFull.SetShape( l_cw );
walkP = p_cw;
}
else if ( valid_ccw )
{
walkFull.SetShape( l_ccw );
walkP = p_ccw;
}
else
{
return false;
}
}
else if ( wr.statusCcw == WALKAROUND::STUCK || wr.statusCw == WALKAROUND::STUCK )
{
printf("FINISH3\n");
return false;
}
round++;
} while( round < 2 && m_placingVia );
Dbg()->AddLine( walkFull.CLine(), 2, 200000, "walk-full" );
switch( Settings().OptimizerEffort() )
{
@ -489,28 +645,23 @@ bool LINE_PLACER::rhWalkOnly( const VECTOR2I& aP, LINE& aNewHead )
if( Settings().SmartPads() && !m_mouseTrailTracer.IsManuallyForced() )
effort |= OPTIMIZER::SMART_PADS;
if( wr.statusCw == WALKAROUND::STUCK || wr.statusCcw == WALKAROUND::STUCK )
{
walkFull = walkFull.ClipToNearestObstacle( m_currentNode );
rv = true;
}
else if( m_placingVia && viaOk )
if( m_placingVia && viaOk )
{
walkFull.AppendVia( makeVia( walkFull.CPoint( -1 ) ) );
}
OPTIMIZER::Optimize( &walkFull, effort, m_currentNode );
if( m_currentNode->CheckColliding( &walkFull ) )
{
aNewHead = m_head;
return false;
}
m_head = walkFull;
aNewHead = walkFull;
Dbg()->AddLine( walkFull.CLine(), 2, 200000, "walk-full" );
return rv;
return true;
}
@ -751,11 +902,14 @@ void LINE_PLACER::routeStep( const VECTOR2I& aP )
if( !new_head.Is45Degree() )
fail = true;
if( !Settings().FollowMouse() )
if( fail )
return;
m_head = new_head;
if( !Settings().FollowMouse() )
return;
if( handleSelfIntersections() )
{
n_iter++;
@ -1365,7 +1519,7 @@ void LINE_PLACER::SetOrthoMode( bool aOrthoMode )
}
bool LINE_PLACER::buildInitialLine( const VECTOR2I& aP, LINE& aHead )
bool LINE_PLACER::buildInitialLine( const VECTOR2I& aP, LINE& aHead, bool aForceNoVia )
{
SHAPE_LINE_CHAIN l;
DIRECTION_45 guessedDir = m_mouseTrailTracer.GetPosture( aP );
@ -1406,7 +1560,7 @@ bool LINE_PLACER::buildInitialLine( const VECTOR2I& aP, LINE& aHead )
aHead.SetLayer( m_currentLayer );
aHead.SetShape( l );
if( !m_placingVia )
if( !m_placingVia || aForceNoVia )
return true;
VIA v( makeVia( aP ) );

View File

@ -319,7 +319,7 @@ private:
const VIA makeVia( const VECTOR2I& aP );
bool buildInitialLine( const VECTOR2I& aP, LINE& aHead );
bool buildInitialLine( const VECTOR2I& aP, LINE& aHead, bool aForceNoVia = false );
DIRECTION_45 m_direction; ///< current routing direction

View File

@ -52,6 +52,7 @@ ROUTING_SETTINGS::ROUTING_SETTINGS( JSON_SETTINGS* aParent, const std::string& a
m_snapToPads = false;
m_optimizeEntireDraggedTrack = false;
m_cornerMode = CORNER_MODE::MITERED_45;
m_walkaroundHugLengthThreshold = 1.5;
m_autoPosture = true;
m_fixAllSegments = true;
@ -100,6 +101,8 @@ ROUTING_SETTINGS::ROUTING_SETTINGS( JSON_SETTINGS* aParent, const std::string& a
CORNER_MODE::MITERED_45, CORNER_MODE::ROUNDED_90,
CORNER_MODE::MITERED_45 ) );
m_params.emplace_back( new PARAM<double>( "walkaround_hug_length_threshold", &m_walkaroundHugLengthThreshold, 1.5 ) );
LoadFromFile();
}

View File

@ -164,6 +164,8 @@ public:
bool GetFixAllSegments() const { return m_fixAllSegments; }
void SetFixAllSegments( bool aEnable ) { m_fixAllSegments = aEnable; }
double WalkaroundHugLengthThreshold() const { return m_walkaroundHugLengthThreshold; }
private:
bool m_shoveVias;
bool m_startDiagonal;
@ -189,6 +191,8 @@ private:
int m_walkaroundIterationLimit;
int m_shoveIterationLimit;
double m_walkaroundHugLengthThreshold;
TIME_LIMIT m_shoveTimeLimit;
TIME_LIMIT m_walkaroundTimeLimit;
};

View File

@ -52,8 +52,7 @@ NODE::OPT_OBSTACLE WALKAROUND::nearestObstacle( const LINE& aPath )
}
WALKAROUND::WALKAROUND_STATUS WALKAROUND::singleStep( LINE& aPath,
bool aWindingDirection )
WALKAROUND::WALKAROUND_STATUS WALKAROUND::singleStep( LINE& aPath, bool aWindingDirection )
{
OPT<OBSTACLE>& current_obs =
aWindingDirection ? m_currentObstacle[0] : m_currentObstacle[1];
@ -61,47 +60,10 @@ WALKAROUND::WALKAROUND_STATUS WALKAROUND::singleStep( LINE& aPath,
if( !current_obs )
return DONE;
SHAPE_LINE_CHAIN path_walk[2];
SHAPE_LINE_CHAIN path_walk;
if( aPath.PointCount() > 1 )
{
VECTOR2I last = aPath.CPoint( -1 );
if( ( current_obs->m_hull ).PointInside( last ) || ( current_obs->m_hull ).PointOnEdge( last ) )
{
m_recursiveBlockageCount++;
if( m_recursiveBlockageCount < 3 )
aPath.Line().Append( current_obs->m_hull.NearestPoint( last ) );
else
{
aPath = aPath.ClipToNearestObstacle( m_world );
return DONE;
}
}
}
aPath.Walkaround( current_obs->m_hull, path_walk[0],
bool s_cw = aPath.Walkaround( current_obs->m_hull, path_walk,
aWindingDirection );
aPath.Walkaround( current_obs->m_hull, path_walk[1],
!aWindingDirection );
if( !aPath.Walkaround( current_obs->m_hull, path_walk[1], !aWindingDirection ) )
return STUCK;
auto l =aPath.CLine();
#if 0
if( m_logger )
{
m_logger->NewGroup( aWindingDirection ? "walk-cw" : "walk-ccw", m_iteration );
m_logger->Log( &path_walk[0], 0, "path_walk" );
m_logger->Log( &path_pre[0], 1, "path_pre" );
m_logger->Log( &path_post[0], 4, "path_post" );
m_logger->Log( &current_obs->m_hull, 2, "hull" );
m_logger->Log( current_obs->m_item, 3, "item" );
}
#endif
if ( Dbg() )
{
@ -111,70 +73,21 @@ WALKAROUND::WALKAROUND_STATUS WALKAROUND::singleStep( LINE& aPath,
Dbg()->AddLine( current_obs->m_hull, 1, 1, name);
snprintf(name, sizeof(name), "path-%s-%d", aWindingDirection ? "cw" : "ccw", m_iteration );
Dbg()->AddLine( aPath.CLine(), 2, 1, name );
snprintf(name, sizeof(name), "result-%s-%d", aWindingDirection ? "cw" : "ccw", m_iteration );
Dbg()->AddLine( path_walk, 3, 10000, name );
Dbg()->Message( wxString::Format( "Stat cw %d", !!s_cw ) );
Dbg()->EndGroup();
}
int len_pre = path_walk[0].Length();
int len_alt = path_walk[1].Length();
current_obs = nearestObstacle( LINE( aPath, path_walk ) );
LINE walk_path( aPath, path_walk[1] );
bool alt_collides = static_cast<bool>( m_world->CheckColliding( &walk_path, m_itemMask ) );
SHAPE_LINE_CHAIN pnew;
/*if( !m_forceLongerPath && len_alt < len_pre && !alt_collides && !prev_recursive )
{
pnew = path_pre[1];
pnew.Append( path_walk[1] );
pnew.Append( path_post[1] );
if( !path_post[1].PointCount() || !path_walk[1].PointCount() )
current_obs = nearestObstacle( LINE( aPath, path_pre[1] ) );
else
current_obs = nearestObstacle( LINE( aPath, path_post[1] ) );
}
else*/
{
pnew = path_walk[0];
current_obs = nearestObstacle( LINE( aPath, path_walk[0] ) );
}
pnew.Simplify();
aPath.SetShape( pnew );
path_walk.Simplify();
aPath.SetShape( path_walk );
return IN_PROGRESS;
}
bool clipToLoopStart( SHAPE_LINE_CHAIN& l )
{
auto ip = l.SelfIntersecting();
if(!ip)
return false;
else {
int pidx = l.Split( ip->p );
auto lead = l.Slice(0, pidx);
auto tail = l.Slice(pidx + 1, -1);
int pidx2 = tail.Split( ip->p );
auto dbg = ROUTER::GetInstance()->GetInterface()->GetDebugDecorator();
dbg->AddPoint( ip->p, 5 );
l = lead;
l.Append( tail.Slice( 0, pidx2 ) );
//l = l.Slice(0, pidx);
return true;
}
}
const WALKAROUND::RESULT WALKAROUND::Route( const LINE& aInitialPath )
{
LINE path_cw( aInitialPath ), path_ccw( aInitialPath );
@ -210,21 +123,12 @@ const WALKAROUND::RESULT WALKAROUND::Route( const LINE& aInitialPath )
while( m_iteration < m_iterationLimit )
{
if( s_cw != STUCK )
if( s_cw != STUCK && s_cw != ALMOST_DONE )
s_cw = singleStep( path_cw, true );
if( s_ccw != STUCK )
if( s_ccw != STUCK && s_ccw != ALMOST_DONE )
s_ccw = singleStep( path_ccw, false );
auto old = path_cw.CLine();
if( clipToLoopStart( path_cw.Line() ) )
s_cw = ALMOST_DONE;
if( clipToLoopStart( path_ccw.Line() ) )
s_ccw = ALMOST_DONE;
if( s_cw != IN_PROGRESS )
{
result.lineCw = path_cw;
@ -255,9 +159,6 @@ const WALKAROUND::RESULT WALKAROUND::Route( const LINE& aInitialPath )
result.statusCcw = ALMOST_DONE;
}
result.lineCw.Line().Simplify();
result.lineCcw.Line().Simplify();
if( result.lineCw.SegmentCount() < 1 || result.lineCw.CPoint( 0 ) != aInitialPath.CPoint( 0 ) )
{
result.statusCw = STUCK;
@ -361,40 +262,6 @@ WALKAROUND::WALKAROUND_STATUS WALKAROUND::Route( const LINE& aInitialPath,
aWalkPath = ( len_cw < len_ccw ? path_cw : path_ccw );
}
if( m_cursorApproachMode )
{
// int len_cw = path_cw.GetCLine().Length();
// int len_ccw = path_ccw.GetCLine().Length();
bool found = false;
SHAPE_LINE_CHAIN l = aWalkPath.CLine();
for( int i = 0; i < l.SegmentCount(); i++ )
{
const SEG s = l.Segment( i );
VECTOR2I nearest = s.NearestPoint( m_cursorPos );
VECTOR2I::extended_type dist_a = ( s.A - m_cursorPos ).SquaredEuclideanNorm();
VECTOR2I::extended_type dist_b = ( s.B - m_cursorPos ).SquaredEuclideanNorm();
VECTOR2I::extended_type dist_n = ( nearest - m_cursorPos ).SquaredEuclideanNorm();
if( dist_n <= dist_a && dist_n < dist_b )
{
l.Remove( i + 1, -1 );
l.Append( nearest );
l.Simplify();
found = true;
break;
}
}
if( found )
{
aWalkPath = aInitialPath;
aWalkPath.SetShape( l );
}
}
aWalkPath.Line().Simplify();
if( aWalkPath.SegmentCount() < 1 )