Skip to content

Commit a567f83

Browse files
committed
fix: align managed behavior with upstream Box2D
Expose the length-unit and shape-cast APIs, broaden dynamic-tree callback contexts, and match upstream value semantics. Preserve UTF-8 body-name limits, improve assertion and bit-set edge handling, and support profiling hooks for managed objects. Synchronize benchmark defaults and sample behavior, including motor-joint torque updates and bounded sensor overlap output.
1 parent 9e6533c commit a567f83

16 files changed

Lines changed: 185 additions & 84 deletions

src/Box2D.NET.Samples/Samples/Benchmarks/BenchmarkBarrel.cs

Lines changed: 22 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ public enum ShapeType
3131
e_humanShape,
3232
}
3333

34-
private const int e_maxColumns = 30;
35-
private const int e_maxRows = 300;
34+
private const int e_maxColumns = 26;
35+
private const int e_maxRows = 150;
3636

3737

3838
private B2BodyId[] m_bodies = new B2BodyId[e_maxRows * e_maxColumns];
@@ -110,9 +110,6 @@ public BenchmarkBarrel(SampleContext context) : base(context)
110110

111111
m_shapeType = ShapeType.e_compoundShape;
112112

113-
m_columnCount = e_maxColumns / (m_isDebug ? 3 : 2);
114-
m_rowCount = e_maxRows / (m_isDebug ? 3 : 2);
115-
116113
CreateScene();
117114
}
118115

@@ -135,25 +132,24 @@ void CreateScene()
135132
}
136133

137134

138-
// if (m_shapeType == ShapeType.e_compoundShape)
139-
// {
140-
// if (m_context.sampleDebug == false)
141-
// {
142-
// m_columnCount = e_maxColumns;
143-
// }
144-
// }
145-
// else if (m_shapeType == ShapeType.e_humanShape)
146-
// {
147-
// if (m_context.sampleDebug)
148-
// {
149-
// m_rowCount = 5;
150-
// m_columnCount = 10;
151-
// }
152-
// else
153-
// {
154-
// m_rowCount = 30;
155-
// }
156-
// }
135+
m_columnCount = m_isDebug ? 10 : e_maxColumns;
136+
m_rowCount = m_isDebug ? 40 : e_maxRows;
137+
138+
if (m_shapeType == ShapeType.e_compoundShape)
139+
{
140+
#if !DEBUG
141+
m_columnCount = 20;
142+
#endif
143+
}
144+
else if (m_shapeType == ShapeType.e_humanShape)
145+
{
146+
#if DEBUG
147+
m_rowCount = 5;
148+
m_columnCount = 10;
149+
#else
150+
m_rowCount = 30;
151+
#endif
152+
}
157153

158154
float rad = 0.5f;
159155

@@ -311,23 +307,13 @@ public override void UpdateGui()
311307
base.UpdateGui();
312308

313309
float fontSize = ImGui.GetFontSize();
314-
float height = 10.0f * fontSize;
310+
float height = 6.0f * fontSize;
315311
ImGui.SetNextWindowPos(new Vector2(0.5f * fontSize, m_camera.height - height - 2.0f * fontSize), ImGuiCond.Once);
316312
ImGui.SetNextWindowSize(new Vector2(15.0f * fontSize, height));
317313

318314
ImGui.Begin("Benchmark: Barrel", ImGuiWindowFlags.NoResize);
319315

320316
bool changed = false;
321-
if (ImGui.SliderInt("rows", ref m_rowCount, 1, e_maxRows, "%d"))
322-
{
323-
changed = true;
324-
}
325-
326-
if (ImGui.SliderInt("columns", ref m_columnCount, 1, e_maxColumns, "%d"))
327-
{
328-
changed = true;
329-
}
330-
331317
string[] shapeTypes = ["Circle", "Capsule", "Mix", "Compound", "Human"];
332318
int shapeType = (int)m_shapeType;
333319
if (ImGui.Combo("Shape", ref shapeType, shapeTypes, shapeTypes.Length))
@@ -349,4 +335,4 @@ public override void UpdateGui()
349335

350336
ImGui.End();
351337
}
352-
}
338+
}

