使用QTLCLD在Quadtree邻居中搜索恒定时间

时间:2013-10-28 15:03:23

标签: c++ tree linear nearest-neighbor quadtree

我希望实现this论文的算法,以便在恒定时间内访问四叉树节点邻居。

我在尝试访问对角线邻居时遇到问题(当四边形比搜索到的邻居小一个或多个级别时)。示例:root->Child(SE)->Child(NE)->GetNeighbor(NW)应返回root->Child(NE)。但是,我得到root->Child(NW)的结果。

唯一的问题是不同级别的对角线搜索。其他的东西工作正常;我可以找到同一级别的邻居或从较小级别到较大级别的邻居没有问题。

以下是代码:

#define QUAD_MAX_LEVEL 16
#define QUAD_MAX_UNITS 20

#define SOUTH_WEST 0
#define SOUTH_EAST 1
#define NORTH_WEST 2
#define NORTH_EAST 3

#define NORTH 4
#define WEST 5
#define SOUTH 6
#define EAST 7

// Precalculated QTLCLD direction increments for r = 16 = max level
#define EAST_NEIGHBOR 0x01
#define NORTH_EAST_NEIGHBOR 0x03
#define NORTH_NEIGHBOR 0x02
#define NORTH_WEST_NEIGHBOR 0x55555557
#define WEST_NEIGHBOR 0x55555555
#define SOUTH_WEST_NEIGHBOR 0xFFFFFFFF
#define SOUTH_NEIGHBOR 0xAAAAAAAA
#define SOUTH_EAST_NEIGHBOR 0xAAAAAAAB

#define tx 0x55555555
#define ty 0xAAAAAAAA


class Quad;
typedef std::shared_ptr< Quad > QuadPtr;
typedef std::weak_ptr< Quad > QuadWeakPtr;

class Quad {
public:
    static std::vector< QuadPtr > & s_GetLinearTree() {
        static std::vector< QuadPtr > linearTree( pow( QUAD_MAX_LEVEL, 4 ) );
        return linearTree;
    }

    enum Index { None = 0x00, North = 0x10, West = 0x20, South = 0x40, East = 0x80, NorthWest = 0x31, NorthEast = 0x92, SouthWest = 0x64, SouthEast = 0xC8  };

    Index index;
    int position;
    unsigned int level;
    int neighborSizes[8];

    Rectangle quadrant;
    bool hasChildren;

    QuadPtr parent;
    std::vector< QuadPtr > quads;
    std::list< UnitWeakPtr > units;

    Quad( Index p_index, const Rectangle &p_rect, unsigned int p_level, int p_position, QuadPtr p_parent = QuadPtr() ) : quadrant( p_rect ), quads( 4 ), parent( p_parent ) {
        index = p_index;
        position = p_position;
        hasChildren = false;
        level = p_level;

        // standard value zero
        for( int i = 0; i < 8; i++ )
            neighborSizes[i] = 0;

        if( parent.get() != NULL )
            calcNeighborsSizes( InxToI( p_index ) );
    }

    void Clear() {
        units.clear();

        for( auto quad : quads ) {
            if( quad.get() != NULL )
                quad->Clear();      
        }

        quads.clear();
    }

    int getIndex( const Rectangle &p_rect ) {
        if( !hasChildren ) {
            if( level < QUAD_MAX_LEVEL )
                Split();
            else 
                return 0;
        }

        int index = None;

        if( quads[NORTH_WEST]->quadrant.isContaining( p_rect.p0 ) || quads[NORTH_WEST]->quadrant.isContaining( p_rect.p1 ) || 
            quads[NORTH_WEST]->quadrant.isContaining( p_rect.p2 ) || quads[NORTH_WEST]->quadrant.isContaining( p_rect.p3 ) ) {
                index = index | NorthWest;
        }

        if( quads[NORTH_EAST]->quadrant.isContaining( p_rect.p0 ) || quads[NORTH_EAST]->quadrant.isContaining( p_rect.p1 ) || 
            quads[NORTH_EAST]->quadrant.isContaining( p_rect.p2 ) || quads[NORTH_EAST]->quadrant.isContaining( p_rect.p3 ) ) {
                index = index | NorthEast;
        }

        if( quads[SOUTH_WEST]->quadrant.isContaining( p_rect.p0 ) || quads[SOUTH_WEST]->quadrant.isContaining( p_rect.p1 ) || 
            quads[SOUTH_WEST]->quadrant.isContaining( p_rect.p2 ) || quads[SOUTH_WEST]->quadrant.isContaining( p_rect.p3 ) ) {
                index = index | SouthWest;
        }

        if( quads[SOUTH_EAST]->quadrant.isContaining( p_rect.p0 ) || quads[SOUTH_EAST]->quadrant.isContaining( p_rect.p1 ) || 
            quads[SOUTH_EAST]->quadrant.isContaining( p_rect.p2 ) || quads[SOUTH_EAST]->quadrant.isContaining( p_rect.p3 ) ) {
                index = index | SouthEast;
        }

        return index;
    }

