@ferret/physics-actuators
forces, impulses, torques and motors at points of bodies on a schedule: profiles over time, angle, phase or a sensor.
Read the guide: how to use it →
Classes
Bindings class
class BindingsRuns a list of bindings against an actuators capability and the services of a world; step is called once per fixed step by the capability.
constructor(private world: World, private target: BindingTarget) | |
list(): BindingDef[] | |
errors(): Array<{ name: string; error: string }> | |
set(def: BindingDef): void | |
remove(name: string): void | |
clear(): void | |
step(): void | once per fixed step: poll the input actions, and write the actuators' numbers to the animation graphs |
packages/physics-actuators/src/bindings.ts:110
Functions
actuators function
function actuators(): ActuatorsCapabilitypackages/physics-actuators/src/index.ts:188
bodyTarget function
function bodyTarget(host: PhysicsHost, b: Body): ForceTargetThe body the kernel owns as a target.
packages/physics-actuators/src/index.ts:139
compileProfile function
function compileProfile(spec: ProfileSpec | undefined): Profilepackages/physics-actuators/src/profiles.ts:46
motionPhysics function
function motionPhysics(act: ActuatorsCapability, opts: { seed?: number } = {}): MotionCapabilitypackages/physics-actuators/src/motion.ts:49
netActuators function
function netActuators(cap: ActuatorsCapability, net: NetLike, opts: NetActuatorOptions): NetActuatorspackages/physics-actuators/src/net.ts:38
parseQuantity function
function parseQuantity(v: unknown, kind: 'angle' | 'speed'): number"90deg", "0.5turn", "30rpm" and plain numbers (SI) read at load; unit is what a bare number means.
packages/physics-actuators/src/profiles.ts:151
registerActuatorKind function
function registerActuatorKind(kind: string, spec: ActuatorKindSpec): voidpackages/physics-actuators/src/index.ts:32
validateActuator function
function validateActuator(d: unknown, path = 'actuator'): string[]packages/physics-actuators/src/index.ts:153
validateBinding function
function validateBinding(d: unknown, path = 'binding'): string[]packages/physics-actuators/src/bindings.ts:68
validateProfile function
function validateProfile(spec: unknown, path = 'profile'): string[]Problems with a profile spec, as messages (empty when it is good); path prefixes them.
packages/physics-actuators/src/profiles.ts:110
vesselTarget function
function vesselTarget(v: { pos: Vec3; q: Quat; v: Vec3; w: Vec3; applyForce(f: Vec3, at?: Vec3): void; applyTorque(t: Vec3): void; applyImpulse(j: Vec3, at?: Vec3): void }): ForceTargetA vessel (or anything with pos, q, v, w and the three load methods) as a target: an oar on a boat.
packages/physics-actuators/src/index.ts:149
wave function
function wave(shape: Shape, u: number, duty: number): numberThe wave of a shape at a fraction u of its period (0..1) and the duty: 0..1 (sine is -1..1 over the whole period).
packages/physics-actuators/src/profiles.ts:36
Interfaces
ActuatorDef interface
interface ActuatorDefname: string | |
node: string | the node (entity) of the body it acts on, or a target added with addTarget (a vessel, a shaft) |
kind: ActuatorKind | |
at?: Vec3 | the point in the body's frame (metres); default the centre of mass |
point?: string | or the name of a child node whose origin is the point (an empty authored in Blender or UE) |
dir?: Direction | Vec3 | force and impulse: the direction; a plain vector is in the body's frame |
axis?: Direction | Vec3 | torque and motor: the axis, same frames |
magnitude?: number | N for force, N·s per firing for impulse, N·m for torque |
speed?: number | string | { rpm: number } | motor: the held speed (rad/s, or "30rpm") scaled by the profile, its torque limit and gain (N·m per rad/s of error) |
maxTorque?: number | |
gain?: number | |
mode?: SpinMode | SpinMode[] | spin: one mode or several (switchable combines with either of the others; alone it is an unstoppable shaft with a switch) |
rampUp?: number | spin: seconds to speed up from a stop and to slow down to one (instant when missing) |
rampDown?: number | |
initialAngle?: number | string | spin: the angle at time 0 (rad, or "90deg") |
stallTime?: number | spin jammable: a held motor (speed under 10 % of the target with the torque at its limit) for this long is a stall (default 0.5 s) |
profile?: ProfileSpec | |
over?: { angle?: string; turns?: number; phase?: string; sensor?: string } | |
instances?: Array<{ offset?: number | string }> | several copies of one definition run at an offset along what the profile runs over (cylinders, oars) |
offset?: number | string | |
command?: number | the run-time multiplier: a throttle, set by input, a lever, a zone, a program or a command |
on?: boolean | on at load (default true) |
packages/physics-actuators/src/index.ts:37
ActuatorKindSpec interface
interface ActuatorKindSpecvalidate?(d: ActuatorDef, path: string): string[] | |
force(c: KindContext): KindForce |
packages/physics-actuators/src/index.ts:30
ActuatorsCapability interface
interface ActuatorsCapability extends Capabilityreadonly actuators: Map<string, ActuatorState> | |
set(def: ActuatorDef): void | |
remove(name: string): void | |
list(): ActuatorDef[] | |
on(name: string): void | |
off(name: string): void | |
fire(name: string): void | |
command(name: string, value: number): void | |
state(name?: string): ActuatorState | ActuatorState[] | |
addTarget(node: string, target: ForceTarget): void | a body that is not a RigidBody (a vessel, a shaft) as a target for actuators naming node |
removeTarget(node: string): void | |
addSensor(name: string, read: () => number): void | a named number a profile can run over: over: { sensor } |
addSpinSink(node: string, sink: SpinSink): void | a drivetrain speed source as the target of spin actuators naming node: the gears behind it see real torques |
bind(def: BindingDef): void | what drives the commands: input actions, interactables, zones and events, and what the actuators give back to an animation (see BindingDef) |
unbind(name: string): void | |
bindings(): BindingDef[] | |
has(name: string): boolean | |
target(node: string): ForceTarget | null | the body (or vessel, or shaft) that actuators naming node push; null when there is none |
owns?: (def: ActuatorDef) => boolean | over the net: false means another machine owns the body and this one does not act on it (set by netActuators) |
packages/physics-actuators/src/index.ts:106
ActuatorState interface
interface ActuatorStatename: string | |
on: boolean | |
command: number | |
time: number | |
value: number | |
phase: number | |
force: Vec3 | |
torque: Vec3 | |
power: number | |
work: number | |
fired: number | |
done: boolean | |
error?: string | |
speed?: number | spin: the speed now (rad/s about the axis), the angle (rad, unwrapped), and whether it is stalled |
angle?: number | |
stalled?: boolean | |
at?: Vec3 | where the force acts now (world), for the inspector's arrows |
packages/physics-actuators/src/index.ts:86
BindingDef interface
interface BindingDefname?: string | |
actuator: string | |
as?: BindMode | |
action?: string | the sources: exactly one of action, interactable, zone, event (and animate alone for the way back) |
interactable?: string | |
zone?: string | |
event?: string | |
match?: Record<string, unknown> | event: the payload fields that must match; a field holding an entity matches its name |
actor?: string | zone: only this entity (by name) or tag (tag:player) counts |
value?: number | the command while active (default 1) and at rest (default 0); axis multiplies the value read by value |
rest?: number | |
component?: 'x' | 'y' | axis: which component of a two-axis action (default x) |
animate?: { entity: string; /** a parameter of the graph that follows a number of the actuator */ param?: string; from?: 'phase' | 'value' | 'power' | 'speed' | 'command' | 'work'; scale?: number; /** or a trigger of the graph fired when the actuator starts, stops, is done, strokes or stalls */ trigger?: string; on?: 'start' | 'stop' | 'done' | 'stroke' | 'stall'; } |
packages/physics-actuators/src/bindings.ts:16
BindingTarget interface
interface BindingTargetWhat bindings need from the actuators capability.
command(name: string, value: number): void | |
on(name: string): void | |
off(name: string): void | |
fire(name: string): void | |
state(name?: string): any | |
has(name: string): boolean |
packages/physics-actuators/src/bindings.ts:47
Direction interface
interface Directionpackages/physics-actuators/src/index.ts:35
ForceTarget interface
interface ForceTargetWhat an actuator pushes: a rigid body, a vessel, anything with a pose and the three ways to apply a load.
position(): Vec3 | |
orientation(): Quat | |
velocity(): Vec3 | |
angularVelocity(): Vec3 | |
applyForce(f: Vec3, worldPoint?: Vec3): void | |
applyTorque(t: Vec3): void | |
applyImpulse(j: Vec3, worldPoint?: Vec3): void |
packages/physics-actuators/src/index.ts:76
KindContext interface
interface KindContextA kind of actuator that another package supplies (blade comes from
def: ActuatorDef | |
host: PhysicsHost | |
self: ForceTarget | |
dt: number | |
at: Vec3 | the point (world) the force acts at and its velocity |
pointVelocity: Vec3 | |
value: number | profile value × command for this instance |
time: number | the scene time and the profile position (for strokes that follow the phase) |
packages/physics-actuators/src/index.ts:19
MotionCapability interface
interface MotionCapability extends CapabilityPhysics in the language. The motion capability owns a MotionRuntime, ticks it once per fixed step before the actuators act, and gives programs the words that touch the world:
actions (last while their form does) force torque motor flow-ramp
instants impulse command actuator-on/off fire flow-on/off/source/move list world
sensors speed speed-along angular-speed position height tilt rpm angle in-angle in-zone in-medium flow-at
apparent-wind wind-speed wind-dir power work stalled phase
macro (over-angle shaft (from a b [axis]) body...)A program has a target (the node of a body, from motion.run { target } or (bind "Boat" ...)); a level-wide program (weather) has none and can use only the flow words.
readonly rt: MotionRuntime | |
addPoint(name: string, node: string): void | a named point on a body for force and impulse: oarlock-left is the node Oarlock_L |
run(program: string | Form | unknown, opts?: { target?: string; params?: Record<string, Value> }): Program | |
setSource(name: string, source: string | unknown[]): string[] | add or replace a source file (definitions and programs); programs of the file that are not definitions start now, and (bind "Node" ...) gives them a target |
removeSource(name: string): void | |
sources(): Array<{ name: string; source: string }> | |
targetOf(p: Program): string | null |
packages/physics-actuators/src/motion.ts:16
NetActuatorOptions interface
interface NetActuatorOptionsowns?: (def: ActuatorDef) => boolean | does this machine own the body the actuator acts on (default: every body is ours) |
now: () => number | the shared clock in seconds (the same on all machines, as NetService's now) |
delay?: number | seconds between a command and its effect (default 0.15, the net's own delay) |
packages/physics-actuators/src/net.ts:14
NetActuators interface
interface NetActuatorscommand(name: string, value: number): void | the commands of a player: sent to all with an effect time, applied here at the same time |
on(name: string): void | |
off(name: string): void | |
fire(name: string): void | |
step(): void | call every fixed step: applies the commands whose time has come |
pending(): number | the commands still waiting for their time |
dispose(): void |
packages/physics-actuators/src/net.ts:25
NetLike interface
interface NetLikeActuators over the net. Duck-typed on NetService, so this package does not depend on the net package. Rules: a force, torque or motor acts only on the machine that owns the body (the others get the body from replication); a spin that is not jammable is a function of the shared clock and runs on every machine. A command from any player goes to everyone with an effect time now() + delay and is applied at that time on every machine, so a switchable spin switches at the same moment everywhere.
readonly id: string | |
send(name: string, data: unknown): void | |
on(name: string, fn: (data: any, from: string) => void): () => void |
packages/physics-actuators/src/net.ts:9
Profile interface
interface Profilevalue(x: number): number | the value at x |
done(x: number): boolean | the profile has ended for good at x (for, a finite pulse, a curve that does not loop) |
pulses(x0: number, x1: number): number | how many pulses fire in [x0, x1): only a pulse has any (an impulse actuator fires on them) |
readonly pulsed: boolean | a profile made of pulses (an impulse actuator fires on each), as against a value that is on and off |
packages/physics-actuators/src/profiles.ts:19
SpinSink interface
interface SpinSinkSomething a spin can drive instead of a body: a drivetrain speed source (Drivetrain.speedSource).
setSpeed(w: number, ramp?: number): void |
packages/physics-actuators/src/index.ts:96
Types
ActuatorKind type
type ActuatorKind = 'force' | 'impulse' | 'torque' | 'motor' | 'spin' | 'blade'actuators. Data that says where a force acts on a body, what it is and how it runs in time: force, impulse, torque and motor, with profiles over time, the angle of a body, the phase of another actuator or a sensor value. Everything acts in the field phase, so it enters the same integration as gravity and drag.
packages/physics-actuators/src/index.ts:13
ActuatorOp type
type ActuatorOp = 'command' | 'on' | 'off' | 'fire'packages/physics-actuators/src/net.ts:22
BindMode type
type BindMode = 'hold' | 'press' | 'toggle' | 'axis' | 'inside' | 'on' | 'off' | 'set'Bindings: what drives an actuator's command without code, and what the actuator gives back to an animation. Four sources, one rule each; all of them end in the same few calls on the actuators capability.
action an input action: `hold` (command while down), `press` (one impulse or cycle), `toggle`, `axis` (the stick or trigger value)
interactable `interact:use` of an entity (a lever, a capstan, a wheel): `toggle` (default), `press`, `on`, `off`
zone `zone:enter` / `zone:exit` of a zone: `inside` (default; command while anyone, or the named actor, is inside)
event any event of the world with a payload filter (`machine:stall`, `anim:notify` ...): `on`, `off`, `toggle`, `press`, `set`value is the command while active (default 1), rest the command otherwise (default 0). animate goes the other way: a number of the actuator (its phase, power ...) is written to a parameter of an entity's animation graph every step, or a trigger of the graph fires on an actuator event.
packages/physics-actuators/src/bindings.ts:15
Frame type
type Frame = 'body' | 'world' | stringpackages/physics-actuators/src/index.ts:34
Interp type
type Interp = 'step' | 'linear' | 'smooth'packages/physics-actuators/src/profiles.ts:7
KindForce type
type KindForce = Vec3 | { force: Vec3; at: Vec3 } | nullA force, or a force at another point than the actuator's (a blade at the end of an oar): the point is in the world.
packages/physics-actuators/src/index.ts:29
ProfileSpec type
type ProfileSpec = | 'const' | { const: number } | { for: { duration: number; inner?: ProfileSpec } } | { delay: { after: number; inner?: ProfileSpec } } | { periodic: { period: number; duty?: number; phase?: number; shape?: Shape } } | { pulse: { period: number; count?: number } } | { curve: { keys: Array<[number, number]>; interp?: Interp; loop?: boolean } } | { ramp: { time: number } }packages/physics-actuators/src/profiles.ts:9
Shape type
type Shape = 'square' | 'sine' | 'half-sine' | 'triangle' | 'saw'Profiles of the plan: a value in -1..1 over one variable x. By default x is the seconds since the actuator was switched on; an
actuator may run its profile over the angle of a body (x = turns / turns, so one cycle is x = 1), the phase of another actuator (x = its cycles)
or a sensor value. A profile is a pure function of x: the same x gives the same value, on every peer and after any number of steps.
packages/physics-actuators/src/profiles.ts:6
SpinMode type
type SpinMode = 'unstoppable' | 'jammable' | 'switchable'packages/physics-actuators/src/index.ts:33