src/Box2D.NET.Samples/Samples/Events/SensorTypes.cs

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,9 @@ public SensorTypes(SampleContext context) : base(context)
141141

142142
void PrintOverlaps(B2ShapeId sensorShapeId, string prefix)
143143
{
144+
const int bufferSize = 256;
145+
const int payloadCapacity = bufferSize - 1;
146+
144147
// Determine the necessary capacity
145148
int capacity = b2Shape_GetSensorCapacity(sensorShapeId);
146149
m_visitorIds.Resize(capacity);
@@ -149,8 +152,12 @@ void PrintOverlaps(B2ShapeId sensorShapeId, string prefix)
149152
int count = b2Shape_GetSensorData(sensorShapeId, CollectionsMarshal.AsSpan(m_visitorIds), capacity);
150153
m_visitorIds.Resize(count);
151154

152-
var builder = new StringBuilder();
153-
for (int i = 0; i < count; ++i)
155+
var builder = new StringBuilder(payloadCapacity);
156+
string header = $"{prefix}: ";
157+
int storedByteCount = AppendUtf8(builder, header, payloadCapacity);
158+
int start = Encoding.UTF8.GetByteCount(header);
159+
160+
for (int i = 0; i < count && start < bufferSize; ++i)
154161
{
155162
B2ShapeId visitorId = m_visitorIds[i];
156163
if (b2Shape_IsValid(visitorId) == false)
@@ -160,19 +167,40 @@ void PrintOverlaps(B2ShapeId sensorShapeId, string prefix)
160167

161168
B2BodyId bodyId = b2Shape_GetBody(visitorId);
162169
string name = b2Body_GetName(bodyId);
163-
if (string.IsNullOrEmpty(name))
170+
if (name == null)
164171
{
165172
continue;
166173
}
167174

168175

169176
// todo fix this
170-
builder.Append($"{prefix}: {name}, ");
177+
string entry = $"{name}, ";
178+
int entryByteCount = Encoding.UTF8.GetByteCount(entry);
179+
storedByteCount += AppendUtf8(builder, entry, payloadCapacity - storedByteCount);
180+
start += entryByteCount;
171181
}
172182

173183
DrawTextLine(builder.ToString());
174184
}
175185

186+
private static int AppendUtf8(StringBuilder builder, string text, int maxByteCount)
187+
{
188+
int byteCount = 0;
189+
foreach (Rune rune in text.EnumerateRunes())
190+
{
191+
int runeByteCount = rune.Utf8SequenceLength;
192+
if (byteCount + runeByteCount > maxByteCount)
193+
{
194+
break;
195+
}
196+
197+
builder.Append(rune.ToString());
198+
byteCount += runeByteCount;
199+
}
200+
201+
return byteCount;
202+
}
203+
176204
public override void Step()
177205
{
178206
B2Vec2 position = b2Body_GetPosition(m_kinematicBodyId);
@@ -207,4 +235,4 @@ public override void Draw()
207235
DrawPoint(m_draw, result.point, 10.0f, B2HexColor.b2_colorCyan);
208236
}
209237
}
210-
}
238+
}

src/Box2D.NET.Samples/Samples/Issues/UnstablePrismaticJoints.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace Box2D.NET.Samples.Samples.Issues;
1313
// This can be made stable by increasing the size of the middle circle and/or increasing the number of sub-steps.
1414
public class UnstablePrismaticJoints : Sample
1515
{
16-
private static readonly int SamplePrismaticJointCrash = SampleFactory.Shared.RegisterSample("Issues", "Unstable Joints", Create);
16+
private static readonly int SamplePrismaticJointCrash = SampleFactory.Shared.RegisterSample("Issues", "Unstable Prismatic Joints", Create);
1717

1818
private static Sample Create(SampleContext context)
1919
{
@@ -101,4 +101,4 @@ public UnstablePrismaticJoints(SampleContext context) : base(context)
101101
b2CreatePrismaticJoint(m_worldId, jd);
102102
}
103103
}
104-
}
104+
}

src/Box2D.NET.Samples/Samples/Joints/MotorJoint.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ public override void UpdateGui()
145145