    void Insert( UnitPtr p_unit ) {
        if( p_unit.get() == NULL )
            return;

        int index = getIndex( p_unit->boundingBox->box );

        if( index != 0 ) {
            if( NorthWest == ( index & NorthWest ) )
                quads[NORTH_WEST]->Insert( p_unit );

            if( NorthEast == ( index & NorthEast ) )
                quads[NORTH_EAST]->Insert( p_unit );

            if( SouthWest == ( index & SouthWest ) )
                quads[SOUTH_WEST]->Insert( p_unit );

            if( SouthEast == ( index & SouthEast ) )
                quads[SOUTH_EAST]->Insert( p_unit );

            return;
        }

        units.push_back( p_unit );
    }

    inline unsigned char InxToI( Index p_index ) {
        if( p_index == NorthWest )
            return NORTH_WEST;

        if( p_index == NorthEast )
            return NORTH_EAST;

        if( p_index == SouthWest )
            return SOUTH_WEST;

        if( p_index == SouthEast )
            return SOUTH_EAST;

        return 0;
    }

    // elements are not unique
    void Retrieve( const Rectangle &p_box, std::list< UnitPtr > &retUnits ) {
        if( hasChildren ) {
            int index = getIndex( p_box );

            if( NorthWest == ( index & NorthWest ) )
                quads[NORTH_WEST]->Retrieve( p_box, retUnits );

            if( NorthEast == ( index & NorthEast ) )
                quads[NORTH_EAST]->Retrieve( p_box, retUnits );

            if( SouthWest == ( index & SouthWest ) )
                quads[SOUTH_WEST]->Retrieve( p_box, retUnits );

            if( SouthEast == ( index & SouthEast ) )
                quads[SOUTH_EAST]->Retrieve( p_box, retUnits );
        }

        retUnits.insert( retUnits.end(), units.begin(), units.end() );
    }

    void Split() {
        int subWidth = (int)( quadrant.Width() / 2 );
        int subHeight = (int)( quadrant.Height() / 2 );
        int x = (int) quadrant.p0.getX();
        int y = (int) quadrant.p0.getY();


        quads[SOUTH_WEST] = QuadPtr( new Quad( SouthWest, Rectangle( Vector3( x, y + subHeight, 0.0f ), subWidth, subHeight), level + 1, calcPosition( SOUTH_WEST ), QuadPtr( this, nodelete() ) ) );
        quads[SOUTH_EAST] = QuadPtr( new Quad( SouthEast, Rectangle( Vector3( x + subWidth, y + subHeight, 0.0f ), subWidth, subHeight), level + 1,  calcPosition( SOUTH_EAST ), QuadPtr( this, nodelete() ) ) );
        quads[NORTH_WEST] = QuadPtr( new Quad( NorthWest, Rectangle( Vector3( x, y, 0.0f ), subWidth, subHeight), level + 1, calcPosition( NORTH_WEST ), QuadPtr( this, nodelete() ) ) );
        quads[NORTH_EAST] = QuadPtr( new Quad( NorthEast, Rectangle( Vector3( x + subWidth, y, 0.0f ), subWidth, subHeight ), level + 1, calcPosition( NORTH_EAST ),  QuadPtr( this, nodelete() ) ) );      

        hasChildren = true;

        // add to linear tree
        s_GetLinearTree().push_back( quads[SOUTH_WEST] );
        s_GetLinearTree().push_back( quads[SOUTH_EAST] );
        s_GetLinearTree().push_back( quads[NORTH_WEST] );
        s_GetLinearTree().push_back( quads[NORTH_EAST] );

        // look for neighbors with this as neighbor index in linear tree and increment same index in size with one
        incNeighborSize( position, parent );
    }

    // ToDo: this is not finding all neighbors, only the one within the same parent!
    void incNeighborSize( int p_position, QuadPtr p_entry ) {
        if( parent.get() == NULL )
            return;

        for( auto quad : p_entry->quads ) {
            for( int i = 0; i < 8; i++ ) {
                if( quad->getNeighbor( i ) == p_position ) {

                    if( quad->neighborSizes[i] < 1 )
                        quad->neighborSizes[i] += 1;

                    // recursion: find all children of children with this as neighbor
                    if( quad->hasChildren )
                        quad->incNeighborSize( p_position, quad );
                }
            }
        }
    }

