@ferret/flow
wind, fronts, vortices, jets, shelters and channels as one field of moving air and water, without physics.
Flow sources: wind, fronts, vortices, jets, shelters and channels add up to one field
flowAt(position, time, medium) that bodies, vessels, the character and the render all read. No physics and no Babylon here:
every source is a pure function of (position, time), so every peer computes the same field and a game without physics can use it.
Read the guide: how to use it →
Classes
FlowField class
class FlowFieldreadonly sources: Map<string, FlowSourceDef> | |
evaluated: number | how many sources were evaluated by the last at (the reach culling is visible to tests and the budget) |
maxSources: number | the most sources the field takes; none by default |
add(def: FlowSourceDef): void | |
remove(name: string) | |
set(name: string, patch: Partial<FlowSourceDef>) | |
list(): FlowSourceDef[] | the sources of the level (those published by code, with a publisher, are left out: they are not data) |
centreOf(s: FlowSourceDef, t: number): V3 | where a source is centred at time t (its first reference point, moved along its path) |
at(p: V3, t: number, medium: Medium = 'air', skipPublisher?: string): FlowSample | The flow at a point and time, per medium: the sum of the sources, then the shelters reduce it. |
packages/flow/src/index.ts:123
FlowService class
class FlowService implements ServiceThe field as a world service: the clock is the world's, and the commands of the plan section 5 are registered.
readonly name: "flow" | |
readonly field: FlowField | |
world: World | |
init(world: World) | |
unload(): void | The sources a level's block added go with the level. |
load(list: unknown): void | Flow sources of a level's physics.flow list. |
packages/flow/src/index.ts:262
Functions
advectClouds function
function advectClouds(field: FlowField, clouds: V3[], t: number, dt: number): V3[]Move clouds by the wind at their own place for dt; returns the new positions (the input is left alone).
packages/flow/src/view.ts:25
cloudVelocity function
function cloudVelocity(field: FlowField, at: V3, t: number): [number, number]The horizontal velocity at the height of a cloud layer; a cloud drifts with the wind there, a front brings its clouds with it.
packages/flow/src/view.ts:20
dropVelocity function
function dropVelocity(field: FlowField, at: V3, t: number, fall: number): V3The slant of rain or snow: the velocity of a drop falling at fall m/s through the wind there (unit direction and speed).
packages/flow/src/view.ts:36
flowGrid function
function flowGrid(field: FlowField, centre: V3, half: number, step: number, t: number, medium: Medium = 'air'): FlowArrow[]A grid of samples round a centre for the inspector's arrows: half metres each way (x and z) at height centre[1], every step metres.
packages/flow/src/view.ts:77
flowReaches function
function flowReaches(field: FlowField, t: number): FlowReach[]Where each source is now, how far it reaches and (for a vortex) its core: what the inspector draws as rings.
packages/flow/src/view.ts:92
funnels function
function funnels(field: FlowField, t: number): Funnel[]packages/flow/src/view.ts:43
gustAt function
function gustAt(t: number, g: Gust): numberA deterministic gust, −1..1 in time (the one of physics-media).
packages/flow/src/index.ts:59
netFlow function
function netFlow(field: FlowField, net: FlowNetLike, opts: NetFlowOptions): NetFlowpackages/flow/src/net.ts:32
patchWind function
function patchWind(field: FlowField, centre: V3, t: number, o: { calm?: number; full?: number } = {}): PatchWindpackages/flow/src/view.ts:13
pathAt function
function pathAt(path: V3[], speed: number, start: number, t: number): V3The point of a path at time t (moving at speed from start); the last point after the end.
packages/flow/src/index.ts:85
plumeLean function
function plumeLean(field: FlowField, base: V3, t: number, riseSpeed: number): [number, number]How far a plume of smoke or steam leans: the horizontal offset per metre of rise, for a rise speed (wind speed over rise speed).
packages/flow/src/view.ts:30
turbulenceNoise function
function turbulenceNoise(seed: number, p: V3, t: number): numberSeeded turbulence for the picture only (D9): a smooth value noise of position and time, in -1..1, the same everywhere for one seed. The physics never reads it; the render adds amplitude * noise to the flow it draws, so a gale looks ragged without peers disagreeing.
packages/flow/src/view.ts:56
validateFlowSource function
function validateFlowSource(d: unknown, path = 'flow'): string[]packages/flow/src/index.ts:98
waterCurrent function
function waterCurrent(field: FlowField, y = 0, t = 0): (x: number, z: number) => [number, number]A current for a liquid surface (SurfaceDef.current) taken from the water flow at height y and time t.
packages/flow/src/index.ts:293
Interfaces
ChannelSource interface
interface ChannelSource extends BaseA flow along a polyline (a river, a mill race, a canyon wind) with a parabolic cross profile of half-width width.
kind: 'channel' | |
points: V3[] | |
velocity: number | |
width: number |
packages/flow/src/index.ts:45
CurveSource interface
interface CurveSource extends BaseA sampled field (baked from a tool): size cells of cell metres from origin, data holds vx, vy, vz per cell (x fastest, then y, then z); trilinear inside, nothing outside.
kind: 'curve' | |
origin: V3 | |
cell: number | |
size: [number, number, number] | |
data: number[] |
packages/flow/src/index.ts:47
FlowArrow interface
interface FlowArrowpackages/flow/src/view.ts:75
FlowNetLike interface
interface FlowNetLikeThe weather over the net. The flow field is a pure function of position and time, so only its changes travel:
the authority of the level (one machine) sends each change with an effect time now() + delay, and every machine, the authority included,
applies it when the shared clock reaches that time. Nothing else is replicated, so a storm costs a message per change, not per step.
send(name: string, data: unknown): void | |
on(name: string, fn: (data: any, from: string) => void): () => void |
packages/flow/src/net.ts:8
FlowReach interface
interface FlowReachname: string | |
kind: FlowSourceDef['kind'] | |
on: boolean | |
centre: V3 | |
reach?: number | |
core?: number |
packages/flow/src/view.ts:90
FlowSample interface
interface FlowSamplevelocity: V3 | |
updraft?: number | |
turbulence?: number |
packages/flow/src/index.ts:11
FrontSource interface
interface FrontSource extends BaseA plane that travels along direction (horizontal) at speed; behind it the flow goes from before to after over width.
packages/flow/src/index.ts:37
Funnel interface
interface FunnelA funnel to draw: the vortices of the field with their core, height and where they are now.
name: string | |
centre: V3 | |
core: number | |
height: number | |
updraft: number | |
circulation: number |
packages/flow/src/view.ts:42
Gust interface
interface Gustamplitude: number | |
period: number | |
seed?: number |
packages/flow/src/index.ts:34
JetSource interface
interface JetSource extends BaseA cone of flow from a point (a fan, a vent, the wash of a propeller) with exponential decay over distance.
packages/flow/src/index.ts:41
LevelCurve interface
interface LevelCurveA level of a source in time: keys [[t, v], ...] (linear, held at the ends); loop repeats over the last key.
curve: Array<[number, number]> | |
loop?: boolean |
packages/flow/src/index.ts:14
NetFlow interface
interface NetFlowadd(def: FlowSourceDef): void | |
set(name: string, patch: Partial<FlowSourceDef>): void | |
remove(name: string): void | |
step(): void | call every fixed step |
pending(): number | |
dispose(): void |
packages/flow/src/net.ts:22
NetFlowOptions interface
interface NetFlowOptionsauthority: boolean | is this machine the authority of the level's weather (default false: it only receives) |
authorityPeer?: string | the peer id of the authority, when known (otherwise the first sender is taken for it) |
now: () => number | |
delay?: number |
packages/flow/src/net.ts:12
PatchWind interface
interface PatchWindOne wind uniform for a patch of grass or trees: the direction (unit, horizontal), the speed and a 0..1 sway amount.
dir: [number, number] | |
speed: number | |
sway: number | |
gust: number |
packages/flow/src/view.ts:12
ShelterSource interface
interface ShelterSource extends BaseA sphere in the lee of an obstacle where the flow of the other sources is reduced by strength (0..1) and made turbulent.
kind: 'shelter' | |
centre: V3 | |
radius: number | |
strength: number |
packages/flow/src/index.ts:43
SolvedSource interface
interface SolvedSource extends BaseA region where the flow is solved (a flow box, @ferret/flow-solver): inside it the solver's field replaces the analytic sum, blended over a margin.
Code only (it has a function); the box publishes it and the readers see the wake without any change on their side. sampler gets the point, the time and the
analytic velocity there and returns the replaced velocity and its weight 0..1, or null outside the region (or while the box sleeps).
packages/flow/src/index.ts:53
UniformSource interface
interface UniformSource extends Basekind: 'uniform' | |
velocity: V3 | |
profile?: { power: number; refHeight: number; seaLevel?: number } | |
gust?: Gust |
packages/flow/src/index.ts:35
VortexSource interface
interface VortexSource extends BaseA Rankine vortex about a vertical axis: solid rotation inside core, Γ/(2πr) outside; inflow near the ground, an updraft in the core.
kind: 'vortex' | |
centre: V3 | |
core: number | |
circulation: number | |
inflow?: number | |
updraft?: number | |
height?: number |
packages/flow/src/index.ts:39
Types
FlowSourceDef type
type FlowSourceDef = SolvedSource | CurveSource | UniformSource | FrontSource | VortexSource | JetSource | ShelterSource | ChannelSourcepackages/flow/src/index.ts:54
Medium type
type Medium = 'air' | 'water'packages/flow/src/index.ts:10
V3 type
type V3 = [number, number, number]Flow sources: wind, fronts, vortices, jets, shelters and channels add up to one field
flowAt(position, time, medium) that bodies, vessels, the character and the render all read. No physics and no Babylon here:
every source is a pure function of (position, time), so every peer computes the same field and a game without physics can use it.
packages/flow/src/index.ts:9