146146
if (ImGui.SliderFloat("Max Torque", ref m_maxTorque, 0.0f, 10000.0f, "%.0f"))
147147
{
148-
b2MotorJoint_SetMaxVelocityTorque(m_jointId, m_maxTorque);
148+
b2MotorJoint_SetMaxSpringTorque(m_jointId, m_maxTorque);
149149
}
150150

151151
if (ImGui.Button("Apply Impulse"))
@@ -199,4 +199,4 @@ public override void Draw()
199199
DrawTextLine($"force = {force.X:3,F0}, {force.Y:3,F0}, torque = {torque:3,F0}");
200200
DrawTransform(m_draw, _transform, 1.0f);
201201
}
202-
}
202+
}

src/Box2D.NET/B2BitSets.cs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,23 @@ public static class B2BitSets
1515
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1616
public static void b2SetBit(ref B2BitSet bitSet, int bitIndex)
1717
{
18+
if (bitIndex < 0)
19+
{
20+
throw new ArgumentOutOfRangeException(nameof(bitIndex));
21+
}
22+
1823
int blockIndex = bitIndex / 64;
1924
B2_ASSERT(blockIndex < bitSet.blockCount);
2025
bitSet.bits[blockIndex] |= ((ulong)1 << (bitIndex % 64));
2126
}
2227

2328
public static void b2SetBitGrow(ref B2BitSet bitSet, int bitIndex)
2429
{
30+
if (bitIndex < 0)
31+
{
32+
throw new ArgumentOutOfRangeException(nameof(bitIndex));
33+
}
34+
2535
int blockIndex = bitIndex / 64;
2636
if (blockIndex >= bitSet.blockCount)
2737
{
@@ -46,6 +56,11 @@ public static void b2ClearBit(ref B2BitSet bitSet, uint bitIndex)
4656
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4757
public static bool b2GetBit(ref B2BitSet bitSet, int bitIndex)
4858
{
59+
if (bitIndex < 0)
60+
{
61+
return false;
62+
}
63+
4964
int blockIndex = bitIndex / 64;
5065
if (blockIndex >= bitSet.blockCount)
5166
{
@@ -75,7 +90,10 @@ public static void b2CreateBitSet(ref B2BitSet bitSet, int bitCapacity)
7590
bitSet.blockCount = 0;
7691
bitSet.bits = b2Alloc<ulong>(bitSet.blockCapacity);
7792
//memset( bitSet.bits, 0, bitSet.blockCapacity * sizeof( ulong ) );
78-
Array.Fill(bitSet.bits, 0UL);
93+
if (bitSet.blockCapacity > 0)
94+
{
95+
Array.Fill(bitSet.bits, 0UL);
96+
}
7997
}
8098

8199
public static void b2DestroyBitSet(ref B2BitSet bitSet)
@@ -99,7 +117,10 @@ public static void b2SetBitCountAndClear(ref B2BitSet bitSet, int bitCount)
99117

100118
bitSet.blockCount = blockCount;
101119
//memset( bitSet->bits, 0, bitSet->blockCount * sizeof( ulong ) );
102-
Array.Fill(bitSet.bits, 0UL, 0, bitSet.blockCount);
120+
if (bitSet.blockCount > 0)
121+
{
122+
Array.Fill(bitSet.bits, 0UL, 0, bitSet.blockCount);
123+
}
103124
}
104125

105126
public static void b2GrowBitSet(ref B2BitSet bitSet, int blockCount)
@@ -154,4 +175,4 @@ public static void b2InPlaceUnion(ref B2BitSet setA, ref B2BitSet setB)
154175
}
155176
}
156177
}
157-
}
178+
}

src/Box2D.NET/B2Bodies.cs

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// SPDX-License-Identifier: MIT
44

55
using System;
6+
using System.Text;
67
using static Box2D.NET.B2Arrays;
78
using static Box2D.NET.B2Cores;
89
using static Box2D.NET.B2Diagnostics;
@@ -27,6 +28,42 @@ public static class B2Bodies
2728
// Length of body debug name
2829
public const int B2_NAME_LENGTH = 32;
2930

