#include "constraint_graph.h"
#include "array.h"
#include "bitset.h"
#include "body.h"
#include "contact.h"
#include "joint.h"
#include "solver_set.h"
#include "world.h"
#include <string.h>
#define B2_FORCE_OVERFLOW 0
_Static_assert( b2_graphColorCount == 12, "graph color count assumed to be 12" );
void b2CreateGraph( b2ConstraintGraph* graph, int bodyCapacity )
{
_Static_assert( b2_graphColorCount >= 2, "must have at least two constraint graph colors" );
_Static_assert( b2_overflowIndex == b2_graphColorCount - 1, "bad over flow index" );
*graph = ( b2ConstraintGraph ){ 0 };
bodyCapacity = b2MaxInt( bodyCapacity, 8 );
for ( int i = 0; i < b2_overflowIndex; ++i )
{
b2GraphColor* color = graph->colors + i;
color->bodySet = b2CreateBitSet( bodyCapacity );
b2SetBitCountAndClear( &color->bodySet, bodyCapacity );
}
}
void b2DestroyGraph( b2ConstraintGraph* graph )
{
for ( int i = 0; i < b2_graphColorCount; ++i )
{
b2GraphColor* color = graph->colors + i;
B2_ASSERT( i != b2_overflowIndex || color->bodySet.bits == NULL );
b2DestroyBitSet( &color->bodySet );
b2ContactSimArray_Destroy( &color->contactSims );
b2JointSimArray_Destroy( &color->jointSims );
}
}
void b2AddContactToGraph( b2World* world, b2ContactSim* contactSim, b2Contact* contact )
{
B2_ASSERT( contactSim->manifold.pointCount > 0 );
B2_ASSERT( contactSim->simFlags & b2_simTouchingFlag );
B2_ASSERT( contact->flags & b2_contactTouchingFlag );
b2ConstraintGraph* graph = &world->constraintGraph;
int colorIndex = b2_overflowIndex;
int bodyIdA = contact->edges[0].bodyId;
int bodyIdB = contact->edges[1].bodyId;
b2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA );
b2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB );
bool staticA = bodyA->setIndex == b2_staticSet;
bool staticB = bodyB->setIndex == b2_staticSet;
B2_ASSERT( staticA == false || staticB == false );
#if B2_FORCE_OVERFLOW == 0
if ( staticA == false && staticB == false )
{
for ( int i = 0; i < b2_overflowIndex; ++i )
{
b2GraphColor* color = graph->colors + i;
if ( b2GetBit( &color->bodySet, bodyIdA ) || b2GetBit( &color->bodySet, bodyIdB ) )
{
continue;
}
b2SetBitGrow( &color->bodySet, bodyIdA );
b2SetBitGrow( &color->bodySet, bodyIdB );
colorIndex = i;
break;
}
}
else if ( staticA == false )
{
for ( int i = 1; i < b2_overflowIndex; ++i )
{
b2GraphColor* color = graph->colors + i;
if ( b2GetBit( &color->bodySet, bodyIdA ) )
{
continue;
}
b2SetBitGrow( &color->bodySet, bodyIdA );
colorIndex = i;
break;
}
}
else if ( staticB == false )
{
for ( int i = 1; i < b2_overflowIndex; ++i )
{
b2GraphColor* color = graph->colors + i;
if ( b2GetBit( &color->bodySet, bodyIdB ) )
{
continue;
}
b2SetBitGrow( &color->bodySet, bodyIdB );
colorIndex = i;
break;
}
}
#endif
b2GraphColor* color = graph->colors + colorIndex;
contact->colorIndex = colorIndex;
contact->localIndex = color->contactSims.count;
b2ContactSim* newContact = b2ContactSimArray_Add( &color->contactSims );
memcpy( newContact, contactSim, sizeof( b2ContactSim ) );
if ( staticA )
{
newContact->bodySimIndexA = B2_NULL_INDEX;
newContact->invMassA = 0.0f;
newContact->invIA = 0.0f;
}
else
{
B2_ASSERT( bodyA->setIndex == b2_awakeSet );
b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
int localIndex = bodyA->localIndex;
newContact->bodySimIndexA = localIndex;
b2BodySim* bodySimA = b2BodySimArray_Get( &awakeSet->bodySims, localIndex );
newContact->invMassA = bodySimA->invMass;
newContact->invIA = bodySimA->invInertia;
}
if ( staticB )
{
newContact->bodySimIndexB = B2_NULL_INDEX;
newContact->invMassB = 0.0f;
newContact->invIB = 0.0f;
}
else
{
B2_ASSERT( bodyB->setIndex == b2_awakeSet );
b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
int localIndex = bodyB->localIndex;
newContact->bodySimIndexB = localIndex;
b2BodySim* bodySimB = b2BodySimArray_Get( &awakeSet->bodySims, localIndex );
newContact->invMassB = bodySimB->invMass;
newContact->invIB = bodySimB->invInertia;
}
}
void b2RemoveContactFromGraph( b2World* world, int bodyIdA, int bodyIdB, int colorIndex, int localIndex )
{
b2ConstraintGraph* graph = &world->constraintGraph;
B2_ASSERT( 0 <= colorIndex && colorIndex < b2_graphColorCount );
b2GraphColor* color = graph->colors + colorIndex;
if ( colorIndex != b2_overflowIndex )
{
b2ClearBit( &color->bodySet, bodyIdA );
b2ClearBit( &color->bodySet, bodyIdB );
}
int movedIndex = b2ContactSimArray_RemoveSwap( &color->contactSims, localIndex );
if ( movedIndex != B2_NULL_INDEX )
{
b2ContactSim* movedContactSim = color->contactSims.data + localIndex;
int movedId = movedContactSim->contactId;
b2Contact* movedContact = b2ContactArray_Get( &world->contacts, movedId );
B2_ASSERT( movedContact->setIndex == b2_awakeSet );
B2_ASSERT( movedContact->colorIndex == colorIndex );
B2_ASSERT( movedContact->localIndex == movedIndex );
movedContact->localIndex = localIndex;
}
}
static int b2AssignJointColor( b2ConstraintGraph* graph, int bodyIdA, int bodyIdB, bool staticA, bool staticB )
{
B2_ASSERT( staticA == false || staticB == false );
#if B2_FORCE_OVERFLOW == 0
if ( staticA == false && staticB == false )
{
for ( int i = 0; i < b2_overflowIndex; ++i )
{
b2GraphColor* color = graph->colors + i;
if ( b2GetBit( &color->bodySet, bodyIdA ) || b2GetBit( &color->bodySet, bodyIdB ) )
{
continue;
}
b2SetBitGrow( &color->bodySet, bodyIdA );
b2SetBitGrow( &color->bodySet, bodyIdB );
return i;
}
}
else if ( staticA == false )
{
for ( int i = 0; i < b2_overflowIndex; ++i )
{
b2GraphColor* color = graph->colors + i;
if ( b2GetBit( &color->bodySet, bodyIdA ) )
{
continue;
}
b2SetBitGrow( &color->bodySet, bodyIdA );
return i;
}
}
else if ( staticB == false )
{
for ( int i = 0; i < b2_overflowIndex; ++i )
{
b2GraphColor* color = graph->colors + i;
if ( b2GetBit( &color->bodySet, bodyIdB ) )
{
continue;
}
b2SetBitGrow( &color->bodySet, bodyIdB );
return i;
}
}
#endif
return b2_overflowIndex;
}
b2JointSim* b2CreateJointInGraph( b2World* world, b2Joint* joint )
{
b2ConstraintGraph* graph = &world->constraintGraph;
int bodyIdA = joint->edges[0].bodyId;
int bodyIdB = joint->edges[1].bodyId;
b2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA );
b2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB );
bool staticA = bodyA->setIndex == b2_staticSet;
bool staticB = bodyB->setIndex == b2_staticSet;
int colorIndex = b2AssignJointColor( graph, bodyIdA, bodyIdB, staticA, staticB );
b2JointSim* jointSim = b2JointSimArray_Add( &graph->colors[colorIndex].jointSims );
joint->colorIndex = colorIndex;
joint->localIndex = graph->colors[colorIndex].jointSims.count - 1;
return jointSim;
}
void b2AddJointToGraph( b2World* world, b2JointSim* jointSim, b2Joint* joint )
{
b2JointSim* jointDst = b2CreateJointInGraph( world, joint );
memcpy( jointDst, jointSim, sizeof( b2JointSim ) );
}
void b2RemoveJointFromGraph( b2World* world, int bodyIdA, int bodyIdB, int colorIndex, int localIndex )
{
b2ConstraintGraph* graph = &world->constraintGraph;
B2_ASSERT( 0 <= colorIndex && colorIndex < b2_graphColorCount );
b2GraphColor* color = graph->colors + colorIndex;
if ( colorIndex != b2_overflowIndex )
{
b2ClearBit( &color->bodySet, bodyIdA );
b2ClearBit( &color->bodySet, bodyIdB );
}
int movedIndex = b2JointSimArray_RemoveSwap( &color->jointSims, localIndex );
if ( movedIndex != B2_NULL_INDEX )
{
b2JointSim* movedJointSim = color->jointSims.data + localIndex;
int movedId = movedJointSim->jointId;
b2Joint* movedJoint = b2JointArray_Get( &world->joints, movedId );
B2_ASSERT( movedJoint->setIndex == b2_awakeSet );
B2_ASSERT( movedJoint->colorIndex == colorIndex );
B2_ASSERT( movedJoint->localIndex == movedIndex );
movedJoint->localIndex = localIndex;
}
}