    int getNeighbor( int p_location ) {
        if( neighborSizes[p_location] == INT_MAX ) {
            return INT_MAX;
        }

        int neigborBin = 0;

        switch( p_location ) {
        case WEST:
            neigborBin = WEST_NEIGHBOR;
            break;
        case NORTH:
            neigborBin = NORTH_NEIGHBOR;
            break;
        case EAST:
            neigborBin = EAST_NEIGHBOR;
            break;
        case SOUTH:
            neigborBin = SOUTH_NEIGHBOR;
            break;
        case NORTH_EAST:
            neigborBin = NORTH_EAST_NEIGHBOR;
            break;
        case NORTH_WEST:
            neigborBin = NORTH_WEST_NEIGHBOR;
            break;
        case SOUTH_EAST:
            neigborBin = SOUTH_EAST_NEIGHBOR;
            break;
        case SOUTH_WEST:
            neigborBin = SOUTH_WEST_NEIGHBOR;
            break;
        default:
            return 0;
        }

        if( neighborSizes[p_location] < 0 ) {
            int shift = ( 2 * ( QUAD_MAX_LEVEL - level - neighborSizes[p_location] ) );
            return quad_location_add( ( position >> shift ) << shift, neigborBin << shift );
        } else {
            return quad_location_add( position, neigborBin << ( 2 * ( QUAD_MAX_LEVEL - level ) ) );
        }
    }

    // ToDo: merge quads children to this one, and decrement neighbors size to this one
    void Merge() {
        hasChildren = false;

    }

    int calcPosition( int p_location ) {
        return position | ( p_location << ( 2 * ( QUAD_MAX_LEVEL - ( level + 1 ) ) ) );
    }