31+
private static string b2TruncateBodyName(string name)
32+
{
33+
if (string.IsNullOrEmpty(name))
34+
{
35+
return string.Empty;
36+
}
37+
38+
int maximumByteCount = B2_NAME_LENGTH - 1;
39+
if (Encoding.UTF8.GetByteCount(name) <= maximumByteCount)
40+
{
41+
return name;
42+
}
43+
44+
int characterCount = 0;
45+
int byteCount = 0;
46+
while (characterCount < name.Length)
47+
{
48+
int scalarCharacterCount =
49+
char.IsHighSurrogate(name[characterCount]) &&
50+
characterCount + 1 < name.Length &&
51+
char.IsLowSurrogate(name[characterCount + 1])
52+
? 2
53+
: 1;
54+
int scalarByteCount = Encoding.UTF8.GetByteCount(name, characterCount, scalarCharacterCount);
55+
if (byteCount + scalarByteCount > maximumByteCount)
56+
{
57+
break;
58+
}
59+
60+
characterCount += scalarCharacterCount;
61+
byteCount += scalarByteCount;
62+
}
63+
64+
return name.Substring(0, characterCount);
65+
}
66+
3067
// Identity body state, notice the deltaRotation is {1, 0}
3168
internal static readonly B2BodyState b2_identityBodyState = new B2BodyState()
3269
{
@@ -301,14 +338,7 @@ public static B2BodyId b2CreateBody(B2WorldId worldId, in B2BodyDef def)
301338

302339
B2Body body = b2Array_Get(ref world.bodies, bodyId);
303340

304-
if (!string.IsNullOrEmpty(def.name))
305-
{
306-
body.name = def.name;
307-
}
308-
else
309-
{
310-
body.name = string.Empty;
311-
}
341+
body.name = b2TruncateBodyName(def.name);
312342

313343
body.userData = def.userData;
314344
body.setIndex = setId;
@@ -1399,14 +1429,7 @@ public static void b2Body_SetName(B2BodyId bodyId, string name)
13991429
B2World world = b2GetWorld(bodyId.world0);
14001430
B2Body body = b2GetBodyFullId(world, bodyId);
14011431

1402-
if (!string.IsNullOrEmpty(name))
1403-
{
1404-
body.name = name;
1405-
}
1406-
else
1407-
{
1408-
body.name = string.Empty;
1409-
}
1432+
body.name = b2TruncateBodyName(name);
14101433
}
14111434

14121435
/// Get the body name.

src/Box2D.NET/B2Cores.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,15 @@ public static B2Version b2GetVersion()
4242
/// forces.
4343
/// @warning This must be modified before any calls to Box2D
4444
[MethodImpl(MethodImplOptions.AggressiveInlining)]
45-
internal static void b2SetLengthUnitsPerMeter(float lengthUnits)
45+
public static void b2SetLengthUnitsPerMeter(float lengthUnits)
4646
{
4747
B2_ASSERT(b2IsValidFloat(lengthUnits) && lengthUnits > 0.0f);
4848
b2_lengthUnitsPerMeter = lengthUnits;
4949
}
5050

5151
/// Get the current length units per meter.
5252
[MethodImpl(MethodImplOptions.AggressiveInlining)]
53-
internal static float b2GetLengthUnitsPerMeter()
53+
public static float b2GetLengthUnitsPerMeter()
5454
{
5555
return b2_lengthUnitsPerMeter;
5656
}
@@ -121,4 +121,4 @@ internal static void B2_CHECK_DEF(in B2ShapeDef def)
121121
B2_ASSERT(def.internalValue == B2_SECRET_COOKIE);
122122
}
123123
}
124-
}
124+
}

src/Box2D.NET/B2DebugDraw.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace Box2D.NET
77
/// This struct holds callbacks you can implement to draw a Box2D world.
88
/// This structure should be zero initialized.
99
/// @ingroup world
10-
public class B2DebugDraw
10+
public struct B2DebugDraw
1111
{
1212
public DrawPolygonFcn DrawPolygonFcn;
1313
public DrawSolidPolygonFcn DrawSolidPolygonFcn;

0 commit comments

Comments
 (0)