802 lines
21 KiB
C++
802 lines
21 KiB
C++
// X2DQuadTree_divide_polygon.h
|
|
//
|
|
// Last modified by Young-Hyun Joo, 2007/12/11
|
|
//
|
|
// Created by Testors , 2005/08/01
|
|
|
|
#pragma once
|
|
|
|
//
|
|
// X2D 에 기반이지만 어짜피 완전히 generic 하게 만들었기 때문에
|
|
// X2D::INCLUDE( X2D::Rect< T >, U ), X2D::COLLISION( X2D::Rect< T >, U ), X2D::COLLISION( U, C ), X2D::COLLISION( X2D::Rect< T >, C )
|
|
// 만 주어진다면 3D 에서의 사용도 가능하다.
|
|
//
|
|
// T : 좌표계 타입
|
|
// U : 보관할 대상타입
|
|
// THRESHOLD : 한 노드에 몇개정도까지 U 를 저장할것인가의 여부
|
|
// C : 검색시 범위 타입
|
|
//
|
|
|
|
#include <vector>
|
|
#include <cassert>
|
|
|
|
#include "X2DBasicTypes.h"
|
|
|
|
|
|
namespace X2D
|
|
{
|
|
|
|
template< typename T, typename U, size_t THRESHOLD = 20, size_t MAX_DEPTH = 10 >
|
|
struct QuadTree_divide_polygon
|
|
{
|
|
struct PointHelper
|
|
{
|
|
PointHelper( Point< double > _point, size_t _index )
|
|
: point( _point ),
|
|
nIndex( _index )
|
|
{
|
|
bUsed = false;
|
|
#ifdef _DEBUG
|
|
bIn = false;
|
|
#endif
|
|
pPartner = NULL;
|
|
}
|
|
|
|
bool operator==( const Point< T >& r ) const
|
|
{
|
|
return (r.x == static_cast< T >( point.x )) &&
|
|
(r.y == static_cast< T >( point.y ));
|
|
}
|
|
bool operator==( size_t idx ) const
|
|
{
|
|
return (nIndex == idx);
|
|
}
|
|
|
|
size_t nIndex; // 내 Index 정보
|
|
Point< double > point; // 내 Point 정보(소수점 소실로 인한 오차 판별)
|
|
bool bUsed; // 한번 사용 되었는 지 (폴리곤 중복 생성 안하기 위하여 구별)
|
|
#ifdef _DEBUG
|
|
bool bIn;
|
|
#endif
|
|
PointHelper *pPartner; // Partner 정보
|
|
};
|
|
|
|
template< typename Allocator, typename Deleter >
|
|
static std::vector< U > divide_polygon( const U& polygon, const X2D::Line< T >& line, Allocator& allocator, Deleter& deleter )
|
|
{
|
|
std::vector< U > v;
|
|
|
|
if( polygon->GetBoundingBox().IsLooseCollision( line ) == false &&
|
|
polygon->IsLooseCollision( line ) == false )
|
|
{
|
|
v.push_back( polygon );
|
|
return v;
|
|
}
|
|
|
|
X2D::Line< double > real_line( line.begin.x, line.begin.y, line.end.x, line.end.y );
|
|
std::vector< PointHelper > vIntersection;
|
|
std::vector< X2D::Point< T > > vList;
|
|
X2D::Point< double > ptUnknownPT( 0 , 0 );
|
|
// 1. index를 기록하면서 vList에 집어넣는다.
|
|
for( size_t i = 0 ; i < polygon->Size(); ++i )
|
|
{
|
|
Line< T > currLine = polygon->GetSegment( i );
|
|
vList.push_back( currLine.begin );
|
|
|
|
Point< double > ptTemp;
|
|
X2D::Line< double > real_curr( currLine.begin.x, currLine.begin.y, currLine.end.x, currLine.end.y );
|
|
if( real_line.GetIntersectPoint( real_curr, &ptTemp ) )
|
|
{
|
|
// 시작점이 접해 있다면 끝점일때가 나오므로 그때 판단한다.
|
|
if( ptTemp == real_curr.begin )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
|
|
// 끝점이 접해 있다면 주변 상황을 파악해야 한다.
|
|
// 1. 접해만 있고 나눌 필요없는 경우(|<), 2. 일치하는 경우(|), 3.접해서 통과하는 경우 (/|\)
|
|
// 이 판단은 현재 선 첫 점과 다음선 끝 점을 선으로 만들어 기준선과 접하는지 본다.
|
|
if( ptTemp == real_curr.end )
|
|
{
|
|
// 현재 점과 다다음의 점으로 가는 선을 구한다.
|
|
size_t next_idx = i+1;
|
|
if( next_idx >= polygon->Size() ) // 끝점.
|
|
{
|
|
next_idx = 0;
|
|
}
|
|
|
|
Line< T > nextLine = polygon->GetSegment( next_idx );
|
|
Point< double > begin;
|
|
if( ptUnknownPT == Point< double >( 0, 0 ) )
|
|
{
|
|
begin = real_curr.begin;
|
|
}
|
|
else
|
|
{
|
|
begin = ptUnknownPT;
|
|
}
|
|
|
|
Line< double > testLine( begin.x, begin.y, nextLine.end.x, nextLine.end.y );
|
|
|
|
Point< double > ptTest;
|
|
// 1. 접해만 있고 나눌 필요없는 경우(|<) -> vIntersection이 아님
|
|
if( real_line.GetIntersectPoint( testLine, &ptTest ) == false )
|
|
{
|
|
ptUnknownPT.Set( 0, 0 );
|
|
continue;
|
|
}
|
|
|
|
// 2-a). nextLine이 겹치는 경우( | )
|
|
// -> 겹친 선을 빠져 나갈 때 판단 (2-b)를 기다린다 (반드시 다음 케이스가 2-b로 가게 됨, 단 처음부터 겹치는 점에서 시작하면 2-b)인 경우가 있긴 함)
|
|
if( ptTest == testLine.end )
|
|
{
|
|
if( ptUnknownPT == Point< double >( 0, 0 ) )
|
|
{
|
|
ptUnknownPT = testLine.begin;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// 2-b). curLine 겹치는 경우( | ) -> 같은 쪽이면 X, 다른 쪽으로 나가면 점 기록
|
|
if( ptTest == testLine.begin )
|
|
{
|
|
if( i == 0 ) // 맨 처음이 하필 겹치는 경우(예전 점 정보가 없으므로 끝점 정보 가져옴)
|
|
{
|
|
Point< T > lastPt = polygon->GetPoint( polygon->Size()-1 );
|
|
ptUnknownPT.Set( lastPt.GetX(), lastPt.GetY() );
|
|
}
|
|
|
|
Line< double > newTestLine( ptUnknownPT, testLine.end );
|
|
ptUnknownPT.Set( 0, 0 );
|
|
// 단순히 겹쳤다가 다시 같은 쪽으로 가므로 아무 짓도 안 함
|
|
if( real_line.GetIntersectPoint( testLine, &ptTest ) == false )
|
|
continue;
|
|
}
|
|
|
|
// 3. 접해서 통과하는 경우 (/|\), 그리고 2-b)에서 겹쳐서 밖으로 나오는 경우
|
|
// 맨마지막 꺼 외에는 vList에는 다음에 들어갈 것이다.
|
|
size_t idx = vList.size();
|
|
if( next_idx == 0 )
|
|
{
|
|
idx = 0;
|
|
}
|
|
|
|
vIntersection.push_back( PointHelper( ptTemp, idx ) );
|
|
ptUnknownPT.Set( 0, 0 );
|
|
continue;
|
|
}
|
|
|
|
Point< T > input( static_cast< T >( ptTemp.x ), static_cast< T >( ptTemp.y ) );
|
|
vList.push_back( input );
|
|
vIntersection.push_back( PointHelper( ptTemp, vList.size()-1 ) );
|
|
}
|
|
}
|
|
// 선 분할 끝났는데 미 처리 점이 남아있다면 이 녀석도 접점임. (첫 번째 점이겠지)
|
|
// if( ptUnknownPT.GetX() != 0 || ptUnknownPT.GetY() != 0 )
|
|
// {
|
|
// vIntersection.push_back( PointHelper( Point< double >( ptUnknownPT.GetX(), ptUnknownPT.GetY() ) , 0 ) );
|
|
// }
|
|
|
|
struct IntersectionPredicate
|
|
{
|
|
IntersectionPredicate( Point< double > _ptStandard )
|
|
: m_ptStandard( _ptStandard )
|
|
{}
|
|
|
|
bool operator() ( PointHelper& lhs, PointHelper& rhs )
|
|
{
|
|
double ld = lhs.point.GetDistance( m_ptStandard );
|
|
double rd = rhs.point.GetDistance( m_ptStandard );
|
|
assert( ld != rd );
|
|
if( ld < rd )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
Point< double > m_ptStandard;
|
|
};
|
|
|
|
if( vIntersection.empty() == true )
|
|
{
|
|
v.push_back( polygon );
|
|
return v;
|
|
}
|
|
|
|
// 2. 분할 line에 가까운 쪽으로 sorting을 통하여 vLineList 라인을 정리한다.
|
|
assert( (vIntersection.size() % 2) == 0 );
|
|
std::sort( vIntersection.begin(), vIntersection.end(), IntersectionPredicate( real_line.begin ) );
|
|
|
|
#ifdef _DEBUG
|
|
for( auto it = vIntersection.begin(); it != vIntersection.end(); ++it )
|
|
{
|
|
PointHelper &a = (*it);
|
|
a.bIn = false;
|
|
size_t next_idx = a.nIndex + 1;
|
|
if( next_idx >= vList.size() )
|
|
{
|
|
next_idx = 0;
|
|
}
|
|
|
|
if( line.begin.x == line.end.x )
|
|
{
|
|
if( vList[next_idx].x < line.begin.x )
|
|
{
|
|
a.bIn = true;
|
|
}
|
|
}
|
|
|
|
if( line.begin.y == line.end.y )
|
|
{
|
|
if( vList[next_idx].y < line.begin.y )
|
|
{
|
|
a.bIn = true;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
for( auto it = vIntersection.begin() ; it != vIntersection.end() ; it = it+2 )
|
|
{
|
|
PointHelper &a = (*it);
|
|
PointHelper &b = (*(it+1));
|
|
|
|
assert( a.bIn != b.bIn );
|
|
|
|
a.pPartner = &b;
|
|
b.pPartner = &a;
|
|
}
|
|
|
|
// 3. 폴리곤 생성 시작
|
|
Polygon< T > myPolygon;
|
|
if( myPolygon.Set( vList.begin(), vList.end() ) == false )
|
|
{
|
|
assert( 0 );
|
|
v.push_back( polygon );
|
|
return v;
|
|
}
|
|
|
|
std::vector< size_t > vCoverList;
|
|
vCoverList.push_back( 0 );
|
|
while( !vCoverList.empty() )
|
|
{
|
|
// 폴리곤 형성 시작
|
|
size_t i = *(vCoverList.begin());
|
|
vCoverList.erase( vCoverList.begin() );
|
|
std::vector< X2D::Point< T > > pol;
|
|
Point< T > pt = myPolygon.GetPoint( i );
|
|
pol.push_back( pt );
|
|
i = myPolygon.getNextIndex( i );
|
|
pt = myPolygon.GetPoint( i );
|
|
while( pol[0] != pt )
|
|
{
|
|
pol.push_back( pt );
|
|
// 이 점이 라인 상에 있는 점인지 찾음
|
|
auto itInter = std::find( vIntersection.begin(), vIntersection.end(), i );
|
|
// 라인 상에 있다면 점프
|
|
if( itInter != vIntersection.end() )
|
|
{
|
|
PointHelper *pPartner = (*itInter).pPartner;
|
|
if( !(*itInter).bUsed )
|
|
{
|
|
pPartner->bUsed = true;
|
|
(*itInter).bUsed = true;
|
|
vCoverList.push_back( (*itInter).nIndex );
|
|
}
|
|
i = pPartner->nIndex;
|
|
pt = myPolygon.GetPoint( i );
|
|
if( pol[0] == pt )
|
|
break;
|
|
pol.push_back( pt );
|
|
i = myPolygon.getNextIndex( i );
|
|
pt = myPolygon.GetPoint( i );
|
|
}
|
|
// 라인 상의 점이 아니라면 계속 진행
|
|
else
|
|
{
|
|
i = myPolygon.getNextIndex( i );
|
|
pt = myPolygon.GetPoint( i );
|
|
}
|
|
}
|
|
|
|
U pp = allocator();
|
|
if( pp->Set( pol.begin(), pol.end() ) == false )
|
|
{
|
|
assert( 0 );
|
|
for( auto pos = v.begin(); pos != v.end(); ++pos )
|
|
{
|
|
deleter( *pos );
|
|
}
|
|
|
|
v.clear();
|
|
v.push_back( polygon );
|
|
return v;
|
|
}
|
|
|
|
v.push_back( pp );
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
typedef Box< T > NodeType;
|
|
|
|
QuadTree_divide_polygon( const T & left, const T & top, const T & width, const T & height )
|
|
: m_masterNode( 1, left, top, width, height )
|
|
{
|
|
}
|
|
|
|
QuadTree_divide_polygon( const T & width, const T & height )
|
|
: m_masterNode( 1, 0, 0, width, height )
|
|
{
|
|
}
|
|
|
|
~QuadTree_divide_polygon()
|
|
{
|
|
}
|
|
|
|
T GetX()
|
|
{
|
|
return m_masterNode.GetX();
|
|
}
|
|
|
|
T GetY()
|
|
{
|
|
return m_masterNode.GetY();
|
|
}
|
|
|
|
T GetWidth() const
|
|
{
|
|
return m_masterNode.GetWidth();
|
|
}
|
|
|
|
T GetHeight() const
|
|
{
|
|
return m_masterNode.GetHeight();
|
|
}
|
|
|
|
size_t getItemCount() const
|
|
{
|
|
return m_masterNode.getItemCount();
|
|
}
|
|
|
|
template< typename Allocator, typename Deleter >
|
|
bool Add( const U & u, Allocator& allocator, Deleter& deleter )
|
|
{
|
|
return m_masterNode.Add( u, allocator, deleter );
|
|
}
|
|
|
|
template< typename C, typename Deleter >
|
|
void Remove( const C & c, Deleter& deleter )
|
|
{
|
|
m_masterNode.Remove( c, deleter );
|
|
}
|
|
|
|
template< typename C >
|
|
void Enum( const C & c, std::vector< U > * pResult )
|
|
{
|
|
_FUNCTOR_ADAPTOR adaptor;
|
|
adaptor.pResult = pResult;
|
|
m_masterNode.Enum( c, adaptor );
|
|
}
|
|
|
|
template< typename C >
|
|
void EnumLoose( const C & c, std::vector< U > * pResult )
|
|
{
|
|
_FUNCTOR_ADAPTOR adaptor;
|
|
adaptor.pResult = pResult;
|
|
m_masterNode.EnumLoose( c, adaptor );
|
|
}
|
|
|
|
template< typename C >
|
|
bool Collision( const C & c ) const
|
|
{
|
|
return m_masterNode.Collision( c );
|
|
}
|
|
|
|
template< typename C >
|
|
bool LooseCollision( const C & c ) const
|
|
{
|
|
return m_masterNode.LooseCollision( c );
|
|
}
|
|
|
|
struct Node
|
|
{
|
|
Node( unsigned short depth, const T & left, const T & top, const T & width, const T & height )
|
|
{
|
|
m_unDepth = depth;
|
|
|
|
m_pNode[0] = NULL;
|
|
m_pNode[1] = NULL;
|
|
m_pNode[2] = NULL;
|
|
m_pNode[3] = NULL;
|
|
|
|
init( depth, left, top, width, height );
|
|
}
|
|
|
|
~Node()
|
|
{
|
|
if( hasChildNode() )
|
|
{
|
|
delete m_pNode[0];
|
|
delete m_pNode[1];
|
|
delete m_pNode[2];
|
|
delete m_pNode[3];
|
|
}
|
|
}
|
|
|
|
T GetX()
|
|
{
|
|
return m_rcEffectiveArea.GetX();
|
|
}
|
|
|
|
T GetY()
|
|
{
|
|
return m_rcEffectiveArea.GetY();
|
|
}
|
|
|
|
T GetWidth() const
|
|
{
|
|
return m_rcEffectiveArea.GetWidth();
|
|
}
|
|
|
|
T GetHeight() const
|
|
{
|
|
return m_rcEffectiveArea.GetHeight();
|
|
}
|
|
|
|
size_t getItemCount() const
|
|
{
|
|
size_t total = 0;
|
|
|
|
if( hasChildNode() )
|
|
{
|
|
total += m_pNode[0]->getItemCount();
|
|
total += m_pNode[1]->getItemCount();
|
|
total += m_pNode[2]->getItemCount();
|
|
total += m_pNode[3]->getItemCount();
|
|
}
|
|
|
|
return total + m_vList.size();
|
|
}
|
|
|
|
bool hasChildNode() const
|
|
{
|
|
return m_pNode[0] != NULL;
|
|
}
|
|
|
|
template< typename Allocator, typename Deleter >
|
|
bool Add( const U & u, Allocator& allocator, Deleter& deleter )
|
|
{
|
|
if( !COLLISION( m_rcEffectiveArea, u ) )
|
|
{
|
|
assert( 0 );
|
|
return false;
|
|
}
|
|
|
|
// 자식 노드가 있다면
|
|
if( hasChildNode() )
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
T half_width = m_rcEffectiveArea.GetWidth() / 2;
|
|
T half_height = m_rcEffectiveArea.GetHeight() / 2;
|
|
|
|
// 분할 선
|
|
X2D::Line<T> width_center( m_rcEffectiveArea.GetLeft() + half_width, m_rcEffectiveArea.GetTop(),
|
|
m_rcEffectiveArea.GetLeft() + half_width, m_rcEffectiveArea.GetBottom() );
|
|
X2D::Line<T> height_center( m_rcEffectiveArea.GetLeft(), m_rcEffectiveArea.GetTop() + half_height,
|
|
m_rcEffectiveArea.GetRight(), m_rcEffectiveArea.GetTop() + half_height );
|
|
|
|
// 입력된 폴리곤 분할
|
|
std::vector< U > r; // 결과
|
|
|
|
// width_center로 분할
|
|
std::vector< U > v = QuadTree_divide_polygon::divide_polygon( u, width_center, allocator, deleter );
|
|
|
|
// 분할된 폴리곤 다시 height_center로 분할
|
|
for( auto pos = v.begin(); pos != v.end(); ++pos )
|
|
{
|
|
std::vector< U > t = QuadTree_divide_polygon::divide_polygon( (*pos), height_center, allocator, deleter );
|
|
r.insert( r.end(), t.begin(), t.end() );
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// 자식 노드에 추가
|
|
bool bdivide = (r.size() > 1);
|
|
for( auto pos = r.begin(); pos != r.end(); )
|
|
{
|
|
const U& polygon = (*pos);
|
|
Node *pNode = getFitNode( polygon );
|
|
if( pNode )
|
|
{
|
|
if( pNode->Add( polygon, allocator, deleter ) == true )
|
|
{
|
|
pos = r.erase( pos );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
assert( 0 );
|
|
++pos;
|
|
}
|
|
|
|
// 모두 자식에게 넣기 실패했다면 자신이 가진다.
|
|
if( !r.empty() )
|
|
{
|
|
m_vList.push_back( u );
|
|
}
|
|
// 분할 되었고 모두 자식에게 넣었다면 u 삭제
|
|
else if( bdivide == true && r.empty() )
|
|
{
|
|
deleter( u );
|
|
}
|
|
|
|
// 추가 실패한 것들 정리
|
|
for( auto pos = r.begin(); pos != r.end(); ++pos )
|
|
{
|
|
const U& polygon = (*pos);
|
|
// 만약 u 추가에 실패했다면 부모가 처리한다.
|
|
if( polygon != u )
|
|
{
|
|
deleter( polygon );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
add( u, allocator, deleter );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template< typename C, typename Deleter >
|
|
void Remove( const C & c, Deleter& deleter )
|
|
{
|
|
if( hasChildNode() )
|
|
{
|
|
// quadtree(0,0,100,100) 일경우 Box(70,70,80,80) 를 add() 하면 m_pNode[3] 에 들어간다.
|
|
// 그런데 이걸 point(70,70) 으로 검사하면 m_pNode[0] 이 fit 이 된다.
|
|
// 그러므로 4 노드 모두 검사 해야 한다.
|
|
m_pNode[0]->Remove( c, deleter );
|
|
m_pNode[1]->Remove( c, deleter );
|
|
m_pNode[2]->Remove( c, deleter );
|
|
m_pNode[3]->Remove( c, deleter );
|
|
}
|
|
|
|
for( std::vector< U >::iterator it = m_vList.begin(); it != m_vList.end(); )
|
|
{
|
|
if( COLLISION( (*it), c ) )
|
|
{
|
|
deleter( (*it) );
|
|
|
|
it = m_vList.erase( it );
|
|
continue;
|
|
}
|
|
++it;
|
|
}
|
|
}
|
|
|
|
bool IsAddable( const U & u ) const
|
|
{
|
|
return CLOSELY_INCLUDE( m_rcEffectiveArea, u );
|
|
}
|
|
|
|
template< typename C >
|
|
bool Collision( const C & c ) const
|
|
{
|
|
if( !COLLISION( m_rcEffectiveArea, c ) )
|
|
return false;
|
|
|
|
for( std::vector< U >::const_iterator it = m_vList.begin(); it != m_vList.end(); ++it )
|
|
{
|
|
if( COLLISION( (*it), c ) )
|
|
return true;
|
|
}
|
|
|
|
if( hasChildNode() )
|
|
{
|
|
// quadtree(0,0,100,100) 일경우 Box(70,70,80,80) 를 add() 하면 m_pNode[3] 에 들어간다.
|
|
// 그런데 이걸 point(70,70) 으로 검사하면 m_pNode[0] 이 fit 이 된다.
|
|
// 그러므로 4 노드 모두 검사 해야 한다.
|
|
if( m_pNode[0]->Collision( c ) )
|
|
return true;
|
|
if( m_pNode[1]->Collision( c ) )
|
|
return true;
|
|
if( m_pNode[2]->Collision( c ) )
|
|
return true;
|
|
if( m_pNode[3]->Collision( c ) )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
template< typename C >
|
|
bool LooseCollision( const C & c ) const
|
|
{
|
|
if( !COLLISION( m_rcEffectiveArea, c ) )
|
|
return false;
|
|
|
|
for( std::vector< U >::const_iterator it = m_vList.begin(); it != m_vList.end(); ++it )
|
|
{
|
|
if( LOOSE_COLLISION( (*it), c ) )
|
|
return true;
|
|
}
|
|
|
|
if( hasChildNode() )
|
|
{
|
|
// quadtree(0,0,100,100) 일경우 Box(70,70,80,80) 를 add() 하면 m_pNode[3] 에 들어간다.
|
|
// 그런데 이걸 point(70,70) 으로 검사하면 m_pNode[0] 이 fit 이 된다.
|
|
// 그러므로 4 노드 모두 검사 해야 한다.
|
|
if( m_pNode[0]->LooseCollision( c ) )
|
|
return true;
|
|
if( m_pNode[1]->LooseCollision( c ) )
|
|
return true;
|
|
if( m_pNode[2]->LooseCollision( c ) )
|
|
return true;
|
|
if( m_pNode[3]->LooseCollision( c ) )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
template< typename C, typename F >
|
|
void Enum( const C & c, F & f ) const
|
|
{
|
|
if( !COLLISION( m_rcEffectiveArea, c ) )
|
|
return;
|
|
|
|
if( hasChildNode() )
|
|
{
|
|
// quadtree(0,0,100,100) 일경우 Box(70,70,80,80) 를 add() 하면 m_pNode[3] 에 들어간다.
|
|
// 그런데 이걸 point(70,70) 으로 검사하면 m_pNode[0] 이 fit 이 된다.
|
|
// 그러므로 4 노드 모두 검사 해야 한다.
|
|
m_pNode[0]->Enum( c, f );
|
|
m_pNode[1]->Enum( c, f );
|
|
m_pNode[2]->Enum( c, f );
|
|
m_pNode[3]->Enum( c, f );
|
|
}
|
|
|
|
for( std::vector< U >::const_iterator it = m_vList.begin(); it != m_vList.end(); ++it )
|
|
{
|
|
if( COLLISION( (*it), c ) )
|
|
f( *it );
|
|
}
|
|
}
|
|
|
|
template< typename C, typename F >
|
|
void EnumLoose( const C & c, F & f )
|
|
{
|
|
if( !COLLISION( m_rcEffectiveArea, c ) )
|
|
return;
|
|
|
|
if( hasChildNode() )
|
|
{
|
|
// quadtree(0,0,100,100) 일경우 Box(70,70,80,80) 를 add() 하면 m_pNode[3] 에 들어간다.
|
|
// 그런데 이걸 point(70,70) 으로 검사하면 m_pNode[0] 이 fit 이 된다.
|
|
// 그러므로 4 노드 모두 검사 해야 한다.
|
|
m_pNode[0]->EnumLoose( c, f );
|
|
m_pNode[1]->EnumLoose( c, f );
|
|
m_pNode[2]->EnumLoose( c, f );
|
|
m_pNode[3]->EnumLoose( c, f );
|
|
}
|
|
|
|
for( std::vector< U >::iterator it = m_vList.begin(); it != m_vList.end(); ++it )
|
|
{
|
|
U& u = (*it);
|
|
if( u->GetBoundingBox().IsCollision( c ) == true )
|
|
f( u );
|
|
}
|
|
}
|
|
|
|
private:
|
|
|
|
Node()
|
|
{
|
|
m_pNode[0] = NULL;
|
|
m_pNode[1] = NULL;
|
|
m_pNode[2] = NULL;
|
|
m_pNode[3] = NULL;
|
|
}
|
|
|
|
Node* getFitNode( const U & u ) const
|
|
{
|
|
if( m_pNode[0]->IsAddable( u ) == true )
|
|
return m_pNode[0];
|
|
if( m_pNode[1]->IsAddable( u ) == true )
|
|
return m_pNode[1];
|
|
if( m_pNode[2]->IsAddable( u ) == true )
|
|
return m_pNode[2];
|
|
if( m_pNode[3]->IsAddable( u ) == true )
|
|
return m_pNode[3];
|
|
|
|
return NULL;
|
|
}
|
|
|
|
template< typename Allocator, typename Deleter >
|
|
void add( const U & u, Allocator& allocator, Deleter& deleter )
|
|
{
|
|
m_vList.push_back( u );
|
|
|
|
if( u->Size() > THRESHOLD && m_unDepth < MAX_DEPTH )
|
|
divide( allocator, deleter );
|
|
}
|
|
|
|
template< typename Allocator, typename Deleter >
|
|
void divide( Allocator& allocator, Deleter& deleter )
|
|
{
|
|
if( hasChildNode() )
|
|
return;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// 자식 분할
|
|
// 더 자를 수 없다.
|
|
if( m_rcEffectiveArea.GetWidth() <= 2 || m_rcEffectiveArea.GetHeight() <= 2 )
|
|
return;
|
|
|
|
m_pNode[0] = new Node;
|
|
m_pNode[1] = new Node;
|
|
m_pNode[2] = new Node;
|
|
m_pNode[3] = new Node;
|
|
|
|
T half_width = m_rcEffectiveArea.GetWidth() / 2;
|
|
T half_height = m_rcEffectiveArea.GetHeight() / 2;
|
|
|
|
m_pNode[0]->init( m_unDepth + 1,
|
|
m_rcEffectiveArea.GetLeft(), m_rcEffectiveArea.GetTop(),
|
|
half_width, half_height );
|
|
m_pNode[1]->init( m_unDepth + 1,
|
|
m_rcEffectiveArea.GetLeft() + half_width, m_rcEffectiveArea.GetTop(),
|
|
m_rcEffectiveArea.GetWidth() - half_width, half_height );
|
|
m_pNode[2]->init( m_unDepth + 1,
|
|
m_rcEffectiveArea.GetLeft(), m_rcEffectiveArea.GetTop() + half_height,
|
|
half_width, m_rcEffectiveArea.GetHeight() - half_height );
|
|
m_pNode[3]->init( m_unDepth + 1,
|
|
m_rcEffectiveArea.GetLeft() + half_width, m_rcEffectiveArea.GetTop() + half_height,
|
|
m_rcEffectiveArea.GetWidth() - half_width, m_rcEffectiveArea.GetHeight() - half_height );
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// 자식에게 분배
|
|
std::vector< U > vTmpList = std::move( m_vList );
|
|
for( auto it = vTmpList.begin(); it != vTmpList.end(); ++it )
|
|
{
|
|
if( Add( *it, allocator, deleter ) == false )
|
|
{
|
|
assert( 0 );
|
|
}
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
void init( unsigned short depth, const T & left, const T & top, const T & width, const T & height )
|
|
{
|
|
m_rcEffectiveArea.Set( left, top, width, height );
|
|
|
|
m_unDepth = depth;
|
|
}
|
|
|
|
std::vector< U > m_vList;
|
|
Rect< T > m_rcEffectiveArea;
|
|
struct Node* m_pNode[4];
|
|
unsigned short m_unDepth;
|
|
};
|
|
|
|
struct _FUNCTOR_ADAPTOR
|
|
{
|
|
std::vector< U > * pResult;
|
|
void operator()( const U & item )
|
|
{
|
|
pResult->push_back( item );
|
|
}
|
|
};
|
|
|
|
struct Node m_masterNode;
|
|
};
|
|
|
|
}; // namespace X2D
|