packages feed

Box2D-0.1.0.0: upstream/src/dynamic_tree.h

// SPDX-FileCopyrightText: 2026 Erin Catto
// SPDX-License-Identifier: MIT

#pragma once

#include "box2d/collision.h"
#include "box2d/math_functions.h"

#define B2_TREE_STACK_SIZE 512

// Used to mark a node as being moved such that pairs need to be generated and the tree
// may need to be rebuilt. This mark comes from a few sources:
// - updating the transform and AABB in the solver from movement
// - a joint disabling collision
// - creating a proxy
// - setting the transform on a body
#define B2_MOVED_NODE ( 1u << 30 )

#define B2_LEAF_NODE ( 1u << 31 )
#define B2_NODE_INDEX_MASK ( 0xFFFFFFFFu & ~( B2_MOVED_NODE | B2_LEAF_NODE ) )

// Used to indicate empty nodes. It also passes as a leaf so that internal node
// processing will naturally skip it.
#define B2_EMPTY_NODE ( B2_NODE_INDEX_MASK | B2_LEAF_NODE )

#define B2_ROOT_NODE 0

/// A node in the dynamic tree. Siblings sit together at an even index so two nodes fit in a 64 byte
/// cache line. The root is at index zero and index one is always empty.
typedef struct b2TreeNode
{
	/// The node bounding box
	b2AABB aabb;

	/// In 3D this space is used by the AABB z components.
	uint64_t padding;

	/// - bit 31 : 1 for leaf node
	/// - bit 30 : 1 for moved flag
	/// - bits 0-29 : index of the sibling pair node or the proxy id for a leaf
	uint32_t flagIndex;

	union
	{
		/// The height of an internal node. A leaf has zero height.
		int32_t height;

		/// The shape index for a leaf. Truncated from proxy user data.
		int32_t shapeIndex;
	};

} b2TreeNode;

/// Separate storage for tree leaves.
typedef struct b2TreeProxy
{
	/// User data is an index instead of void* because it is used internally as a shape index.
	uint64_t userData;

	/// Category bits for collision filtering.
	uint64_t categoryBits;

	/// The leaf node. B2_NULL_INDEX for a free proxy.
	int32_t node;

	/// Next free proxy.
	int32_t next;

} b2TreeProxy;

struct b2DynamicTree
{
	/// Array of nodes. The root is at index zero and index 1 is empty. Otherwise siblings are
	/// paired at even indices. Has holes for free node pairs.
	b2TreeNode* nodes;

	/// Parent index per node. The free list is interwoven.
	int32_t* parents;

	/// Proxy data split from node array as cold data.
	b2TreeProxy* proxies;

	/// Every allocated node has a lower index than this.
	int32_t nodeEnd;

	/// The allocated node space
	int32_t nodeCapacity;

	/// Free pairs below nodeEnd
	int32_t pairFreeList;

	/// Number of proxies created
	int32_t proxyCount;

	/// The allocated proxy space
	int32_t proxyCapacity;

	/// Proxy free list
	int32_t proxyFreeList;

	/// Array of nodes for rebuild.
	b2TreeNode* swapNodes;

	/// Leaf indices for rebuild
	int32_t* leafIndices;

	/// Leaves for the rebuild. May represent a proxy or a retained subtree.
	b2TreeNode* leafNodes;

	/// Leaf bounding boxes for rebuild
	b2AABB* leafBoxes;

	/// Leaf bounding box centers for rebuild
	b2Vec2* leafCenters;

	/// Bins for sorting during rebuild
	int32_t* binIndices;

	/// Allocated space for rebuilding
	int32_t rebuildCapacity;

	/// Rebuild orders the nodes so the children follow parents. Cache friendly for queries and
	/// refitting. The order can be disrupted by proxy creation.
	bool dfsOrdered;

};

B2_FORCE_INLINE bool b2IsLeaf( const b2TreeNode* node )
{
	return ( node->flagIndex & B2_LEAF_NODE ) == B2_LEAF_NODE;
}

B2_FORCE_INLINE bool b2IsNodeMoved( const b2TreeNode* node )
{
	return ( node->flagIndex & B2_MOVED_NODE ) == B2_MOVED_NODE;
}

B2_FORCE_INLINE bool b2IsEmptyNode( const b2TreeNode* node )
{
	return node->flagIndex == B2_EMPTY_NODE;
}

B2_FORCE_INLINE int b2GetLeftChild( const b2TreeNode* node )
{
	return (int)( node->flagIndex & B2_NODE_INDEX_MASK );
}

B2_FORCE_INLINE int b2GetProxyId( const b2TreeNode* node )
{
	return (int)( node->flagIndex & B2_NODE_INDEX_MASK );
}

B2_FORCE_INLINE int b2GetRootPair( const b2TreeNode* nodes )
{
	const b2TreeNode* root = nodes + B2_ROOT_NODE;
	return b2IsLeaf( root ) ? B2_ROOT_NODE : b2GetLeftChild( root );
}

B2_FORCE_INLINE b2TreeNode b2MakeEmptyNode( void )
{
	return (b2TreeNode){
		.aabb =
			{
				.lowerBound = { .x = INFINITY, .y = INFINITY },
				.upperBound = { .x = -INFINITY, .y = -INFINITY },
			},
		.flagIndex = B2_EMPTY_NODE,
		.height = 0,
	};
}

static inline bool b2HasTreeMoved( const b2DynamicTree* tree )
{
	return b2IsNodeMoved( tree->nodes + B2_ROOT_NODE );
}

static inline bool b2NeedsRebuild( const b2DynamicTree* tree )
{
	return b2IsNodeMoved( tree->nodes + B2_ROOT_NODE ) || tree->dfsOrdered == false;
}

void b2CreateDynamicTreeInternal( b2DynamicTree* tree, int proxyCapacity );
void b2DestroyDynamicTreeInternal( b2DynamicTree* tree );

int b2CreateTreeProxyInternal( b2DynamicTree* tree, b2AABB aabb, uint64_t categoryBits, uint64_t userData,
									   bool markMoved );
void b2DynamicTree_MoveProxyInternal( b2DynamicTree* tree, int proxyId, b2AABB aabb, bool markMoved );
void b2DynamicTree_EnlargeProxy( b2DynamicTree* tree, int proxyId, b2AABB aabb );

/// Rebuild the tree while retaining subtrees that haven't changed. Returns the number of boxes sorted.
int b2DynamicTree_Rebuild( b2DynamicTree* tree, bool fullBuild );

void b2DynamicTree_MarkProxyMovedSerial( b2DynamicTree* tree, int proxyId );
void b2DynamicTree_MarkProxyMoved( b2DynamicTree* tree, int proxyId, b2AABB aabb );
void b2DynamicTree_ClearMoved( b2DynamicTree* tree );
int b2DynamicTree_GatherMovedProxies( const b2DynamicTree* tree, int* proxyIds );
void b2DynamicTree_Refit( b2DynamicTree* tree );

void b2DynamicTree_ValidateNoMoved( const b2DynamicTree* tree );