Spherical wander (WP-8.2): steer toward a target that jitters on a
sphere in a heading-aligned frame.
angle += uniform(−jitter · dt, +jitter · dt) (draw 1 of 2)
elevation += uniform(−jitter · dt, +jitter · dt) (draw 2 of 2)
elevation = clamp(elevation, −π/2, +π/2)
frame = orthonormal(forward = velocity, up ≈ +Y) // Gram-Schmidt
centre = position + forward · distance
target = centre + radius · (
cos(el)·cos(az) · forward +
sin(el) · up +
cos(el)·sin(az) · right
)
result = seek(target)
The frame is built from the agent's velocity: forward is the unit
velocity, up is world +Y with its forward component removed
(Gram-Schmidt), and right = forward × up. A zero (or degenerate)
velocity falls back to +X forward, +Y up; a velocity parallel to
+Y retries the hint with +Z, then +X.
Determinism — two draws (WP-8.2)
Exactly two draws from random per call, always, in that order:
azimuth then elevation. Planar wander stays one draw, so a stream
that used to feed wander cannot be replayed through this function and
expected to match. Two agents seeded alike still trace identical 3D
paths. tests/steering.test.ts asserts both the draw count and the
bit-identity.
Elevation is clamped to [−π/2, π/2] rather than wrapped: wrapping
through a pole would flip the azimuth's meaning and produce a
discontinuous jump. Azimuth wraps to [−π, π) like wander.
Spherical wander (WP-8.2): steer toward a target that jitters on a sphere in a heading-aligned frame.
The frame is built from the agent's velocity:
forwardis the unit velocity,upis world+Ywith itsforwardcomponent removed (Gram-Schmidt), andright = forward × up. A zero (or degenerate) velocity falls back to+Xforward,+Yup; a velocity parallel to+Yretries the hint with+Z, then+X.Determinism — two draws (WP-8.2)
Exactly two draws from
randomper call, always, in that order: azimuth then elevation. Planar wander stays one draw, so a stream that used to feedwandercannot be replayed through this function and expected to match. Two agents seeded alike still trace identical 3D paths.tests/steering.test.tsasserts both the draw count and the bit-identity.Elevation is clamped to
[−π/2, π/2]rather than wrapped: wrapping through a pole would flip the azimuth's meaning and produce a discontinuous jump. Azimuth wraps to[−π, π)like wander.