router: improvements to shove algorithm

This commit is contained in:
Tomasz Wlostowski 2015-08-19 18:07:16 +02:00 committed by Maciej Suminski
parent 95c59c8060
commit a96299d456
16 changed files with 236 additions and 73 deletions

View File

@ -282,6 +282,12 @@ static inline bool Collide( const SHAPE_CONVEX& aA, const SHAPE_SEGMENT& aB, int
return Collide( aA.Vertices(), aB, aClearance, aNeedMTV, aMTV );
}
static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_RECT& aB, int aClearance,
bool aNeedMTV, VECTOR2I& aMTV )
{
return Collide( aA.Outline(), aB.Outline(), aClearance, aNeedMTV, aMTV );
}
template<class ShapeAType, class ShapeBType>
inline bool CollCase( const SHAPE* aA, const SHAPE* aB, int aClearance, bool aNeedMTV, VECTOR2I& aMTV )
@ -310,11 +316,8 @@ bool CollideShapes( const SHAPE* aA, const SHAPE* aB, int aClearance, bool aNeed
case SH_RECT:
switch( aB->Type() )
{
case SH_RECT: // RECT to RECT not yet in use in p&s router
// Should be (todo) somethink like:
//return CollCaseReversed<SHAPE_RECT, SHAPE_RECT>( aA, aB, aClearance, aNeedMTV, aMTV );
assert( aB->Type() != SH_RECT );
return true;
case SH_RECT:
return CollCase<SHAPE_RECT, SHAPE_RECT>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_CIRCLE:
return CollCase<SHAPE_RECT, SHAPE_CIRCLE>( aA, aB, aClearance, aNeedMTV, aMTV );

View File

@ -206,8 +206,6 @@ const SHAPE_LINE_CHAIN SHAPE_POLY_SET::convertFromClipper( const Path& aPath )
return lc;
}
#include <common.h>
#include <wx/wx.h>
void SHAPE_POLY_SET::booleanOp( ClipType aType, const SHAPE_POLY_SET& aOtherShape,
bool aFastMode )
{

View File

@ -142,6 +142,18 @@ public:
return true;
}
const SHAPE_LINE_CHAIN Outline() const
{
SHAPE_LINE_CHAIN rv;
rv.Append( m_p0 );
rv.Append( m_p0.x, m_p0.y + m_w );
rv.Append( m_p0.x + m_h, m_p0.y + m_w );
rv.Append( m_p0.x + m_h, m_p0.y );
rv.Append( m_p0 );
rv.SetClosed( true );
return rv;
}
private:
///> Top-left corner
VECTOR2I m_p0;

View File

@ -22,10 +22,10 @@
#include "pns_line.h"
bool PNS_ITEM::collideSimple( const PNS_ITEM* aOther, int aClearance, bool aNeedMTV,
VECTOR2I& aMTV ) const
VECTOR2I& aMTV, bool aDifferentNetsOnly ) const
{
// same nets? no collision!
if( m_net == aOther->m_net )
if( aDifferentNetsOnly && m_net == aOther->m_net )
return false;
// check if we are not on completely different layers first
@ -39,9 +39,9 @@ bool PNS_ITEM::collideSimple( const PNS_ITEM* aOther, int aClearance, bool aNeed
bool PNS_ITEM::Collide( const PNS_ITEM* aOther, int aClearance, bool aNeedMTV,
VECTOR2I& aMTV ) const
VECTOR2I& aMTV, bool aDifferentNetsOnly ) const
{
if( collideSimple( aOther, aClearance, aNeedMTV, aMTV ) )
if( collideSimple( aOther, aClearance, aNeedMTV, aMTV, aDifferentNetsOnly ) )
return true;
// special case for "head" line with a via attached at the end.
@ -50,7 +50,7 @@ bool PNS_ITEM::Collide( const PNS_ITEM* aOther, int aClearance, bool aNeedMTV,
const PNS_LINE* line = static_cast<const PNS_LINE*>( aOther );
if( line->EndsWithVia() )
return collideSimple( &line->Via(), aClearance - line->Width() / 2, aNeedMTV, aMTV );
return collideSimple( &line->Via(), aClearance - line->Width() / 2, aNeedMTV, aMTV, aDifferentNetsOnly );
}
return false;

View File

@ -270,18 +270,18 @@ public:
* @return true, if a collision was found.
*/
virtual bool Collide( const PNS_ITEM* aOther, int aClearance, bool aNeedMTV,
VECTOR2I& aMTV ) const;
VECTOR2I& aMTV, bool aDifferentNetsOnly = true ) const;
/**
* Function Collide()
*
* A shortcut for PNS_ITEM::Colllide() without MTV stuff.
*/
bool Collide( const PNS_ITEM* aOther, int aClearance ) const
bool Collide( const PNS_ITEM* aOther, int aClearance, bool aDifferentNetsOnly = true ) const
{
VECTOR2I dummy;
return Collide( aOther, aClearance, false, dummy );
return Collide( aOther, aClearance, false, dummy, aDifferentNetsOnly );
}
/**
@ -332,7 +332,7 @@ public:
private:
bool collideSimple( const PNS_ITEM* aOther, int aClearance, bool aNeedMTV,
VECTOR2I& aMTV ) const;
VECTOR2I& aMTV, bool aDifferentNetsOnly ) const;
protected:
PnsKind m_kind;

View File

@ -477,8 +477,13 @@ bool PNS_LINE_PLACER::rhShoveOnly( const VECTOR2I& aP, PNS_LINE& aNewHead )
m_currentNode = m_shove->CurrentNode();
if( status == PNS_SHOVE::SH_OK )
if( status == PNS_SHOVE::SH_OK || status == PNS_SHOVE::SH_HEAD_MODIFIED )
{
if( status == PNS_SHOVE::SH_HEAD_MODIFIED )
{
l2 = m_shove->NewHead();
}
optimizer.SetWorld( m_currentNode );
optimizer.SetEffortLevel( PNS_OPTIMIZER::MERGE_OBTUSE | PNS_OPTIMIZER::SMART_PADS );
optimizer.SetCollisionMask( PNS_ITEM::ANY );
@ -1087,3 +1092,11 @@ void PNS_LINE_PLACER::GetModifiedNets( std::vector<int>& aNets ) const
{
aNets.push_back( m_currentNet );
}
PNS_LOGGER* PNS_LINE_PLACER::Logger()
{
if( m_shove )
return m_shove->Logger();
return NULL;
}

View File

@ -184,6 +184,10 @@ public:
bool IsPlacingVia() const { return m_placingVia; }
void GetModifiedNets( std::vector<int>& aNets ) const;
PNS_LOGGER* Logger();
private:
/**
* Function route()

View File

@ -185,7 +185,11 @@ struct PNS_NODE::OBSTACLE_VISITOR
///> additional clearance
int m_extraClearance;
OBSTACLE_VISITOR( PNS_NODE::OBSTACLES& aTab, const PNS_ITEM* aItem, int aKindMask ) :
bool m_differentNetsOnly;
int m_forceClearance;
OBSTACLE_VISITOR( PNS_NODE::OBSTACLES& aTab, const PNS_ITEM* aItem, int aKindMask, bool aDifferentNetsOnly ) :
m_node( NULL ),
m_override( NULL ),
m_tab( aTab ),
@ -193,7 +197,9 @@ struct PNS_NODE::OBSTACLE_VISITOR
m_kindMask( aKindMask ),
m_limitCount( -1 ),
m_matchCount( 0 ),
m_extraClearance( 0 )
m_extraClearance( 0 ),
m_differentNetsOnly( aDifferentNetsOnly ),
m_forceClearance( -1 )
{
if( aItem->Kind() == PNS_ITEM::LINE )
m_extraClearance += static_cast<const PNS_LINE*>( aItem )->Width() / 2;
@ -228,7 +234,10 @@ struct PNS_NODE::OBSTACLE_VISITOR
if( aItem->Kind() == PNS_ITEM::LINE )
clearance += static_cast<PNS_LINE*>( aItem )->Width() / 2;
if( !aItem->Collide( m_item, clearance ) )
if( m_forceClearance >= 0 )
clearance = m_forceClearance;
if( !aItem->Collide( m_item, clearance, m_differentNetsOnly ) )
return true;
PNS_OBSTACLE obs;
@ -248,9 +257,9 @@ struct PNS_NODE::OBSTACLE_VISITOR
int PNS_NODE::QueryColliding( const PNS_ITEM* aItem,
PNS_NODE::OBSTACLES& aObstacles, int aKindMask, int aLimitCount )
PNS_NODE::OBSTACLES& aObstacles, int aKindMask, int aLimitCount, bool aDifferentNetsOnly, int aForceClearance )
{
OBSTACLE_VISITOR visitor( aObstacles, aItem, aKindMask );
OBSTACLE_VISITOR visitor( aObstacles, aItem, aKindMask, aDifferentNetsOnly );
#ifdef DEBUG
assert( allocNodes.find( this ) != allocNodes.end() );
@ -258,7 +267,7 @@ int PNS_NODE::QueryColliding( const PNS_ITEM* aItem,
visitor.SetCountLimit( aLimitCount );
visitor.SetWorld( this, NULL );
visitor.m_forceClearance = aForceClearance;
// first, look for colliding items in the local index
m_index->Query( aItem, m_maxClearance, visitor );
@ -273,7 +282,9 @@ int PNS_NODE::QueryColliding( const PNS_ITEM* aItem,
}
PNS_NODE::OPT_OBSTACLE PNS_NODE::NearestObstacle( const PNS_LINE* aItem, int aKindMask )
PNS_NODE::OPT_OBSTACLE PNS_NODE::NearestObstacle( const PNS_LINE* aItem,
int aKindMask,
const std::set<PNS_ITEM*>* aRestrictedSet )
{
OBSTACLES obs_list;
bool found_isects = false;
@ -293,7 +304,6 @@ PNS_NODE::OPT_OBSTACLE PNS_NODE::NearestObstacle( const PNS_LINE* aItem, int aKi
if( aItem->EndsWithVia() )
n += QueryColliding( &aItem->Via(), obs_list, aKindMask );
// if(! QueryColliding ( aItem, obs_list, aKindMask ))
if( !n )
return OPT_OBSTACLE();
@ -308,6 +318,9 @@ PNS_NODE::OPT_OBSTACLE PNS_NODE::NearestObstacle( const PNS_LINE* aItem, int aKi
VECTOR2I ip_first, ip_last;
int dist_max = INT_MIN;
if( aRestrictedSet && aRestrictedSet->find( obs.m_item ) == aRestrictedSet->end() )
continue;
std::vector<SHAPE_LINE_CHAIN::INTERSECTION> isect_list;
int clearance = GetClearance( obs.m_item, &aLine );

View File

@ -184,7 +184,9 @@ public:
int QueryColliding( const PNS_ITEM* aItem,
OBSTACLES& aObstacles,
int aKindMask = PNS_ITEM::ANY,
int aLimitCount = -1 );
int aLimitCount = -1,
bool aDifferentNetsOnly = true,
int aForceClearance = -1 );
/**
* Function NearestObstacle()
@ -196,7 +198,8 @@ public:
* @return the obstacle, if found, otherwise empty.
*/
OPT_OBSTACLE NearestObstacle( const PNS_LINE* aItem,
int aKindMask = PNS_ITEM::ANY );
int aKindMask = PNS_ITEM::ANY,
const std::set<PNS_ITEM*>* aRestrictedSet = NULL );
/**
* Function CheckColliding()

View File

@ -125,6 +125,9 @@ int PNS_PCBNEW_CLEARANCE_FUNC::operator()( const PNS_ITEM* aA, const PNS_ITEM* a
bool linesOnly = aA->OfKind( PNS_ITEM::SEGMENT | PNS_ITEM::LINE ) && aB->OfKind( PNS_ITEM::SEGMENT | PNS_ITEM::LINE );
if( net_a == net_b )
return 0;
if( linesOnly && net_a >= 0 && net_b >= 0 && m_clearanceCache[net_a].coupledNet == net_b )
{
cl_a = cl_b = m_router->Sizes().DiffPairGap() - 2 * PNS_HULL_MARGIN;
@ -1041,6 +1044,10 @@ void PNS_ROUTER::DumpLog()
logger = m_dragger->Logger();
break;
case ROUTE_TRACK:
logger = m_placer->Logger();
break;
default:
break;
}

View File

@ -39,6 +39,7 @@
#include "pns_router.h"
#include "pns_shove.h"
#include "pns_utils.h"
#include "pns_topology.h"
#include "time_limit.h"
@ -242,7 +243,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::processHullSet( PNS_LINE& aCurrent, PNS_LINE&
return SH_OK;
}
return failingDirCheck ? SH_OK : SH_INCOMPLETE;
return SH_INCOMPLETE;
}
@ -277,7 +278,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ProcessSingleLine( PNS_LINE& aCurrent, PNS_LI
int w = aObstacle.Width();
int n_segs = aCurrent.SegmentCount();
int clearance = getClearance( &aCurrent, &aObstacle );
int clearance = getClearance( &aCurrent, &aObstacle ) + 1;
HULL_SET hulls;
@ -313,6 +314,18 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingSegment( PNS_LINE& aCurrent, PNS_S
SHOVE_STATUS rv = ProcessSingleLine( aCurrent, obstacleLine, shovedLine );
const double extensionWalkThreshold = 1.0;
double obsLen = obstacleLine.CLine().Length();
double shovedLen = shovedLine.CLine().Length();
double extensionFactor = 0.0;
if( obsLen != 0.0f )
extensionFactor = shovedLen / obsLen - 1.0;
if( extensionFactor > extensionWalkThreshold )
return SH_TRY_WALK;
assert( obstacleLine.LayersOverlap( &shovedLine ) );
#ifdef DEBUG
@ -333,12 +346,12 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingSegment( PNS_LINE& aCurrent, PNS_S
m_newHead = shovedLine;
}
sanityCheck( &obstacleLine, &shovedLine );
replaceItems( &obstacleLine, &shovedLine );
int rank = aCurrent.Rank();
shovedLine.SetRank( rank - 1 );
sanityCheck( &obstacleLine, &shovedLine );
replaceItems( &obstacleLine, &shovedLine );
if( !pushLine( shovedLine ) )
rv = SH_INCOMPLETE;
}
@ -380,15 +393,13 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingLine( PNS_LINE& aCurrent, PNS_LINE
if( !pushLine( shovedLine ) )
{
rv = SH_INCOMPLETE;
//printf( "pushLine failed\n" );
}
}
return rv;
}
PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingSolid( PNS_LINE& aCurrent, PNS_SOLID* aObstacleSolid )
PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingSolid( PNS_LINE& aCurrent, PNS_ITEM* aObstacle )
{
PNS_WALKAROUND walkaround( m_currentNode, Router() );
PNS_LINE walkaroundLine( aCurrent );
@ -411,26 +422,43 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingSolid( PNS_LINE& aCurrent, PNS_SOL
}
}
if( via && m_currentNode->CheckColliding( via, aObstacleSolid ) )
return onCollidingVia( aObstacleSolid, via );
if( via && m_currentNode->CheckColliding( via, aObstacle ) )
return onCollidingVia( aObstacle, via );
}
walkaround.SetSolidsOnly( true );
walkaround.SetIterationLimit ( 8 ); // fixme: make configurable
PNS_TOPOLOGY topo( m_currentNode );
std::set<PNS_ITEM*> cluster = topo.AssembleCluster( aObstacle, aCurrent.Layers().Start() );
#ifdef DEBUG
m_logger.NewGroup( "on-colliding-solid-cluster", m_iter );
BOOST_FOREACH( PNS_ITEM* item, cluster )
{
m_logger.Log( item, 0, "cluster-entry" );
}
#endif
walkaround.SetSolidsOnly( false );
walkaround.RestrictToSet( true, cluster );
walkaround.SetIterationLimit( 16 ); // fixme: make configurable
int currentRank = aCurrent.Rank();
int nextRank;
if( !Settings().JumpOverObstacles() )
for( int attempt = 0; attempt < 2; attempt++ )
{
nextRank = currentRank + 10000;
walkaround.SetSingleDirection( false );
}
else
if( attempt == 1 || Settings().JumpOverObstacles() )
{
nextRank = currentRank - 1;
walkaround.SetSingleDirection( true );
}
else
{
nextRank = currentRank + 10000;
walkaround.SetSingleDirection( false );
}
if( walkaround.Route( aCurrent, walkaroundLine, false ) != PNS_WALKAROUND::DONE )
return SH_INCOMPLETE;
@ -453,12 +481,28 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingSolid( PNS_LINE& aCurrent, PNS_SOL
}
sanityCheck( &aCurrent, &walkaroundLine );
if( !m_lineStack.empty() )
{
PNS_LINE lastLine = m_lineStack.front();
if( m_currentNode->CheckColliding( &lastLine, &walkaroundLine ) )
{
PNS_LINE dummy ( lastLine );
if( ProcessSingleLine( walkaroundLine, lastLine, dummy ) == SH_OK )
break;
} else
break;
}
}
replaceItems( &aCurrent, &walkaroundLine );
walkaroundLine.SetRank( nextRank );
#ifdef DEBUG
m_logger.NewGroup( "on-colliding-solid", m_iter );
m_logger.Log( aObstacleSolid, 0, "obstacle-solid" );
m_logger.Log( aObstacle, 0, "obstacle-solid" );
m_logger.Log( &aCurrent, 1, "current-line" );
m_logger.Log( &walkaroundLine, 3, "walk-line" );
#endif
@ -620,7 +664,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::pushVia( PNS_VIA* aVia, const VECTOR2I& aForc
replaceItems( &lp.first, &lp.second );
lp.second.SetRank( aCurrentRank - 1 );
if( !pushLine( lp.second ) )
if( !pushLine( lp.second, true ) )
return SH_INCOMPLETE;
}
else
@ -807,12 +851,20 @@ void PNS_SHOVE::unwindStack( PNS_ITEM* aItem )
}
bool PNS_SHOVE::pushLine( const PNS_LINE& aL )
bool PNS_SHOVE::pushLine( const PNS_LINE& aL, bool aKeepCurrentOnTop )
{
if( aL.LinkCount() >= 0 && ( aL.LinkCount() != aL.SegmentCount() ) )
return false;
if( aKeepCurrentOnTop && m_lineStack.size() > 0)
{
m_lineStack.insert( m_lineStack.begin() + m_lineStack.size() - 1, aL );
}
else
{
m_lineStack.push_back( aL );
}
m_optimizerQueue.push_back( aL );
return true;
@ -919,6 +971,11 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::shoveIteration( int aIter )
case PNS_ITEM::SEGMENT:
TRACE( 2, "iter %d: collide-segment ", aIter );
st = onCollidingSegment( currentLine, (PNS_SEGMENT*) ni );
if( st == SH_TRY_WALK )
{
st = onCollidingSolid( currentLine, (PNS_SOLID*) ni );
}
break;
case PNS_ITEM::VIA:

View File

@ -49,7 +49,8 @@ public:
SH_OK = 0,
SH_NULL,
SH_INCOMPLETE,
SH_HEAD_MODIFIED
SH_HEAD_MODIFIED,
SH_TRY_WALK
};
PNS_SHOVE( PNS_NODE* aWorld, PNS_ROUTER* aRouter );
@ -110,7 +111,7 @@ private:
SHOVE_STATUS onCollidingLine( PNS_LINE& aCurrent, PNS_LINE& aObstacle );
SHOVE_STATUS onCollidingSegment( PNS_LINE& aCurrent, PNS_SEGMENT* aObstacleSeg );
SHOVE_STATUS onCollidingSolid( PNS_LINE& aCurrent, PNS_SOLID* aObstacleSolid );
SHOVE_STATUS onCollidingSolid( PNS_LINE& aCurrent, PNS_ITEM* aObstacle );
SHOVE_STATUS onCollidingVia( PNS_ITEM* aCurrent, PNS_VIA* aObstacleVia );
SHOVE_STATUS onReverseCollidingVia( PNS_LINE& aCurrent, PNS_VIA* aObstacleVia );
SHOVE_STATUS pushVia( PNS_VIA* aVia, const VECTOR2I& aForce, int aCurrentRank, bool aDryRun = false );
@ -122,7 +123,7 @@ private:
void runOptimizer( PNS_NODE* aNode );
bool pushLine( const PNS_LINE& aL );
bool pushLine( const PNS_LINE& aL, bool aKeepCurrentOnTop = false );
void popLine();
PNS_LINE assembleLine( const PNS_SEGMENT* aSeg, int* aIndex = NULL );

View File

@ -396,3 +396,34 @@ bool PNS_TOPOLOGY::AssembleDiffPair( PNS_ITEM* aStart, PNS_DIFF_PAIR& aPair )
return true;
}
const std::set<PNS_ITEM*> PNS_TOPOLOGY::AssembleCluster( PNS_ITEM* aStart, int aLayer )
{
std::set<PNS_ITEM*> visited;
std::deque<PNS_ITEM*> pending;
pending.push_back( aStart );
while( !pending.empty() )
{
PNS_NODE::OBSTACLES obstacles;
PNS_ITEM* top = pending.front();
pending.pop_front();
visited.insert( top );
m_world->QueryColliding( top, obstacles, PNS_ITEM::ANY, -1, false, 0 );
BOOST_FOREACH( PNS_OBSTACLE& obs, obstacles )
{
if( visited.find( obs.m_item ) == visited.end() && obs.m_item->Layers().Overlaps( aLayer ) && !( obs.m_item->Marker() & MK_HEAD ) )
{
visited.insert( obs.m_item );
pending.push_back( obs.m_item );
}
}
}
return visited;
}

View File

@ -61,6 +61,8 @@ public:
const PNS_LINE DpCoupledLine( PNS_LINE* aLine );
bool AssembleDiffPair( PNS_ITEM* aStart, PNS_DIFF_PAIR& aPair );
const std::set<PNS_ITEM*> AssembleCluster( PNS_ITEM* aStart, int aLayer );
private:
bool followTrivialPath( PNS_LINE* aLine, bool aLeft, PNS_ITEMSET& aSet, std::set<PNS_ITEM*>& aVisited );

View File

@ -38,7 +38,15 @@ void PNS_WALKAROUND::start( const PNS_LINE& aInitialPath )
PNS_NODE::OPT_OBSTACLE PNS_WALKAROUND::nearestObstacle( const PNS_LINE& aPath )
{
return m_world->NearestObstacle( &aPath, m_itemMask );
PNS_NODE::OPT_OBSTACLE obs = m_world->NearestObstacle( &aPath, m_itemMask, m_restrictedSet.empty() ? NULL : &m_restrictedSet );
if( m_restrictedSet.empty() )
return obs;
else if( obs && m_restrictedSet.find ( obs->m_item ) != m_restrictedSet.end() )
return obs;
return PNS_NODE::OPT_OBSTACLE();
}
@ -93,7 +101,7 @@ PNS_WALKAROUND::WALKAROUND_STATUS PNS_WALKAROUND::singleStep( PNS_LINE& aPath,
SHAPE_LINE_CHAIN pnew;
if( !m_forceSingleDirection && len_alt < len_pre && !alt_collides && !prev_recursive )
if( !m_forceLongerPath && len_alt < len_pre && !alt_collides && !prev_recursive )
{
pnew = path_pre[1];
pnew.Append( path_walk[1] );

View File

@ -21,6 +21,8 @@
#ifndef __PNS_WALKAROUND_H
#define __PNS_WALKAROUND_H
#include <set>
#include "pns_line.h"
#include "pns_node.h"
#include "pns_router.h"
@ -105,6 +107,14 @@ public:
m_forceWinding = aEnabled;
}
void RestrictToSet( bool aEnabled, const std::set<PNS_ITEM*>& aSet )
{
if( aEnabled )
m_restrictedSet = aSet;
else
m_restrictedSet.clear();
}
WALKAROUND_STATUS Route( const PNS_LINE& aInitialPath, PNS_LINE& aWalkPath,
bool aOptimize = true );
@ -133,6 +143,7 @@ private:
PNS_NODE::OPT_OBSTACLE m_currentObstacle[2];
bool m_recursiveCollision[2];
PNS_LOGGER m_logger;
std::set<PNS_ITEM*> m_restrictedSet;
};
#endif // __PNS_WALKAROUND_H