# UNDF: UNDF-2026-000000015 --- a/src/broad_phase.h +++ b/src/broad_phase.h @@ -30,8 +30,9 @@ typedef struct b2BroadPhase // The move set and array are used to track shapes that have moved significantly // and need a pair query for new contacts. The array has a deterministic order. - // todo perhaps just a move set? - // todo implement a 32bit hash set for faster lookup - // todo moveSet can grow quite large on the first time step and remain large b2HashSet moveSet; b2IntArray moveArray; + // Maps proxyKey+1 → index into moveArray for O(1) removal. + // Updated on every push and on every RemoveSwap (displaced element index fix-up). + b2HashTable moveIndex; // key: proxyKey+1, value: array index (stored in value field) --- a/src/broad_phase.c +++ b/src/broad_phase.c @@ -35,6 +35,7 @@ void b2CreateBroadPhase( b2BroadPhase* bp ) bp->moveSet = b2CreateSet( 16 ); bp->moveArray = b2IntArray_Create( 16 ); + bp->moveIndex = b2CreateTable( 16 ); bp->pairSet = b2CreateSet( 16 ); } @@ -55,6 +56,7 @@ void b2DestroyBroadPhase( b2BroadPhase* bp ) b2DestroySet( &bp->moveSet ); b2IntArray_Destroy( &bp->moveArray ); + b2DestroyTable( &bp->moveIndex ); b2DestroySet( &bp->pairSet ); @@ -63,25 +64,29 @@ void b2DestroyBroadPhase( b2BroadPhase* bp ) // in b2BufferMove — add both to moveSet (O(1)) and moveArray, recording the // new array index into moveIndex (O(1)). static inline void b2BufferMove( b2BroadPhase* bp, int queryProxy ) { // Adding 1 because 0 is the sentinel bool alreadyAdded = b2AddKey( &bp->moveSet, queryProxy + 1 ); if ( alreadyAdded == false ) { + int idx = bp->moveArray.count; b2IntArray_Push( &bp->moveArray, queryProxy ); + b2TableSet( &bp->moveIndex, (uint32_t)( queryProxy + 1 ), (uint32_t)idx ); } } static inline void b2UnBufferMove( b2BroadPhase* bp, int proxyKey ) { bool found = b2RemoveKey( &bp->moveSet, proxyKey + 1 ); if ( found ) { - // Purge from move buffer. Linear search. - // todo if I can iterate the move set then I don't need the moveArray - int count = bp->moveArray.count; - for ( int i = 0; i < count; ++i ) - { - if ( bp->moveArray.data[i] == proxyKey ) - { - b2IntArray_RemoveSwap( &bp->moveArray, i ); - break; - } - } + // O(1) index lookup, then RemoveSwap with displaced-element fix-up. + uint32_t idx = b2TableGet( &bp->moveIndex, (uint32_t)( proxyKey + 1 ) ); + b2TableRemove( &bp->moveIndex, (uint32_t)( proxyKey + 1 ) ); + int last = bp->moveArray.count - 1; + if ( (int)idx != last ) + { + // RemoveSwap moves the last element to position idx. + int displaced = bp->moveArray.data[last]; + bp->moveArray.data[idx] = displaced; + bp->moveArray.count = last; + // Update the displaced element's index in the map. + b2TableSet( &bp->moveIndex, (uint32_t)( displaced + 1 ), idx ); + } + else + { + bp->moveArray.count = last; + } } }