namespacePhysics7 functions, 1 propertyUI apps only
datumhue.physics
Unified 2D/3D physics. A DrawPrimitive's body simulates in the app's 2D world, a SceneNode's or Voxel's in its 3D world. Each app simulates in its own worlds: bodies, joints, queries, and gravity never interact with other apps.
Create an additional isolated physics world. Bodies join it via add_body{world = ...}; it has its own gravity and query methods. Bodies in different worlds never interact.
Register this app's physics step handler, called once per completed simulation step with dt the fixed timestep in seconds -- the constant interval every step advances by, independent of frame rate. Steps finish before the frame's Lua callbacks run, so the handler reads post-step state; within a frame it runs after the same steps' collision callbacks and before on_update, and a frame that ran several catch-up steps calls it once per step. At most one handler is active per app -- calling again replaces the previous one. Pass nil to clear it.
Create a physics joint (fixed, revolute, prismatic, distance, or spherical) between two bodies of the same dimension and world. The joint rests in the pose the bodies are in when it is made: a fixed joint keeps their turn, a hinge's zero angle is that pose. The anchors and axis follow each body's handle as it scales, and the joint survives add_body replacing a body in the same world (moving a jointed body to another world raises). Raises for a body joined to itself, bodies of different worlds or dimensions, and an option its kind does not take.
Example
local hub = canvas:circle({ radius = 6 })
local arm = canvas:rect({ pos = vec2(0, -4), width = 80, height = 8 })
hub:add_body({ type = "static" })
arm:add_body({ mass = 1 })
-- A hinge at the hub's center. Anchors are in each body's own-- frame, so the arm's is its left end. The motor turns it.datumhue.physics.add_joint(hub, arm, {
type = "revolute",
anchor2 = vec3(-40, 0, 0),
motor_speed = math.pi,
motor_force = 100000,
})
+z for a revolute joint, +x for a prismatic or spherical one
Hinge axis (revolute), slide axis (prismatic), or twist axis (spherical): a non-zero direction in the first body's handle frame; the second body takes the same line as it is drawn. A 2D revolute turns about +z and takes no other axis.
angle_limits
number[]
the hinge turns freely
Revolute-only {min, max} angle range in radians from the pose the joint was made in, positive counter-clockwise about the axis as it points from the first body.
limits
number[]
the body slides freely along the axis
Prismatic-only {min, max} travel along the axis, in world units.
distance_limits
number[]
a distance joint raises
Distance-only, {0, max}: the anchors stay at most max world units apart (at most 1000000), like a rope; nothing holds them apart.
swing_limits
number[]
the swing is free
Spherical-only {min, max} angle in radians between the two bodies' twist axes, min 0 or more: a cone around the axis.
twist_limits
number[]
the twist is free
Spherical-only {min, max} twist about the axis, in radians from the pose the joint was made in.
motor_speed
number
no velocity motor
Target motor velocity (radians/s revolute, world units/s prismatic).
motor_force
number
the motor's torque or force is unlimited
Maximum motor torque (revolute) or force (prismatic), 0 or more.
motor_target
number
no position motor
Motor target position (radians revolute, world units prismatic).
motor_stiffness
number
1000 with a motor_target, else 0
Spring gain toward motor_target, 0 or more.
motor_damping
number
100 with a motor_speed, 30 with only a motor_target, else 0
Cast a ray and return the nearest body hit, or nil. An options value that is not a table is ignored.
Example
-- New bodies are found once the simulation has stepped.datumhue.time.sleep(0.1)
-- A 2D ray runs in the canvas plane, at z = 0.local down = datumhue.math.ray(vec3(0, 100, 0), vec3(0, -1, 0))
local hit = datumhue.physics.raycast(down, { max_distance = 500 })
-- The floor's top is at y = -130.print(hit.body == floor, hit.distance) --> true 230.0print(hit.point) --> vec3(0.0, -130.0, 0.0)print(hit.normal) --> vec3(0.0, 1.0, 0.0)
Sweep a 3D collider shape along the ray and return the nearest body hit, or nil.
Example
-- It sweeps a 3D shape, so it finds bodies in a 3D scene.local scene = datumhue.scene.new({ width = 320, height = 240 })
local ground = scene:cube({ scale = vec3(10, 1, 10) })
ground:add_body({ type = "static" })
-- New bodies are found once the simulation has stepped.datumhue.time.sleep(0.1)
local down = datumhue.math.ray(vec3(0, 5, 0), vec3(0, -1, 0))
local box = { shape = "cube", width = 2, height = 1, depth = 2 }
local hit = datumhue.physics.shapecast(down, box)
-- The box's bottom, 0.5 below the ray, meets the top at y = 0.5.print(hit.body == ground, hit.distance) --> true 4.0
Box, cylinder, or cone height, or a capsule's straight length between its caps, in the handle's own units.
depth
number
taken from what the handle draws, or 1
Box depth in the handle's own units, 3D only.
radius
number
taken from what the handle draws, or 0.5
Circle, sphere, capsule, cylinder, or cone radius, in the handle's own units.
restitution
number
0.3
Bounciness, 0 or more: 0 absorbs an impact, 1 keeps all its energy.
friction
number
0.5
Surface friction, 0 or more.
mass
number
derived from the collider's size
Mass override, more than 0.
sensor
boolean
false
Make the body a non-colliding trigger volume that reports the bodies overlapping it: dynamic and kinematic ones, and static ones unless the sensor is static itself.
Axes to lock rotation on, on top of a 2D body's in-plane constraints.
ccd
boolean
false
Sweep this body against moving (kinematic and dynamic) bodies too when it moves fast; fast bodies always sweep static geometry so they never tunnel through it.
Step-climbing config, or false to disable; stepping is expensive. Needs a flat-bottomed body (cube or cylinder); a capsule's rounded bottom slides back off the ledge. The body also needs head room of its own height plus max_height above where it stands, so a low ceiling stops it stepping.
normal_nudge_factor
number
0.0001
Tiny anti-stick push along contact normals while sliding, 0 to 1.