Limits

Every number the step multiplies has a range, in meters, kilograms and seconds. Each leaves realistic game values far inside it, and each is chosen against the products the solver forms in 32-bit floats. A value outside its range is refused, whether it arrives in a def, a setter, a journal tape or a snapshot. A def or another call that returns a status refuses it with m2_errorLimit, a NaN or a value outside the quantity's own domain (a negative rate, a restitution above 1) with m2_errorInvalid, and invalid wins when one call finds both; a call that returns nothing refuses either by changing nothing. Each counts one misuse. A tape or snapshot is refused with m2_errorFormat and counts nothing. What the step itself accumulates is held to its range as well, so every snapshot a world writes restores. A gear's phase and a ratchet's angle and tooth are the exceptions: they grow with every turn, and a restore holds them only to be finite. The constants are in maul2d/base.h.

The ranges

Constant Value What it bounds Realistic values
M2_SPEED_LIMIT 1e5 m/s, rad/s a body's velocity per axis and its spin, a conveyor's speed, a motor's speed, the wind, a water volume's flow, a particle's velocity a rifle bullet is about 1e3 m/s; the solver caps motion at 400 m/s anyway
M2_ACCEL_LIMIT 1e6 m/s^2 gravity per axis Earth is 9.8; a game's gravity rarely passes a few thousand
M2_GRAVITY_SCALE_LIMIT 1e3 a body's or the particles' gravity scale, either sign 0 to a few
M2_RATE_LIMIT 1e6 1/s linear and angular damping, wind and fluid drag, a spring's hertz damping 0 to 10, springs up to a few hundred hertz
M2_COEFFICIENT_LIMIT 1e3 friction, a damping ratio, a particle strength friction 0 to 2, damping ratios 0 to 10
restitution 0 to 1 a shape's or a chain's bounce 0 to 1
M2_DENSITY_LIMIT 1e6 kg/m^2 a shape's, a water volume's or the particles' density foam about 30, water 1e3, lead about 1.1e4
M2_MIN_RATIO, M2_RATIO_LIMIT 1e-3 to 1e3 a gear's or pulley's ratio, by magnitude 0.01 to 100
M2_MIN_MASS, M2_MASS_LIMIT 1e-9 to 1e12 kg a dynamic body's mass; a particle's mass a 1 g bullet to a 1e8 kg ship
M2_MIN_INERTIA, M2_INERTIA_LIMIT 1e-12 to 1e24 kg m^2, or 0 a dynamic body's rotational inertia about its center of mass; 0 does not rotate a 5 mm disc at density 1 is about 1e-9
M2_FORCE_LIMIT 1e20 N, N m a force or torque per axis, a body's accumulated load, a motor's or mouse's budget a rocket engine is about 1e7 N
M2_IMPULSE_LIMIT 1e15 N s an impulse per axis that one call applies, and every impulse a joint row or contact point accumulates a 1e8 kg ship stopping from 100 m/s takes 1e10
M2_MIN_STEP, M2_STEP_LIMIT 1e-6 to 10 s m2World_Step's and m2Body_SetTargetTransform's time step 1/240 to 1/20
M2_ANGLE_LIMIT 1e5 rad a joint's angle limits, angular offset, a ratchet's tooth and phase within a few turns
M2_MIN_RATCHET_TOOTH 1e-3 rad a ratchet's tooth, by magnitude 2 pi / 12 to 2 pi / 360
M2_REACH_LIMIT 1e9 m a joint's length, translations and limits; the farthest a joint takes its bodies apart a rope of a few hundred meters
M2_LOCAL_LIMIT 1e5 m a body-local length: shape coordinates and radii, joint anchors, an explosion's radius and falloff a level a few kilometers wide

World positions are 64-bit and not bounded. A joint takes bodies farther apart than M2_REACH_LIMIT along an axis as that far apart, and so does an impulse or force applied at a far point. At the solver's 400 m/s cap, closing 1e9 m takes a month.

World size

Snapshot and journal sizes are int64_t byte counts, so a world is bounded by its capacities and memory, not by its formats. A shape slot takes about 1.3 KB of the snapshot, its contact pairs included, a particle about 170 bytes, a body about 110 and a joint about 160; m2World_GetSnapshotSize gives a world's exact size. On a 32-bit target, m2CreateWorld refuses with m2_errorCapacity, before it allocates anything, a def whose snapshot a size_t cannot hold, and m2World_StartJournal and m2World_ReplayJournal refuse a size past it as an invalid argument.

Clamped and saturated values

Three kinds of value are held to their ranges instead of refused:

Why these values

The bounds are chosen against FLT_MAX, about 3.4e38. The tightest product among them is the push a joint row gives a body's spin: inverse inertia times impulse times lever. At the smallest inertia, twice the impulse limit (the change between two impulses within it) and the longest joint lever, that is 1e12 x 2e15 x 1e9 = 2e36, two orders of magnitude below FLT_MAX. Making M2_MIN_INERTIA smaller or M2_REACH_LIMIT larger would have to make the impulse limit smaller to match. The other bounds leave more room. With the saturation above, these are the largest values each kind of product can reach, at the worst corner of the ranges:

Product Worst case Bound
a row's speed J v speed times the longest joint lever, both bodies 2 x 1e5 x 1e9 = 2e14
a row's effective-mass term k inverse inertia times the longest lever squared 2 x 1e12 x (1e9)^2 = 2e30
a row pair's k11 k22 both terms at the fallback threshold 1e19 x 1e19 = 1e38
a line row's impulse before its clamp effective mass 1e12 times a limit gap of 4e9 m over the shortest substep (6.4e7 1/s) 1e12 x 2.6e17 = 2.6e29
a turn row's impulse before its clamp effective mass 1e24 times an angle gap of 1e5 rad over the shortest substep 1e24 x 6.4e12 = 6.4e36
a gear row against a body that cannot turn effective mass 1e24 / r^2 times its speed, r = 1e-3 1e24 x 1e5 x 1e6 = 1e35
a push to a velocity, along a line inverse mass times twice the impulse limit times the largest ratio 1e9 x 2e15 x 1e3 = 2e27
a push to a spin inverse inertia times twice the impulse limit times the longest lever 1e12 x 2e15 x 1e9 = 2e36
a friction bound friction times the impulse limit 1e3 x 1e15 = 1e18
a contact's push target the largest separation over the shortest substep, times its mass 2.9e5 x 6.4e7 x 1e12 = 1.9e25
a force over a substep longest substep, lightest body, largest force 10 x 1e9 x 1e20 = 1e30
a torque over a substep longest substep, smallest inertia, largest torque 10 x 1e12 x 1e20 = 1e33
water's spin drag squared radius of gyration (held to 4e10) times area, spin and drag 4e10 x 4e10 x 1e5 x 1e6 = 1.6e32

test/test_extremes.c builds scenes at these corners (the lightest and heaviest bodies, the longest levers, the stiffest springs, ratios at both ends, water and wind at their strongest, particles at every strength) and steps them with overflow, invalid operations and division by zero trapping, so a single product past the float range would stop it.