    // Fig. 7: change if child is north, take north neighbor of this
    void calcNeighborsSizes( int p_location ) {
        if( p_location == NORTH_WEST  ) {
            if( parent->neighborSizes[NORTH] == INT_MAX )
                neighborSizes[NORTH_EAST] = INT_MAX;
            else
                neighborSizes[NORTH_EAST] = parent->neighborSizes[NORTH] - 1;
        }

        if( p_location == NORTH_WEST || p_location == NORTH_EAST ) {
            if( parent->neighborSizes[NORTH] == INT_MAX )
                neighborSizes[NORTH] = INT_MAX;
            else
                neighborSizes[NORTH] = parent->neighborSizes[NORTH] - 1;
        }

        if( p_location == NORTH_WEST ) {
            if( parent->neighborSizes[NORTH_WEST] == INT_MAX )
                neighborSizes[NORTH_WEST] = INT_MAX;
            else
                neighborSizes[NORTH_WEST] = parent->neighborSizes[NORTH_WEST] - 1;
        }

        if( p_location == NORTH_WEST ) {
            if( parent->neighborSizes[WEST] == INT_MAX )
                neighborSizes[WEST] = INT_MAX;
            else
                neighborSizes[WEST] = parent->neighborSizes[WEST] - 1;
        }

        if( p_location == NORTH_WEST  ) {
            if( parent->neighborSizes[WEST] == INT_MAX )
                neighborSizes[SOUTH_WEST] = INT_MAX;
            else
                neighborSizes[SOUTH_WEST] = parent->neighborSizes[WEST] - 1;
        }


        if( p_location == NORTH_EAST  ) {
            if( parent->neighborSizes[NORTH_EAST] == INT_MAX )
                neighborSizes[NORTH_EAST] = INT_MAX;
            else
                neighborSizes[NORTH_EAST] = parent->neighborSizes[NORTH_EAST] - 1;
        }

        if( p_location == NORTH_EAST  ) {
            if( parent->neighborSizes[EAST] == INT_MAX )
                neighborSizes[SOUTH_EAST] = INT_MAX;
            else
                neighborSizes[SOUTH_EAST] = parent->neighborSizes[EAST] - 1;
        }

        if( p_location == NORTH_EAST  ) {
            if( parent->neighborSizes[NORTH] == INT_MAX )
                neighborSizes[NORTH] = INT_MAX;
            else
                neighborSizes[NORTH] = parent->neighborSizes[NORTH] - 1;
        }

        if( p_location == NORTH_EAST  ) {
            if( parent->neighborSizes[NORTH] == INT_MAX )
                neighborSizes[NORTH_WEST] = INT_MAX;
            else
                neighborSizes[NORTH_WEST] = parent->neighborSizes[NORTH] - 1;
        }

        if( p_location == NORTH_EAST  ) {
            if( parent->neighborSizes[EAST] == INT_MAX )
                neighborSizes[EAST] = INT_MAX;
            else
                neighborSizes[EAST] = parent->neighborSizes[EAST] - 1;
        }


        if( p_location == SOUTH_EAST  ) {
            if( parent->neighborSizes[EAST] == INT_MAX )
                neighborSizes[EAST] = INT_MAX;
            else
                neighborSizes[EAST] = parent->neighborSizes[EAST] - 1;
        }

        if( p_location == SOUTH_EAST  ) {
            if( parent->neighborSizes[EAST] == INT_MAX )
                neighborSizes[NORTH_EAST] = INT_MAX;
            else
                neighborSizes[NORTH_EAST] = parent->neighborSizes[EAST] - 1;
        }

        if( p_location == SOUTH_EAST  ) {
            if( parent->neighborSizes[SOUTH_EAST] == INT_MAX )
                neighborSizes[SOUTH_EAST] = INT_MAX;
            else
                neighborSizes[SOUTH_EAST] = parent->neighborSizes[SOUTH_EAST] - 1;
        }

        if( p_location == SOUTH_EAST  ) {
            if( parent->neighborSizes[SOUTH] == INT_MAX )
                neighborSizes[SOUTH] = INT_MAX;
            else
                neighborSizes[SOUTH] = parent->neighborSizes[SOUTH] - 1;
        }

        if( p_location == SOUTH_EAST  ) {
            if( parent->neighborSizes[SOUTH] == INT_MAX )
                neighborSizes[SOUTH_WEST] = INT_MAX;
            else
                neighborSizes[SOUTH_WEST] = parent->neighborSizes[SOUTH] - 1;
        }

        if( p_location == SOUTH_WEST  ) {
            if( parent->neighborSizes[SOUTH] == INT_MAX )
                neighborSizes[SOUTH_EAST] = INT_MAX;
            else
                neighborSizes[SOUTH_EAST] = parent->neighborSizes[SOUTH] - 1;
        }

        if( p_location == SOUTH_WEST  ) {
            if( parent->neighborSizes[SOUTH] == INT_MAX )
                neighborSizes[SOUTH] = INT_MAX;
            else
                neighborSizes[SOUTH] = parent->neighborSizes[SOUTH] - 1;
        }


        if( p_location == SOUTH_WEST  ) {
            if( parent->neighborSizes[SOUTH_WEST] == INT_MAX )
                neighborSizes[SOUTH_WEST] = INT_MAX;
            else
                neighborSizes[SOUTH_WEST] = parent->neighborSizes[SOUTH_WEST] - 1;
        }

        if( p_location == SOUTH_WEST  ) {
            if( parent->neighborSizes[WEST] == INT_MAX )
                neighborSizes[WEST] = INT_MAX;
            else
                neighborSizes[WEST] = parent->neighborSizes[WEST] - 1;
        }

        if( p_location == SOUTH_WEST  ) {
            if( parent->neighborSizes[WEST] == INT_MAX )
                neighborSizes[NORTH_WEST] = INT_MAX;
            else
                neighborSizes[NORTH_WEST] = parent->neighborSizes[WEST] - 1;
        }
    }

    int quad_location_add( int p_a, int p_b ) {
        return ( ( ( p_a | ty ) + ( p_b & tx ) ) & tx ) | ( ( ( p_a | tx ) + ( p_b & ty ) ) & ty );
    }
};

所需用法:     root = QuadPtr(new Quad(Quad :: None,Rectangle(0,0,400,400),0,0));     根 - &GT;分流();     根 - &GT;四边形[SOUTH_EAST] - GT;分流();

std::cout << "NE->SE->S  : " << root->quads[SOUTH_EAST]->quads[NORTH_EAST]->getNeighbor( NORTH_WEST ) << std::endl;
// is !=, but it have to be equal
std::cout << "SE->NE->NW : " << root->quads[SOUTH_EAST]->getNeighbor( NORTH ) << std::endl;

2 个答案:

答案 0 :(得分:1)

最新的paper(2015年)定义了 Cardinal Neighbors Quadtree ,这是一种区域细分的新技术,您可以在恒定时间内找到 < em> O(1) 叶子的所有邻居,与它们的大小无关。通过每个节点添加4个指针(所谓的基数邻居)来获得时间复杂度的降低。

我在Go中实现了算法:https://github.com/arl/go-rquad

答案 1 :(得分:-1)

只是一个猜测。 至少在JAVA中,“FFFFFFFF”大于Integer.MAX_VALUE(==“7FFFFFFF”)。那你可能会为你的南边邻居带来某种溢出吗?