SwingTwist Constraint
Combines elliptical swing limits, a signed twist range, friction and independent native swing/twist motors.
jolt_swing_twist() exposes Jolt Physics 5.6 SwingTwistConstraint directly. It keeps two attachment points together while controlling the orientation between articulated links, limbs, rope-like chains and other joints that need more detail than Cone.
Syntax#
jolt_swing_twist(body_a, body_b, anchor_x, anchor_y, anchor_z, options=undefined)Options#
| Field | Default | Description |
|---|---|---|
| twist_axis | {x:0,y:0,z:1} | World-space constraint X axis around which twist occurs. |
| plane_axis | {x:1,y:0,z:0} | World-space constraint Z axis defining the swing frame. |
| swing_angle | 45 | Degree alias used for both swing half-angles unless they are supplied separately. |
| normal_half_angle | swing_angle | Swing half-angle around constraint-space normal/Y, 0..180 degrees. |
| plane_half_angle | swing_angle | Swing half-angle around constraint-space plane/Z, 0..180 degrees. |
| twist_min | -30 | Inclusive signed minimum twist in degrees, at least -180. |
| twist_max | 30 | Inclusive signed maximum twist in degrees, at most 180. |
| friction_torque | 0 | Non-negative maximum passive friction torque in N m. |
| collision | false | Whether the connected bodies may collide. |
| enabled | true | Initial solver state. |
Reference frame#
- The anchor and both axes are world-space values at creation.
- Twist Axis becomes constraint-space X. Plane Axis becomes constraint-space Z, and Jolt derives the normal/Y axis.
- Axes are normalized and the plane axis is orthogonalized around the twist axis.
- Zero-length or nearly parallel axes are rejected.
- Different normal and plane half-angles create a real elliptical swing range rather than a visual approximation.
Limits and state#
| Method | Result |
|---|---|
| set_limits(normal, plane, twist_min, twist_max) | Updates all four degree limits. |
| set_swing_limits(normal, plane) | Updates only the elliptical swing half-angles. |
| set_twist_limits(minimum, maximum) | Updates only the signed twist range. |
| get_limits() | Returns {normal_half_angle, plane_half_angle, twist_min, twist_max}. |
| set_friction_torque(value) / get_friction_torque() | Changes or reads passive maximum friction torque. |
| get_twist_angle() | Returns current signed twist in degrees. |
| get_orientation() | Returns current constraint-space quaternion {x,y,z,w}. |
| get_swing_twist_impulses() | Returns position, twist, swing and motor solver impulses. |
Independent motors#
| Swing | Twist |
|---|---|
| set_swing_motor_velocity(y_deg_s, z_deg_s) | set_twist_motor_velocity(deg_s) |
| set_swing_motor_position(angle_deg, axis_y=1, axis_z=0) | set_twist_motor_position(angle_deg) |
| enable_swing_motor(enabled) | enable_twist_motor(enabled) |
| set_swing_motor_max_torque(value) | set_twist_motor_max_torque(value) |
| set_swing_motor_spring(frequency, damping) | set_twist_motor_spring(frequency, damping) |
| get_swing_motor_state() | get_twist_motor_state() |
Swing velocity components use constraint-space Y/Z. A swing position is an angle around a normalized axis in that Y/Z plane. Twist uses constraint-space X. These high-level motor velocities are degrees per second.
Example: articulated link#
var upper = jolt_box_create_dynamic(0, 0, 5, 0.5, 0.5, 1, { mass: 2 });
var lower = jolt_box_create_dynamic(0, 0, 4, 0.5, 0.5, 1, { mass: 2 });
var joint = jolt_swing_twist(upper, lower, 0, 0, 4.5, {
twist_axis: { x:0, y:0, z:1 },
plane_axis: { x:1, y:0, z:0 },
normal_half_angle: 20,
plane_half_angle: 60,
twist_min: -30,
twist_max: 30,
friction_torque: 0.5,
collision: false
});Example: twist motor#
joint.set_twist_motor_max_torque(20);
joint.set_twist_motor_spring(3, 0.8);
joint.set_twist_motor_position(25);
var twist = joint.get_twist_angle();
var state = joint.get_twist_motor_state();Lifecycle, debug and limitations#
- is_valid() / get_native_handle(): Checks the wrapper or returns its opaque native handle.
- destroy(): Idempotently releases the constraint without destroying either body.
- set_enabled() / get_enabled(): Changes solver participation while preserving the resource.
- set_collision_enabled() / get_collision_enabled(): Controls the connected-body pair filter.
- Destroying either body invalidates the generational constraint handle; jolt_stop() cleans it before bodies and world.
- JOLT_DEBUG_CONSTRAINTS draws the native frame, elliptical boundary, twist arc, current orientation and active motors.
- This creator supports body-to-body Cone swing only. There is no world convenience variant and no public rope/chain helper.
- Solver impulses are accumulated Lagrange multipliers from the latest step, not forces or torques per second.
Verified operation contracts#
These contracts distinguish units, return values, errors and lifetime according to the current implementation. Check results: false, 0 and undefined can mean different things for different methods.
| Symbol / owner | Signature | Operation contract |
|---|---|---|
| jolt_swing_twist | jolt_swing_twist(body_a, body_b, anchor_x, anchor_y, anchor_z, options = undefined) | Returns JoltConstraint/undefined, does not own bodies. Two distinct live same-world bodies; _world variant one Dynamic body and fixed world. Enabled defaults true; ordinary two-body collision defaults true unless specified otherwise below. Also check jolt_has_error after creation: some subsequent options can fail while returning the created constraint. World anchor; twist_axis defaults+Z, plane_axis+X, nonzero nonparallel. Swing half angles0..180° default 45, twist-30..30° within±180, friction_torque>=0 default 0. Collision defaults false. |
| jolt_swing_twist_world | jolt_swing_twist_world(body,anchor_x,anchor_y,anchor_z,options=undefined) | Returns JoltConstraint/undefined, does not own bodies. Two distinct live same-world bodies; _world variant one Dynamic body and fixed world. Enabled defaults true; ordinary two-body collision defaults true unless specified otherwise below. Also check jolt_has_error after creation: some subsequent options can fail while returning the created constraint. World anchor; twist_axis defaults+Z, plane_axis+X, nonzero nonparallel. Swing half angles0..180° default 45, twist-30..30° within±180, friction_torque>=0 default 0. Collision defaults false. |
| JoltConstraint.enable_swing_motor | JoltConstraint.enable_swing_motor(enabled) | SwingTwist only; Boolean success. False selects OFF, true restores the last selected mode (VELOCITY before the first setting). |
| JoltConstraint.enable_twist_motor | JoltConstraint.enable_twist_motor(enabled) | SwingTwist only; Boolean success. False selects OFF, true restores the last selected mode (VELOCITY before the first setting). |
| JoltConstraint.get_current_orientation | JoltConstraint.get_current_orientation() | SixDOF: target/current constraint-space quaternion {x, y, z, w}. Composes native reads without a reliable undefined sentinel; check type/liveness and errors first. |
| JoltConstraint.get_current_orientation_euler | JoltConstraint.get_current_orientation_euler() | SixDOF: Euler {x, y, z} in degrees from the corresponding quaternion getter; check validity and errors first. |
| JoltConstraint.get_orientation | JoltConstraint.get_orientation() | SwingTwist only: current relative orientation in constraint space as {x, y, z, w}; undefined on error. |
| JoltConstraint.get_swing_motor_state | JoltConstraint.get_swing_motor_state() | SwingTwist struct {mode, velocity_y, velocity_z, max_torque, spring_frequency, spring_damping}. Velocities °/s, torque N·m, frequency Hz. undefined for invalid type/wrapper; inspect individual native errors separately. |
| JoltConstraint.get_swing_twist_impulses | JoltConstraint.get_swing_twist_impulses() | SwingTwist only, latest solver impulses: position {x, y, z} (N·s), twist/swing_y/swing_z and motor {x, y, z} (N·m·s). undefined for invalid type/wrapper; not forces per second. |
| JoltConstraint.get_target_orientation | JoltConstraint.get_target_orientation() | SixDOF: target/current constraint-space quaternion {x, y, z, w}. Composes native reads without a reliable undefined sentinel; check type/liveness and errors first. |
| JoltConstraint.get_target_orientation_euler | JoltConstraint.get_target_orientation_euler() | SixDOF: Euler {x, y, z} in degrees from the corresponding quaternion getter; check validity and errors first. |
| JoltConstraint.get_twist_angle | JoltConstraint.get_twist_angle() | SwingTwist only: current twist around constraint X in degrees; 0 on error/invalid type. |
| JoltConstraint.get_twist_motor_state | JoltConstraint.get_twist_motor_state() | SwingTwist struct {mode, velocity, max_torque, spring_frequency, spring_damping}; velocity °/s, N·m, Hz. undefined for invalid type/wrapper; inspect native errors separately. |
| JoltConstraint.set_swing_limits | JoltConstraint.set_swing_limits(normal, plane) | SwingTwist only; Boolean. Swing half-angles 0..180°, twist min/max within -180..180° with min≤max. Preserves the other part of current limits. |
| JoltConstraint.set_swing_motor_max_torque | JoltConstraint.set_swing_motor_max_torque(torque) | SwingTwist only; finite nonnegative limit in N·m; Boolean. Setting the limit alone does not enable the motor. |
| JoltConstraint.set_swing_motor_position | JoltConstraint.set_swing_motor_position(angle, constraint_axis_y=1, constraint_axis_z=0) | SwingTwist only: finite angle in degrees and nonzero Y/Z-plane axis, default (1, 0), normalized natively. Boolean; enables position motor and preserves target twist. |
| JoltConstraint.set_swing_motor_spring | JoltConstraint.set_swing_motor_spring(frequency,damping) | SwingTwist only; finite frequency ≥0 Hz, damping ≥0 as a damping ratio; Boolean. |
| JoltConstraint.set_swing_motor_velocity | JoltConstraint.set_swing_motor_velocity(constraint_y, constraint_z) | SwingTwist only: finite velocities around constraint Y/Z in degrees/s. Boolean; also enables the swing motor in velocity mode. |
| JoltConstraint.set_target_orientation | JoltConstraint.set_target_orientation(x,y,z,w) | SixDOF: constraint-space orientation; finite nonzero quaternion x/y/z/w normalized natively, or Euler XYZ in degrees. Boolean. Motors must be enabled separately. |
| JoltConstraint.set_target_orientation_euler | JoltConstraint.set_target_orientation_euler(x,y,z) | SixDOF: constraint-space orientation; finite nonzero quaternion x/y/z/w normalized natively, or Euler XYZ in degrees. Boolean. Motors must be enabled separately. |
| JoltConstraint.set_twist_limits | JoltConstraint.set_twist_limits(minimum, maximum) | SwingTwist only; Boolean. Swing half-angles 0..180°, twist min/max within -180..180° with min≤max. Preserves the other part of current limits. |
| JoltConstraint.set_twist_motor_max_torque | JoltConstraint.set_twist_motor_max_torque(torque) | SwingTwist only; finite nonnegative limit in N·m; Boolean. Setting the limit alone does not enable the motor. |
| JoltConstraint.set_twist_motor_position | JoltConstraint.set_twist_motor_position(angle) | SwingTwist only: finite twist angle in degrees. Boolean; enables position motor and preserves target swing. |
| JoltConstraint.set_twist_motor_spring | JoltConstraint.set_twist_motor_spring(frequency,damping) | SwingTwist only; finite frequency ≥0 Hz, damping ≥0 as a damping ratio; Boolean. |
| JoltConstraint.set_twist_motor_velocity | JoltConstraint.set_twist_motor_velocity(degrees_per_second) | SwingTwist only: finite velocity around constraint X in degrees/s. Boolean; also enables the twist motor in velocity mode. |