router: improvements in LINE::Walkaround()

- Fix walk failure if the input path starts on the hull boundary
  (happens very often during tight walkaround)
- Input line can have a loop at the end now. Such condition happens when the routing
  destination point lies inside the hull of a colliding primitive.
This commit is contained in:
Tomasz Wlostowski 2021-05-28 00:00:40 +02:00
parent d2ec3fc4ed
commit 8148b5d81f
1 changed files with 116 additions and 34 deletions

View File

@ -153,6 +153,22 @@ int LINE::CountCorners( int aAngles ) const
return count;
}
static int areNeighbours( int x, int y, int max = 0 )
{
if( x > 0 && x - 1 == y )
return true;
if( x < max - 1 && x + 1 == y )
return true;
return false;
}
#ifdef TOM_EXTRA_DEBUG
SHAPE_LINE_CHAIN g_pnew, g_hnew;
#endif
bool LINE::Walkaround( const SHAPE_LINE_CHAIN& aObstacle, SHAPE_LINE_CHAIN& aPath, bool aCw ) const
{
const SHAPE_LINE_CHAIN& line( CLine() );
@ -163,16 +179,14 @@ bool LINE::Walkaround( const SHAPE_LINE_CHAIN& aObstacle, SHAPE_LINE_CHAIN& aPat
}
const auto pFirst = line.CPoint(0);
const auto pLast = line.CPoint(-1);
bool inFirst = aObstacle.PointInside( pFirst ) && !aObstacle.PointOnEdge( pFirst );
bool inLast = aObstacle.PointInside( pLast ) && !aObstacle.PointOnEdge( pLast );
// We can't really walk around if the beginning or the end of the path lies inside the obstacle hull.
// We can't really walk around if the beginning of the path lies inside the obstacle hull.
// Double check if it's not on the hull itself as this triggers many unroutable corner cases.
if( inFirst || inLast )
if( inFirst )
{
return false;
return false;
}
enum VERTEX_TYPE { INSIDE = 0, OUTSIDE, ON_EDGE };
@ -199,7 +213,7 @@ bool LINE::Walkaround( const SHAPE_LINE_CHAIN& aObstacle, SHAPE_LINE_CHAIN& aPat
SHAPE_LINE_CHAIN::INTERSECTIONS ips;
line.Intersect( aObstacle, ips );
HullIntersection( aObstacle, line, ips );
SHAPE_LINE_CHAIN pnew( CLine() ), hnew( aObstacle );
@ -216,33 +230,50 @@ bool LINE::Walkaround( const SHAPE_LINE_CHAIN& aObstacle, SHAPE_LINE_CHAIN& aPat
return nullptr;
};
// make sure all points that lie on the edge of the hull also exist as vertices in the path
for( int i = 0; i < pnew.PointCount(); i++ )
{
const VECTOR2I &p = pnew.CPoint(i);
if( hnew.PointOnEdge( p ) )
{
SHAPE_LINE_CHAIN::INTERSECTION ip;
ip.p = p;
ips.push_back( ip );
}
// corner case for loopy tracks: insert the end loop point back into the hull
if( auto isect = pnew.SelfIntersecting() )
{
pnew.Split( isect->p );
}
// insert all intersections found into the new hull/path SLCs
for( auto& ip : ips )
{
if( pnew.Find( ip.p ) < 0 )
if( pnew.Find( ip.p, 1 ) < 0)
{
pnew.Split(ip.p);
}
if( hnew.Find( ip.p ) < 0 )
if( hnew.Find( ip.p, 1 ) < 0 )
{
hnew.Split(ip.p);
}
}
for( int i = 0; i < pnew.PointCount(); i++ )
{
auto p = pnew.CPoint(i);
bool onEdge = hnew.PointOnEdge( p );
if ( !onEdge )
continue;
int idx = hnew.Find( p );
if(idx < 0 )
{
hnew.Split(p);
}
}
#ifdef TOM_EXTRA_DEBUG
for( auto& ip : ips )
{
printf("Chk: %d %d\n", pnew.Find( ip.p ), hnew.Find(ip.p) );
}
#endif
// we assume the default orientation of the hulls is clockwise, so just reverse the vertex
// order if the caller wants a counter-clockwise walkaround
if ( !aCw )
@ -257,6 +288,10 @@ bool LINE::Walkaround( const SHAPE_LINE_CHAIN& aObstacle, SHAPE_LINE_CHAIN& aPat
bool onEdge = hnew.PointOnEdge( p );
bool inside = hnew.PointInside( p );
#ifdef TOM_EXTRA_DEBUG
printf("pnew %d inside %d onedge %d\n", i, !!inside, !!onEdge );
#endif
VERTEX v;
v.indexp = i;
@ -266,12 +301,23 @@ bool LINE::Walkaround( const SHAPE_LINE_CHAIN& aObstacle, SHAPE_LINE_CHAIN& aPat
vts.push_back( v );
}
#ifdef TOM_EXTRA_DEBUG
g_pnew = pnew;
g_hnew = hnew;
#endif
// each path vertex neighbour list points for sure to the next vertex in the path
for( int i = 0; i < pnew.PointCount() - 1; i++ )
{
vts[i].neighbours.push_back( &vts[ i+1 ] );
}
// each path vertex neighbour list points for sure to the next vertex in the path
for( int i = 1; i < pnew.PointCount() ; i++ )
{
vts[i].neighbours.push_back( &vts[ i-1 ] );
}
// insert hull vertices into the graph
for( int i = 0; i < hnew.PointCount(); i++ )
{
@ -306,8 +352,19 @@ bool LINE::Walkaround( const SHAPE_LINE_CHAIN& aObstacle, SHAPE_LINE_CHAIN& aPat
vc->neighbours.push_back(vnext);
}
int i = 0;
#ifdef TOM_EXTRA_DEBUG
for(auto &v: vts)
{
if( v.indexh < 0 && v.type == ON_EDGE )
{
v.type = OUTSIDE; // hack
}
printf("V %d pos %d %d ip %d ih %d type %d\n", i++, v.pos.x, v.pos.y, v.indexp, v.indexh, v.type );
}
#endif
// vts[0] = start point
VERTEX* v = &vts[0];
VERTEX* v = &vts[0], *v_prev = nullptr;
SHAPE_LINE_CHAIN out;
int iterLimit = 1000;
@ -326,11 +383,12 @@ bool LINE::Walkaround( const SHAPE_LINE_CHAIN& aObstacle, SHAPE_LINE_CHAIN& aPat
if(v->visited)
{
// loop found? clip to beginning of the loop
out = out.Slice(0, out.Find( v->pos ) );
// loop found? stop walking
break;
}
#ifdef TOM_EXTRA_DEBUG
printf("---\nvisit ip %d ih %d type %d outs %d neig %d\n", v->indexp, v->indexh, v->type, out.PointCount(), v->neighbours.size() );
#endif
out.Append( v->pos );
VERTEX* v_next = nullptr;
@ -340,40 +398,61 @@ bool LINE::Walkaround( const SHAPE_LINE_CHAIN& aObstacle, SHAPE_LINE_CHAIN& aPat
// current vertex is outside? first look for any vertex further down the path
// that is not inside the hull
out.Append(v->pos);
VERTEX* v_next_fallback = nullptr;
for( auto vn : v->neighbours )
{
if( (vn->indexp > v->indexp) && vn->type != INSIDE )
if( areNeighbours( vn->indexp , v->indexp, pnew.PointCount() ) && vn->type != INSIDE )
{
v_next = vn;
break;
if( !vn->visited)
{
v_next = vn;
break;
}
else if( vn != v_prev )
v_next_fallback = vn;
}
}
if(!v_next)
v_next = v_next_fallback;
// such a vertex must always be present, if not, bummer.
if (!v_next)
{
#ifdef TOM_EXTRA_DEBUG
printf("FAIL VN fallback %p\n", v_next_fallback );
#endif
return false;
}
}
else if (v->type == ON_EDGE)
{
// current vertex lies on the hull? first look for the hull/path vertex with the index (N+1)
for( VERTEX* vn: v->neighbours)
// look first for the first vertex outside the hull
for( VERTEX* vn : v->neighbours )
{
if( vn->type == ON_EDGE &&
( vn->indexp == ( v->indexp + 1 ) ) &&
( vn->indexh == ( ( v->indexh + 1 ) % hnew.PointCount() ) ) )
#ifdef TOM_EXTRA_DEBUG
printf( "- OUT scan ip %d ih %d type %d\n", vn->indexp, vn->indexh, vn->type );
#endif
if( vn->type == OUTSIDE && !vn->visited )
{
v_next = vn;
break;
}
}
// nothing found? look for the first vertex outside the hull then
// no outside vertices found? continue traversing the hull
if( !v_next )
{
for( VERTEX* vn: v->neighbours)
{
if( vn->type == OUTSIDE )
#ifdef TOM_EXTRA_DEBUG
printf("- scan ip %d ih %d type %d\n", vn->indexp, vn->indexh, vn->type );
#endif
if( vn->type == ON_EDGE &&
areNeighbours( vn->indexp, v->indexp, pnew.PointCount() ) &&
( vn->indexh == ( ( v->indexh + 1 ) % hnew.PointCount() ) ) )
{
v_next = vn;
break;
@ -401,15 +480,18 @@ bool LINE::Walkaround( const SHAPE_LINE_CHAIN& aObstacle, SHAPE_LINE_CHAIN& aPat
}
v->visited = true;
v_prev = v;
v = v_next;
if( !v )
{
return false;
}
}
out.Append( v->pos );
out.Simplify();
aPath = out;
return true;