/*
** Copyright (C) 1996, 1997 Microsoft Corporation. All Rights Reserved.
**
** File: KDroot.cpp
**
** Author:
**
** Description:
**
** History:
*/
#include "pch.h"
KDroot::KDroot(bool bStatic)
:
KDnode(NULL),
m_nAdded(0),
m_nDeleted(0),
m_bStatic(bStatic)
{
//allocate enough space for everything we'll ever want
allocHitTestsEndpoints((c_maxHitTests * 3) >> 1);
}
KDroot::~KDroot(void)
{
//everything should be marked dead before the root destructor is called.
flush();
assert (m_nHitTests == 0);
}
void KDroot::addHitTest(HitTest* pHitTest)
{
assert (m_nHitTests <= m_maxHitTests);
//Not allowed to add a node that was marked dead but not yet pruned.
//Danger: we are performing an operation on another KDRoot which,
//theoretically, could have been freed. The only reason it works is
//that -- if it were freed -- then it would have been flushed and all
//of the hittests that refered back to the it would have had their
//KDRoot's cleared.
if (pHitTest->GetKDRoot() != NULL)
pHitTest->GetKDRoot()->flush();
assert (pHitTest->GetKDRoot() == NULL);
pHitTest->SetKDRoot(this);
pHitTest->SetDeadF(false);
pHitTest->SetDeletedF(false);
if (m_nHitTests == m_maxHitTests)
{
//Need more space but, unlike a KDnode, we can't simply nuke the
//object/endpoint arrays: we need to create, copy and then nuke.
assert (m_ppHitTests);
//Double the existing space
int n = m_maxHitTests << 1;
void** v = new void* [n * (1 + 2 * c_nAxes)];
assert (v);
m_maxHitTests = n;
for (int i = 0; (i < c_nAxes); i++)
{
Endpoint** pOldEndpoints = m_ppEndpoints[i];
m_ppEndpoints[i] = (i == 0)
? ((Endpoint**)(v + n))
: m_ppEndpoints[i - 1] + (m_maxHitTests << 1);
for (int j = 0; (j < (m_nHitTests << 1)); j++)
m_ppEndpoints[i][j] = pOldEndpoints[j];
}
{
HitTest** ppOldHitTests = m_ppHitTests;
m_ppHitTests = (HitTest**)v;
for (int i = 0; (i < m_nHitTests); i++)
m_ppHitTests[i] = ppOldHitTests[i];
delete ppOldHitTests;
}
}
assert (m_nHitTests < m_maxHitTests);
if (m_bStatic)
pHitTest->UpdateStaticBB();
//Add a reference to the hitTest's data object
pHitTest->AddRef();
//Add the object to the list of
m_ppHitTests[m_nHitTests] = pHitTest;
//create and sort the endpoint lists for each axis.
{
int ht2 = m_nHitTests << 1;
for (int axis = 0; (axis < c_nAxes); axis++)
{
m_ppEndpoints[axis][ht2 ] = &pHitTest->m_endpoints[axis][0];
m_ppEndpoints[axis][ht2 + 1] = &pHitTest->m_endpoints[axis][1];
}
}
m_nHitTests++;
//Increment the changed count (which determines which sort is used in the update).
m_nAdded++;
}
void KDroot::deleteHitTest(HitTest* pHitTest)
{
//Actually defer the delete until the next update
m_nDeleted++;
pHitTest->SetDeadF(true);
pHitTest->SetDeletedF(true);
//If we are deleting a hit test ... it must preserve the pointer to the old hit test
assert (pHitTest->GetKDRoot() == this);
}
void KDroot::flush(void)
{
if (m_nDeleted > 0)
{
//One or more objects were deleted ... scan through the object and endpoint arrays, nuking the dead objects.
//Endpoint arrays first
for (int axis = 0; (axis < c_nAxes); axis++)
{
int dest = 0;
for (int source = 0; (source < (m_nHitTests << 1)); source++)
{
if (!m_ppEndpoints[axis][source]->pHitTest->m_deletedF)
{
m_ppEndpoints[axis][dest++] = m_ppEndpoints[axis][source];
}
}
}
{
//Do the same for the objects ... but also release the object as well
int dest = 0;
for (int source = 0; (source < m_nHitTests); source++)
{
HitTest* pht = m_ppHitTests[source];
if (pht->m_deletedF)
{
pht->SetKDRoot(NULL); //We are not allowed to add this hit test to another KD tree until after this point
//so mark the node as not being dead (and acceptable to add) here.
pht->Release();
}
else
{
m_ppHitTests[dest++] = pht;
}
}
m_nHitTests = dest;
}
m_nDeleted = 0;
}
}
void KDroot::update(void)
{
//Only works from the root
assert (!m_parent);
bool bSort = (!m_bStatic) || (m_nAdded > 0);
if (bSort || ((m_nDeleted << 4) > m_nHitTests))
{
flush();
if (m_nHitTests >= c_minRootSize)
{
if (bSort)
{
//Resort the list of endpoints.
//Use the short or long sort depending on how things have changed
for (int axis = 0; (axis < c_nAxes); axis++)
{
if ((m_nAdded << 2) > m_nHitTests)
{
//Lots of things have changed ... use the long sort.
Endpoint::longSort(&m_ppEndpoints[axis][0],
&m_ppEndpoints[axis][(m_nHitTests << 1) - 1]);
}
else
{
//Only minor changes ... use the short sort.
Endpoint::shortSort(&m_ppEndpoints[axis][0],
&m_ppEndpoints[axis][(m_nHitTests << 1) - 1]);
}
}
m_nAdded = 0;
}
pivot();
}
else
m_pivotAxis = -1;
}
}