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