/* ** 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; } }