FERRETGame engine over Babylon.js
Ferret

@ferret/physics-liquids

containers and reservoirs, a height-field water surface, particle liquids, foils, hydraulic circuits (cylinders, valves, pumps, accumulators).

80 exports · source in packages/physics-liquids/src

Classes

Capillary class

class Capillary
rise: numberheight of the liquid in the tube above the reservoir surface (negative: pushed down, mercury in glass)
constructor(readonly def: CapillaryDef)
equilibrium(l: LiquidMaterial, angle: number, g: number)
step(dt: number, net: LiquidNetwork, angle: (liquid: string, solid?: string) => number)

packages/physics-liquids/src/index.ts:55

Fluid class

class Fluid
n: number
x: Float64Array
v: Float64Array
readonly colliders: Collider[]
readonly s: number
readonly H: number
readonly mass: number
readonly rho0: number
readonly density: number
readonly viscosity: number
readonly tension: number
adhesion: number
readonly gravity: Vec3
iterations: number
substeps: number
readonly budget: number
stats: FluidStats
time: number
constructor(d: FluidDef)
add(points: Iterable<Vec3>, velocity: Vec3 = [0, 0, 0])Add particles at the points.
fillBox(min: Vec3, max: Vec3, velocity: Vec3 = [0, 0, 0])Fill a box with particles on the lattice.
fillSphere(c: Vec3, r: number, velocity: Vec3 = [0, 0, 0])Fill a sphere (a drop).
remove(i: number)
position(i: number): Vec3
get volume()Total liquid volume, m³ (particles × the volume each stands for).
densityOf(i: number)Density of the particle i at its last solve, kg/m³ (reported in units of the true density, not the lattice's).
step(dt: number)

packages/physics-liquids/src/particles.ts:65

HydraulicCircuit class

class HydraulicCircuit
readonly nodes: Map<string, Node>
readonly links: Map<string, HydraulicLink>
readonly cylinders: Map<string, Cylinder>
time: number
readonly rho: number
readonly bulk: number
substeps: numbersub-steps used by the last step
residual: numberthe worst scaled residual and the Newton iterations of the last sub-step (a check on the solve)
iterations: number
constructor(readonly liquid: LiquidMaterial, o: { bulk?: number } = {})
tank(id: string): thisan open tank at the atmosphere (a node tank exists from the start)
line(id: string, volume: number, pressure = P_AMB): thisa line or junction holding volume m3 of liquid, at pressure Pa
accumulator(id: string, o: { precharge: number; gas: number; gamma?: number; line?: number; pressure?: number }): thisa gas-charged accumulator on a line: precharge Pa, gas volume at precharge m3, polytropic exponent (1.4 adiabatic, 1 isothermal), plus the line volume
stored(id: string): numberliquid held in an accumulator at its pressure, m3
pump(id: string, node: string, rate: number | (() => number)): thisa constant or time-varying flow into a node (a pump), m3/s
pressure(id: string): number
link(id: string, spec: LinkSpec): HydraulicLink
relief(id: string, line: string, o: { crack: number; cdA: number; range?: number; to?: string }): HydraulicLinka relief valve from line to the tank, set to crack Pa
setOpen(id: string, v: number)set the opening of a valve link (a number replaces a function)
cylinder(id: string, def: CylinderDef): Cylinder
handPump(id: string, o: { bore: number; stroke: number; to: string; cdA?: number; from?: string; dead?: number }): HandPumpa hand pump: a small cylinder driven by a lever (0..1), with an inlet check from the tank and an outlet check to to. lever is the position of the piston as a share of the stroke.
directional(id: string, o: { supply: string; a: string; b: string; cdA: number; tank?: string }): DirectionalValvea four-way, three-position valve: the spool in -1..1; positive sends the pump to a and b to the tank, negative the other way, zero closes all (the load holds)
step(dt: number): void
liquidVolume(): numberTotal liquid in the circuit (compressed and stored), m3: for checks that nothing is made or lost.
fromTank(): numberVolume that has gone into the tank and come out of it, m3, through the links that touch it.

packages/physics-liquids/src/hydraulics.ts:82

LiquidLibrary class

class LiquidLibrary
constructor(initial: Record<string, LiquidMaterial> = EXAMPLE_LIQUIDS)
add(name: string, l: LiquidMaterial)
get(name: string): LiquidMaterial
has(name: string)
names()
contactAngle(liquid: string, solid?: string): numberContact angle in degrees against a solid (90° when the liquid has no entry for it and no default).

packages/physics-liquids/src/liquid.ts:26

LiquidNetwork class

class LiquidNetwork

Reservoirs joined by links: levels, pressures and flows. No particles; exact where the physics is exact (hydrostatics, Bernoulli, Poiseuille).

readonly reservoirs: Map<string, Reservoir>
readonly links: Map<string, Link>
readonly discharged: Map<string, number>liquid that has left the network through open ends, by liquid, m³
constructor(readonly liquids: LiquidLibrary, public g = 9.80665)
addReservoir(def: ReservoirDef): Reservoir
addLink(def: LinkDef): Link
removeReservoir(id: string)
removeLink(id: string)
pressureAt(id: string, y: number): numberPressure at a world y inside a reservoir's liquid, p = p0 + ρ·g·depth.
gateForce(id: string, width: number, yBottom: number, yTop: number): { force: number; centreY: number }Force (N) of the liquid on a vertical rectangular gate of the given width between two world heights, and the depth of its centre of pressure.
step(dt: number, o: StepOptions = {})

packages/physics-liquids/src/network.ts:135

Reservoir class

class Reservoir
readonly id: string
liquid: string
readonly container: Container
readonly position: [number, number, number]
volume: number
pressure: numberpressure over the surface when the tank is open
gas?: GasVolume
level: numbersurface height above the container's bottom (m), with what bodies displace
displaced: numberthe volume that bodies in the tank displace at the current level
full: boolean
empty: boolean
discharged: number
received: number
constructor(def: ReservoirDef)
get surfaceY()
get headPressure()pressure of whatever is over the liquid
get bounds(): { min: [number, number, number]; max: [number, number, number] }the box the liquid fills, for a medium of the physics step

packages/physics-liquids/src/network.ts:22

WaterSurface class

class WaterSurface
readonly nx: number
readonly nz: number
readonly dx: number
readonly origin: [number, number]
readonly level: number
readonly g: number
readonly eta: Float64Arrayelevation of the surface above the rest level, per cell
readonly depth: Float64Arrayrest depth per cell
readonly qx: Float64Arrayflow per unit width (m²/s) through the x faces, (nx+1)·nz, and the z faces, nx·(nz+1)
readonly qz: Float64Array
readonly u0: Float64Arraysteady current per cell
readonly v0: Float64Array
friction: number
readonly nonlinear: boolean
time: number
lastMs: numberwall-clock cost of the last step, ms (for the inspector)
constructor(d: SurfaceDef)
get maxSpeed(): numberThe fastest wave speed on the grid, m/s.
get maxStep(): numberThe largest stable step, s.
height(x: number, z: number): numberHeight of the water surface at a point, m (the rest level where the point is off the grid's cells it clamps to the nearest).
depthAt(x: number, z: number): numberDepth of the water at a point, m.
normal(x: number, z: number): Vec3Surface normal at a point.
velocity(x: number, z: number): [number, number]Velocity of the water at a point: the current plus the flow of the waves, m/s ([x, z]).
disturb(x: number, z: number, volume: number, radius = 2 * this.dx)Add (or take away, when negative) a volume of water around a point, spread over a smooth disc of the radius; at the next step. A body that pushes into the water adds the volume it displaces.
get volume(): numberTotal volume above the rest level, m³.
energy(rho = 1000): numberEnergy of the waves, J per unit density of water ρ = 1000 kg/m³.
field()The heights and normals handed to a surface shader of @ferret/fx: nothing is drawn here.
step(dt: number)Advance by dt, split into steps below the stability limit.

packages/physics-liquids/src/surface.ts:39

Wick class

class Wick
reach: numberhow far the liquid has crept into the wick, m
constructor(readonly def: WickDef)
step(dt: number, net: LiquidNetwork, angle: (liquid: string, solid?: string) => number)

packages/physics-liquids/src/index.ts:73

Functions

adhesionFor function

function adhesionFor: (contactAngleDeg: number) => number

packages/physics-liquids/src/particles.ts:479

bodySdf function

function bodySdf(b: Body): Collider['sdf']

The SDF of a body's shape where it is now (it turns with the body). A hull stands in as its bounding box; a mesh is refused.

packages/physics-liquids/src/fluid.ts:51

boxContainer function

function boxContainer(width: number, depth: number, height: number): Container

packages/physics-liquids/src/container.ts:28

boxSdf function

function boxSdf: (c: Vec3, half: Vec3) => (x: number, y: number, z: number) => number

A solid box (negative inside it).

packages/physics-liquids/src/particles.ts:26

buildCircuit function

function buildCircuit(d: CircuitDef, liquids: Record<string, LiquidMaterial> = EXAMPLE_LIQUIDS): BuiltCircuit

packages/physics-liquids/src/hydraulics-host.ts:63

capVolume function

function capVolume(r: number, s: number): number

Volume of the part of a sphere of radius r below a height s above its bottom.

packages/physics-liquids/src/water.ts:40

containerOf function

function containerOf(s: ContainerSpec): Container

packages/physics-liquids/src/index.ts:96

creep function

function creep(u: number, drive: number, gTerm: number, radius: number, viscosity: number, dt: number): number

How far a liquid has crept after dt in a thin channel of radius R: with the length u, u·du/dt = R²/(8μ)·(drive − g_term·u), which is singular at u = 0 in u but smooth in M = u²: dM/dt = R²/(4μ)·(drive − g_term·√M). drive is the capillary pressure 2γ|cosθ|/R (Pa) and gTerm the weight of the column per metre (ρ·g, or ρ·g·sin α for a tilted wick).

packages/physics-liquids/src/index.ts:48

cylinderContainer function

function cylinderContainer(radius: number, height: number): Container

packages/physics-liquids/src/container.ts:33

foilForce function

function foilForce(f: FoilDef, v: Vec3, flow: Vec3, rho: number, chordDir: Vec3 = f.chord ?? [1, 0, 0], spanDir: Vec3 = f.span ?? [0, 0, 1]): FoilForce

Force on a foil moving with velocity v through a fluid moving with velocity flow (both in the frame of chord and span, which the caller has rotated into place), in a fluid of density rho. The lift is perpendicular to the relative flow and to the span; the drag is along the relative flow.

packages/physics-liquids/src/foil.ts:57

hydraulicForms function

function hydraulicForms(br: FormBridge, cap: HydraulicsCapability): void

packages/physics-liquids/src/hydraulics-host.ts:181

hydraulics function

function hydraulics(): HydraulicsCapability

packages/physics-liquids/src/hydraulics-host.ts:87

jurinHeight function

function jurinHeight(l: LiquidMaterial, angleDeg: number, radius: number, g = 9.80665): number

Jurin's law: the rise of a liquid in a tube of radius r, h = 2·γ·cosθ / (ρ·g·r); negative (a depression) when θ is over 90°.

packages/physics-liquids/src/liquid.ts:47

laplacePressure function

function laplacePressure(l: LiquidMaterial, radius: number, surfaces = 1): number

Laplace pressure inside a drop (one surface) or a bubble in the liquid; a soap bubble has two surfaces: pass surfaces = 2.

packages/physics-liquids/src/liquid.ts:57

liquids function

function liquids(): LiquidsCapability

packages/physics-liquids/src/index.ts:117

magnus function

function magnus(v: Vec3, omega: Vec3, radius: number, rho: number): Vec3

Magnus force on a spinning ball: F = ½ρ·A·Cl·|v|²·(ω̂ × v̂), with Cl rising with the spin ratio r|ω|/|v| and saturating at 0.4. v is the velocity of the ball relative to the fluid.

packages/physics-liquids/src/foil.ts:73

particleLiquid function

function particleLiquid(): FluidCapability

packages/physics-liquids/src/fluid.ts:73

planeSdf function

function planeSdf: (normal: Vec3, offset: number) => (x: number, y: number, z: number) => number

packages/physics-liquids/src/particles.ts:23

profileContainer function

function profileContainer(heights: number[], areas: number[], half?: [number, number]): Container

Any container from its profile: the cross-section area at a list of heights (increasing from 0), linear between them. A funnel, a bottle, a hull's hold; the table can be built once from a closed mesh by slicing it.

packages/physics-liquids/src/container.ts:53

roomSdf function

function roomSdf: (c: Vec3, half: Vec3) => (x: number, y: number, z: number) => number

A box-shaped room: the fluid is kept inside it.

packages/physics-liquids/src/particles.ts:31

sphereContainer function

function sphereContainer(radius: number): Container

A spherical tank, or a round flask.

packages/physics-liquids/src/container.ts:39

sphereSdf function

function sphereSdf: (c: Vec3, r: number) => (x: number, y: number, z: number) => number

packages/physics-liquids/src/particles.ts:24

thinFoil function

function thinFoil(o: { aspect?: number; Cd0?: number; stall?: number; efficiency?: number } = {}): Polar

A flat-plate polar with a stall: thin-airfoil lift 2π·α up to the stall angle, then the post-stall flat plate (Cl = sin 2α), drag from the zero-lift drag plus the induced term Cl²/(π·e·AR).

packages/physics-liquids/src/foil.ts:18

tubeSdf function

function tubeSdf: (x0: number, z0: number, radius: number, y0: number, y1: number) => (x: number, y: number, z: number) => number

A vertical tube: a wall of the radius around the axis (x0, z0), the fluid inside; open at both ends between y0 and y1 (the wall ends there).

packages/physics-liquids/src/particles.ts:33

validateCircuit function

function validateCircuit(d: unknown, path = 'hydraulics'): string[]

packages/physics-liquids/src/hydraulics-host.ts:31

venturiDrop function

function venturiDrop(density: number, v1: number, a1: number, a2: number): number

Pressure drop across a contraction from Bernoulli, Δp = ½·ρ·(v₂² − v₁²), the Venturi effect.

packages/physics-liquids/src/liquid.ts:62

washburnLength function

function washburnLength(l: LiquidMaterial, angleDeg: number, radius: number, t: number): number

Washburn: the distance a liquid has crept into a horizontal pore of radius r after t seconds, L² = γ·r·cosθ·t / (2·μ).

packages/physics-liquids/src/liquid.ts:52

waterSurface function

function waterSurface(): SurfaceCapability

packages/physics-liquids/src/water.ts:45

Interfaces

AttachDef interface

interface AttachDef

The hydraulics capability: circuits of the level, stepped every fixed step in the field phase, cylinders that push two bodies apart along an axis, and the commands and forms to drive the valves.

Level block physics.hydraulics: a list of circuits

  { name, liquid?, bulk?, lines: [{ id, volume, pressure? }], accumulators: [{ id, precharge, gas, gamma?, line? }], pumps: [{ node, rate }],
    cylinders: [{ id, bore, rod, stroke, mass?, ..., attach?: { a, b, axis } }], links: [{ id, from, to, cdA | laminar, open?, check?, relief? }],
    valves: [{ id, supply, a, b, cdA }], handPumps: [{ id, bore, stroke, to }], reliefs: [{ id, line, crack, cdA }] }

where a and b of an attached cylinder are the nodes of two bodies (the barrel on a, the rod on b) and axis the extension direction in the frame of a.

a: string
b: string
axis: Vec3
offset?: number

packages/physics-liquids/src/hydraulics-host.ts:15

BuiltCircuit interface

interface BuiltCircuit

What the capability keeps of a circuit: the circuit, its valves and pumps by name.

name: string
circuit: HydraulicCircuit
valves: Map<string, { id: string; spool: number }>
handPumps: Map<string, { id: string; lever: number; handleForce: number }>
attached: Array<{ cyl: Cylinder; def: AttachDef }>

packages/physics-liquids/src/hydraulics-host.ts:55

CapillaryDef interface

interface CapillaryDef

Capability L1: liquids as reservoirs and pipes. Liquid in a container has a level that follows from its volume; reservoirs are joined by links (pipes, orifices, valves, pumps, check valves) whose flow comes from the pressure difference, with Hagen–Poiseuille in laminar flow and Darcy–Weisbach in turbulent flow, so levels equalise with the right time constant, two liquids in a U-tube balance ρ₁h₁ = ρ₂h₂, a tank drains by Torricelli. A body put in raises the level by what it displaces. A reservoir is also a medium of the capability D (when it is on), so a body in it floats, sinks and drags by the same law as in a lake. Capillary tubes (Jurin) and wicks (Washburn) take liquid from a reservoir.

values: liquids ({name: LiquidMaterial}) over the examples.

id: string
reservoir: string
radius: number
solid?: stringthe solid the tube is made of, for the contact angle
length?: numberthe tube's length above the reservoir surface, m (the liquid stops at the top)

packages/physics-liquids/src/index.ts:24

CircuitCylinder interface

interface CircuitCylinder extends Omit<CylinderDef, 'load'>
id: string
attach?: AttachDef

packages/physics-liquids/src/hydraulics-host.ts:16

CircuitDef interface

interface CircuitDef
name: string
liquid?: string | LiquidMaterial
bulk?: number
lines?: Array<{ id: string; volume: number; pressure?: number }>
accumulators?: Array<{ id: string; precharge: number; gas: number; gamma?: number; line?: number; pressure?: number }>
pumps?: Array<{ node: string; rate: number }>
cylinders?: CircuitCylinder[]
links?: Array<{ id: string } & LinkSpec>
valves?: Array<{ id: string; supply: string; a: string; b: string; cdA: number; tank?: string }>
handPumps?: Array<{ id: string; bore: number; stroke: number; to: string; cdA?: number }>
reliefs?: Array<{ id: string; line: string; crack: number; cdA: number; range?: number }>

packages/physics-liquids/src/hydraulics-host.ts:17

Collider interface

interface Collider

L3: liquid as particles, by position-based fluids: a density constraint per particle solved with a fixed number of iterations over a neighbour grid (incompressible, so a still column holds ρ·g·h), XSPH viscosity mapped from the material's viscosity (water to honey), cohesion between particles (Akinci) that rounds drops up, and adhesion to solids that sets the contact angle. Solids are signed distance functions, so static meshes, boxes, spheres and tubes all work the same way, and moving bodies push back through the same call. Fixed step, fixed iteration count and no randomness: a replay is identical.

sdf(x: number, y: number, z: number): numbersigned distance to the solid: negative inside it (where a particle may not be), positive outside
friction?: numberfriction against the wall, 0..1 (velocity along the wall lost per contact)
adhesion?: numberadhesion of this wall as a share of the liquid's cohesion (see adhesionFor); the fluid's default when missing
onPush?(point: Vec3, impulse: Vec3): voida moving solid reports what the fluid did to it: the impulse of one correction, N·s (sum over a step / dt is the force)
velocity?(x: number, y: number, z: number): Vec3velocity of the wall at a point (a moving body drags the fluid along); still when missing

packages/physics-liquids/src/particles.ts:10

Container interface

interface Container

A container shape for a reservoir: how much liquid fits up to a height. The surface level follows from the volume by inverting volume(h); the cross-section area(h) is what a level rises against.

readonly height: numberinner height, m
readonly half: [number, number]horizontal half extents of the footprint (x, z), m: where bodies count as inside, and the box of the water
area(h: number): numbercross-section at height h, m²
volume(h: number): numberliquid volume up to height h, m³
heightOf(v: number): numberheight reached by volume v (clamped to 0..height)
readonly capacity: number

packages/physics-liquids/src/container.ts:5

Cylinder interface

interface Cylinder
readonly id: string
readonly def: Required<Pick<CylinderDef, 'bore' | 'rod' | 'stroke' | 'dead' | 'mass' | 'coulomb' | 'viscous' | 'leak' | 'damping'>> & CylinderDef
x: number
v: number
cap: stringnodes of the two chambers: id.cap and id.rod
rod: string
force: numberforce of the liquid on the piston along the extension, N, and the force the piston gives the load
atStop: 0 | 1 | -1the extension is held at an end stop
area: number
rodSide: number
attach?: { read(): { x: number; v: number }; push(f: number): void }external extension and speed from bodies, when attached
setLoad(f: ((x: number, v: number, t: number) => number) | undefined): voidthe load given by bodies is the one that moves the piston: set mass: 0 and attach

packages/physics-liquids/src/hydraulics.ts:65

CylinderDef interface

interface CylinderDef
bore: numberpiston bore and rod diameter, m
rod: number
stroke: numberstroke, m
dead?: numberdead volume in each chamber, m3 (default a tenth of the swept volume)
mass?: numbermoving mass (piston and rod), kg; 0 with attach: the bodies carry the mass
coulomb?: numberseal friction: Coulomb N and viscous N·s/m
viscous?: number
damping?: numberdamping of the liquid column as a share of critical (default 0.15): the bulk viscosity of the oil and the give of the hoses take the ringing out of the load on its oil spring
leak?: numberleak across the piston from the cap side to the rod side, m3/s per Pa
x?: numberstarting extension, m
load?: (x: number, v: number, t: number) => numbera force on the rod along the extension (a load, gravity, a spring), N, from the extension and speed

packages/physics-liquids/src/hydraulics.ts:14

DirectionalValve interface

interface DirectionalValve
id: string
spool: number

packages/physics-liquids/src/hydraulics.ts:359

Drain interface

interface Drain extends DrainDef
taken: number

packages/physics-liquids/src/fluid.ts:35

DrainDef interface

interface DrainDef
into?: Reservoirthe reservoir that takes the liquid back; without one the liquid leaves the scene
sdf: (x: number, y: number, z: number) => numberthe region that swallows particles: positive outside it, negative inside (an SDF)

packages/physics-liquids/src/fluid.ts:29

Floater interface

interface Floater extends FloaterDef
volume: number

packages/physics-liquids/src/water.ts:26

FloaterDef interface

interface FloaterDef
body: Body
radius: numberradius of the sphere that stands for the body's displacement, m
Cd?: numberdrag coefficient against the water's velocity, 1 for a rough sphere
push?: booleanfalse: the body does not push the water

packages/physics-liquids/src/water.ts:17

FluidCapability interface

interface FluidCapability extends Capability
readonly fluids: Map<string, Fluid>
add(name: string, def: FluidDef): Fluid
couple(name: string, body: Body, opts?: { friction?: number; adhesion?: number }): ColliderA kernel body becomes a moving solid of the liquid; a dynamic one is pushed back.
pour(name: string, def: PourDef): Pour
drain(name: string, def: DrainDef): Drain
view(name: string): FluidView

packages/physics-liquids/src/fluid.ts:40

FluidDef interface

interface FluidDef
spacing: numberparticle spacing at rest, m
density?: numberkg/m³
viscosity?: numberdynamic viscosity, Pa·s
surfaceTension?: numberN/m: drives the cohesion between particles
adhesion?: numberadhesion to walls as a share κ of the liquid's own cohesion (0 beads up, 1 spreads; see adhesionFor for a contact angle)
iterations?: number
substeps?: number
gravity?: Vec3
budget?: numbermost particles the solver will step; beyond it, the solver reduces its iterations and says so

packages/physics-liquids/src/particles.ts:40

FluidStats interface

interface FluidStats
particles: number
iterations: number
substeps: number
degraded: boolean
neighbours: number
ms: number

packages/physics-liquids/src/particles.ts:58

FluidView interface

interface FluidView

What a renderer of @ferret/fx draws: positions and speeds of the particles, and the spacing (a particle's size).

count: number
spacing: number
positions: Float32Array
speeds: Float32Array

packages/physics-liquids/src/fluid.ts:38

FoilDef interface

interface FoilDef
polar: Polar
area: numberplanform area, m²
chord?: Vec3unit vectors in the body frame: along the chord (leading edge forward) and along the span
span?: Vec3

packages/physics-liquids/src/foil.ts:34

FoilForce interface

interface FoilForce
force: Vec3force on the foil, N, in the same frame as the inputs
lift: number
drag: number
alpha: numberangle of attack, rad

packages/physics-liquids/src/foil.ts:43

FormBridge interface

interface FormBridge

The words of the language for hydraulics: (spool "circuit" "valve" :extend|:hold|:retract|number), (valve "circuit" "link" x), (cylinder-position "circuit" "cyl"), (cylinder-force "circuit" "cyl"). define is the bridge's define.

define(name: string, sig: { args: string[]; returns?: string }, fn: (ctx: unknown, ...args: any[]) => unknown): unknown

packages/physics-liquids/src/hydraulics-host.ts:180

HandPump interface

interface HandPump
id: string
lever: number
last: number
area: number
node: string
handleForce: number
delivered: number

packages/physics-liquids/src/hydraulics.ts:358

HydraulicNodeInfo interface

interface HydraulicNodeInfo
id: string
pressure: number
kind: string

packages/physics-liquids/src/hydraulics.ts:37

HydraulicsCapability interface

interface HydraulicsCapability extends Capability
readonly circuits: Map<string, BuiltCircuit>
add(def: CircuitDef): BuiltCircuit
remove(name: string): void

packages/physics-liquids/src/hydraulics-host.ts:81

LinkDef interface

interface LinkDef
id: string
from: string | 'air'
to: string | 'air'
fromElevation: numberworld y of each end of the pipe
toElevation: number
diameter: numberpipe bore (m) and length (m); an orifice ignores the length
length?: number
roughness?: numberabsolute roughness of the wall (m)
loss?: numbersum of minor-loss coefficients (bends, entrance)
valve?: number0..1 open fraction of a valve
pump?: { pressure?: number; rate?: number }pump: adds pressure (Pa) to the flow from → to, or forces a rate (m³/s)
check?: booleana check valve lets liquid pass from → to only
orifice?: { cd?: number; area?: number }a sharp opening instead of a pipe: Torricelli with a discharge coefficient
main?: { liquid: string; pressure: number }a main (a supply at pressure) in place of from
profile?: Array<{ s: number; diameter: number }>pipe diameter along the length, for pressure taps: [{s, diameter}], s from 0 to length

packages/physics-liquids/src/network.ts:62

LinkEnd interface

interface LinkEnd
reservoir: string | 'air'
elevation: number

packages/physics-liquids/src/network.ts:60

LinkSpec interface

interface LinkSpec
from: string
to: string
cdA?: numberdischarge coefficient times the flow area, m2 (the orifice law Q = cdA·sqrt(2·dp/rho))
laminar?: numbera laminar leak instead of an orifice: m3/s per Pa
open?: number | (() => number)0..1 now (a number, or a function of time for a valve driven from outside)
check?: booleanpasses only from from to to
relief?: { crack: number; range?: number }a relief valve: opens from crack Pa over range Pa (of the pressure at from)

packages/physics-liquids/src/hydraulics.ts:50

LiquidMaterial interface

interface LiquidMaterial

A liquid as data: the same record for a tank, a pipe, a wick and a splash.

density: numberkg/m³
viscosity: numberdynamic viscosity, Pa·s
surfaceTension: numberN/m
compressibility?: number1/Pa; 0 = incompressible
contactAngle?: Record<string, number>degrees against a solid material by name; default for the rest (water on glass is near 0°, mercury on glass near 140°)
henry?: Record<string, number>Henry's constants for dissolved gases by species, mol/(m3·Pa) at 298 K: the dissolved amount at equilibrium is k·(partial pressure) per m3 of liquid (water's come from the aqueous set of physics-chemistry, which also holds the temperature dependence)

packages/physics-liquids/src/liquid.ts:3

LiquidsCapability interface

interface LiquidsCapability extends Capability
net: LiquidNetwork
liquids: LiquidLibrary
tubes: Map<string, Capillary>
wicks: Map<string, Wick>
addReservoir(host: PhysicsHost | null, def: ReservoirDef, asMedium?: boolean): Reservoir
addTube(def: CapillaryDef): Capillary
addWick(def: WickDef): Wick
advance(dt: number, g: number, host?: PhysicsHost | null): voidAdvance by dt with a given gravity (also what the capability's step does).

packages/physics-liquids/src/index.ts:105

Polar interface

interface Polar

Lift and drag of a wing, sail, keel, fin or rudder from coefficients of the angle of attack, against the local flow. This is the Bernoulli effect as a game uses it, not a solved flow around the shape.

Cl(alpha: number): numberlift coefficient at an angle of attack α, rad (odd in α for a symmetric foil)
Cd(alpha: number): numberdrag coefficient (even in α)

packages/physics-liquids/src/foil.ts:7

Pour interface

interface Pour extends PourDef
owed: number
poured: number

packages/physics-liquids/src/fluid.ts:27

PourDef interface

interface PourDef
from?: Reservoirthe L1 reservoir the liquid comes from; without one the pour makes liquid (a tap on the mains)
at: Vec3centre of the nozzle and the velocity the liquid leaves it with
velocity: Vec3
radius: numbernozzle radius, m
rate: numberm³/s

packages/physics-liquids/src/fluid.ts:16

ReservoirDef interface

interface ReservoirDef
id: string
liquid: string
container: Container
position?: [number, number, number]the middle of the container's bottom, world metres
volume?: numberinitial liquid volume (m³), or initial level (m)
level?: number
pressure?: numberpressure over the surface (Pa); the atmosphere unless the tank is closed
closed?: booleana closed tank has a gas head whose pressure follows the volume it has left (isothermal, p·V = const)

packages/physics-liquids/src/network.ts:7

StepOptions interface

interface StepOptions
g?: numbergravity (m/s²)
displacement?: (r: Reservoir, surfaceY: number) => numbervolume that bodies displace in a reservoir whose liquid surface is at y
emit?: (name: string, data: Record<string, unknown>) => voidcalled for events: 'level', 'empty', 'full', 'overflow'

packages/physics-liquids/src/network.ts:125

SurfaceCapability interface

interface SurfaceCapability extends Capability
readonly surfaces: Map<string, WaterSurface>
readonly floaters: Map<Body, Floater>
add(name: string, def: SurfaceDef): WaterSurface
float(def: FloaterDef): Floater
height(x: number, z: number): number | nullHeight of the water at a point (the first surface that covers it), or null off every surface.
cost_ms(): numberWall-clock cost of the surfaces' last updates, ms.

packages/physics-liquids/src/water.ts:28

SurfaceDef interface

interface SurfaceDef

L2: the surface of a lake, a harbour or a river as a height field on a grid, by the linearised shallow-water equations on a staggered grid (heights at cell centres, flows through the faces), stepped forward-backward so it is stable up to a Courant number of about 0.7. Waves travel at √(g·h) in the local depth, reflect from the banks and the walls, shoal in shallow water and are eaten by a sponge at the open edges. A steady current carries the waves and anything floating on them. The grid is not a flow solver around bodies: bodies push it with disturb and read it with height and velocity.

nx: numbercells along x and z
nz: number
dx: numbercell size, m
origin?: [number, number]world position of the corner (min x, min z) of the grid
level?: numberheight of the rest surface, m
depth?: number | ((x: number, z: number) => number)depth of the water at rest, m; a number or a function of the world x, z (a beach is a depth that reaches 0)
current?: [number, number] | ((x: number, z: number) => [number, number])steady current, m/s; a vector or a function of x, z
friction?: numberlinear friction on the flow, 1/s
open?: Array<'minX' | 'maxX' | 'minZ' | 'maxZ'>cells of sponge at the edges named here (open water); the other edges are walls
spongeCells?: number
gravity?: number
nonlinear?: booleankeep the momentum advection of the shallow-water equations (the flow carries its own momentum: u ∂q/∂x...): a wave of finite height then travels at about √(g·h)·(1 + 3a/2h), so crests catch the troughs and a front steepens. Off: the linear waves, cheaper and unconditionally gentle.

packages/physics-liquids/src/surface.ts:10

WickDef interface

interface WickDef
id: string
reservoir: string
poreRadius: numberpore radius, m
solid?: string
area?: numbercross-section of the wick, m², and the fraction of it that is pore
porosity?: number
length?: numberlength of the wick, m
angle?: numberangle from horizontal in degrees: 0 creeps along a level wick, 90 straight up against gravity

packages/physics-liquids/src/index.ts:31

Types

ContainerSpec type

type ContainerSpec = | { kind: 'box'; size: [number, number, number] } | { kind: 'cylinder'; radius: number; height: number } | { kind: 'sphere'; radius: number } | { kind: 'profile'; heights: number[]; areas: number[]; half?: [number, number] }

packages/physics-liquids/src/index.ts:91

Constants

EXAMPLE_LIQUIDS const

const EXAMPLE_LIQUIDS: Record<string, LiquidMaterial>

packages/physics-liquids/src/liquid.ts:18

FLUID_DEFAULTS const

const FLUID_DEFAULTS: { budget: number; }

Capability L3: particle liquids stepped with the world. A kernel body joins a liquid as a solid that moves (the liquid takes its velocity at the wall) and is pushed back (the pressure on it is an impulse on the body, so a ball floats, a paddle splashes). An L1 reservoir stays the truth of the volume (section 8, decision 7): a pour takes its volume out as particles, a drain puts what reaches it back, and nothing is made or lost on the way.

values (each at world / level / zone / entity scope):

  budget    most particles stepped at full quality per liquid   (6000)

packages/physics-liquids/src/fluid.ts:14

P_ATM const

const P_ATM: 101325

packages/physics-liquids/src/network.ts:5

SURFACE_DEFAULTS const

const SURFACE_DEFAULTS: { rate: number; waterDensity: number; }

Capability L2: water surfaces stepped with the world at a lower rate, and the bodies that float on them. A floater gets buoyancy from the volume of its sphere below the surface at its own position (waves lift and tilt it), drag against the water's velocity (the current and the flow of the waves carry it) and pushes the water by the volume it displaces.

values (each at world / level / zone / entity scope):

  rate           surface updates per second   (30)
  waterDensity   kg/m³                        (1000)

The surface of a harbour is read by others through height(x, z); vehicles().surface = water.height makes ships ride the waves.

packages/physics-liquids/src/water.ts:15