diff --git a/src/Box2D.NET/B2BitSets.cs b/src/Box2D.NET/B2BitSets.cs index 3446420..b722a32 100644 --- a/src/Box2D.NET/B2BitSets.cs +++ b/src/Box2D.NET/B2BitSets.cs @@ -42,15 +42,20 @@ public static void b2SetBitGrow(ref B2BitSet bitSet, int bitIndex) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void b2ClearBit(ref B2BitSet bitSet, uint bitIndex) + public static void b2ClearBit(ref B2BitSet bitSet, int bitIndex) { - uint blockIndex = bitIndex / 64; + if (bitIndex < 0) + { + return; + } + + int blockIndex = bitIndex / 64; if (blockIndex >= bitSet.blockCount) { return; } - bitSet.bits[blockIndex] &= ~((ulong)1 << (int)(bitIndex % 64)); + bitSet.bits[blockIndex] &= ~((ulong)1 << (bitIndex % 64)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -137,9 +142,13 @@ public static void b2GrowBitSet(ref B2BitSet bitSet, int blockCount) ulong[] newBits = b2Alloc(bitSet.blockCapacity); //memset( newBits, 0, bitSet->blockCapacity * sizeof( ulong ) ); Array.Fill(newBits, 0UL, 0, bitSet.blockCapacity); - B2_ASSERT(bitSet.bits != null); //memcpy( newBits, bitSet->bits, oldCapacity * sizeof( ulong ) ); - Array.Copy(bitSet.bits, newBits, oldCapacity); + // b2Alloc returns null for a zero capacity, and C's memcpy from NULL with a zero + // length is harmless, so guard rather than fault on the first grow. + if (oldCapacity > 0) + { + Array.Copy(bitSet.bits, newBits, oldCapacity); + } b2Free(bitSet.bits, oldCapacity); bitSet.bits = newBits; } diff --git a/src/Box2D.NET/B2ConstraintGraphs.cs b/src/Box2D.NET/B2ConstraintGraphs.cs index 457a839..23c7c01 100644 --- a/src/Box2D.NET/B2ConstraintGraphs.cs +++ b/src/Box2D.NET/B2ConstraintGraphs.cs @@ -207,8 +207,8 @@ internal static void b2RemoveContactFromGraph(B2World world, int bodyIdA, int bo if (colorIndex != B2_OVERFLOW_INDEX) { // This might clear a bit for a kinematic or static body, but this has no effect - b2ClearBit(ref color.bodySet, (uint)bodyIdA); - b2ClearBit(ref color.bodySet, (uint)bodyIdB); + b2ClearBit(ref color.bodySet, bodyIdA); + b2ClearBit(ref color.bodySet, bodyIdB); } int movedIndex = b2Array_RemoveSwap(ref color.contactSims, localIndex); @@ -324,8 +324,8 @@ internal static void b2RemoveJointFromGraph(B2World world, int bodyIdA, int body if (colorIndex != B2_OVERFLOW_INDEX) { // May clear static bodies, no effect - b2ClearBit(ref color.bodySet, (uint)bodyIdA); - b2ClearBit(ref color.bodySet, (uint)bodyIdB); + b2ClearBit(ref color.bodySet, bodyIdA); + b2ClearBit(ref color.bodySet, bodyIdB); } int movedIndex = b2Array_RemoveSwap(ref color.jointSims, localIndex); diff --git a/src/Box2D.NET/B2Diagnostics.cs b/src/Box2D.NET/B2Diagnostics.cs index 8d2805e..98792a3 100644 --- a/src/Box2D.NET/B2Diagnostics.cs +++ b/src/Box2D.NET/B2Diagnostics.cs @@ -97,7 +97,7 @@ public static void b2SetAssertFcn(b2AssertFcn assertFcn) b2AssertHandler = assertFcn; } - internal static int b2InternalAssert(string condition, string fileName, int lineNumber) + public static int b2InternalAssert(string condition, string fileName, int lineNumber) { return b2AssertHandler(condition, fileName, lineNumber); } diff --git a/src/Box2D.NET/B2SolverSets.cs b/src/Box2D.NET/B2SolverSets.cs index 1501304..016d9df 100644 --- a/src/Box2D.NET/B2SolverSets.cs +++ b/src/Box2D.NET/B2SolverSets.cs @@ -330,8 +330,8 @@ internal static void b2TrySleepIsland(B2World world, int islandId) if (colorIndex != B2_OVERFLOW_INDEX) { // might clear a bit for a static body, but this has no effect - b2ClearBit(ref color.bodySet, (uint)contact.edges[0].bodyId); - b2ClearBit(ref color.bodySet, (uint)contact.edges[1].bodyId); + b2ClearBit(ref color.bodySet, contact.edges[0].bodyId); + b2ClearBit(ref color.bodySet, contact.edges[1].bodyId); } int localIndex = contact.localIndex; @@ -380,8 +380,8 @@ internal static void b2TrySleepIsland(B2World world, int islandId) if (colorIndex != B2_OVERFLOW_INDEX) { // might clear a bit for a static body, but this has no effect - b2ClearBit(ref color.bodySet, (uint)joint.edges[0].bodyId); - b2ClearBit(ref color.bodySet, (uint)joint.edges[1].bodyId); + b2ClearBit(ref color.bodySet, joint.edges[0].bodyId); + b2ClearBit(ref color.bodySet, joint.edges[1].bodyId); } int sleepJointIndex = sleepSet.jointSims.count; diff --git a/src/Box2D.NET/B2Vec2.cs b/src/Box2D.NET/B2Vec2.cs index d7a9f36..7eda33a 100644 --- a/src/Box2D.NET/B2Vec2.cs +++ b/src/Box2D.NET/B2Vec2.cs @@ -78,9 +78,11 @@ public B2Vec2(float x, float y) return !(a == b); } + // Not "this == other". The operator follows C and uses IEEE compare, where NaN != NaN. + // Equals must stay reflexive and agree with GetHashCode, so it compares bitwise. public bool Equals(B2Vec2 other) { - return this == other; + return X.Equals(other.X) && Y.Equals(other.Y); } public override bool Equals(object obj) diff --git a/src/Box2D.NET/B2Worlds.cs b/src/Box2D.NET/B2Worlds.cs index e7bd31e..b392e44 100644 --- a/src/Box2D.NET/B2Worlds.cs +++ b/src/Box2D.NET/B2Worlds.cs @@ -1152,7 +1152,7 @@ internal static bool DrawQueryCallback(int proxyId, ulong userData, ref B2DrawCo // todo this has varying order for moving shapes, causing flicker when overlapping shapes are moving // solution: display order by shape id modulus 3, keep 3 buckets in GLSolid* and flush in 3 passes. /// Call this to draw shapes and other debug draw data - public static void b2World_Draw(B2WorldId worldId, B2DebugDraw draw) + public static void b2World_Draw(B2WorldId worldId, in B2DebugDraw draw) { B2World world = b2GetWorldFromId(worldId); B2_ASSERT(world.locked == false);