FERRETGame engine over Babylon.js
Ferret

@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.

37 exports · source in packages/flow/src

Classes

FlowField class

class FlowField
readonly sources: Map<string, FlowSourceDef>
evaluated: numberhow many sources were evaluated by the last at (the reach culling is visible to tests and the budget)
maxSources: numberthe 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): V3where 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): FlowSampleThe 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 Service

The 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(): voidThe sources a level's block added go with the level.
load(list: unknown): voidFlow 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): V3

The 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): number

A 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): NetFlow

packages/flow/src/net.ts:32

patchWind function

function patchWind(field: FlowField, centre: V3, t: number, o: { calm?: number; full?: number } = {}): PatchWind

packages/flow/src/view.ts:13

pathAt function

function pathAt(path: V3[], speed: number, start: number, t: number): V3

The 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): number

Seeded 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 Base

A 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 Base

A 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 FlowArrow
at: V3
velocity: V3
speed: number
updraft?: number

packages/flow/src/view.ts:75

FlowNetLike interface

interface FlowNetLike

The 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 FlowReach
name: string
kind: FlowSourceDef['kind']
on: boolean
centre: V3
reach?: number
core?: number

packages/flow/src/view.ts:90

FlowSample interface

interface FlowSample
velocity: V3
updraft?: number
turbulence?: number

packages/flow/src/index.ts:11

FrontSource interface

interface FrontSource extends Base

A plane that travels along direction (horizontal) at speed; behind it the flow goes from before to after over width.

kind: 'front'
origin: V3
direction: V3
speed: number
width: number
before?: V3
after: V3

packages/flow/src/index.ts:37

Funnel interface

interface Funnel

A 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 Gust
amplitude: number
period: number
seed?: number

packages/flow/src/index.ts:34

JetSource interface

interface JetSource extends Base

A cone of flow from a point (a fan, a vent, the wash of a propeller) with exponential decay over distance.

kind: 'jet'
origin: V3
direction: V3
velocity: number
halfAngle?: number
decay?: number

packages/flow/src/index.ts:41

LevelCurve interface

interface LevelCurve

A 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 NetFlow
add(def: FlowSourceDef): void
set(name: string, patch: Partial<FlowSourceDef>): void
remove(name: string): void
step(): voidcall every fixed step
pending(): number
dispose(): void

packages/flow/src/net.ts:22

NetFlowOptions interface

interface NetFlowOptions
authority: booleanis this machine the authority of the level's weather (default false: it only receives)
authorityPeer?: stringthe 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 PatchWind

One 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 Base

A 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 Base

A 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).

kind: 'solved'
sampler: (p: V3, t: number, analytic: V3) => { velocity: V3; weight: number } | null

packages/flow/src/index.ts:53

UniformSource interface

interface UniformSource extends Base
kind: 'uniform'
velocity: V3
profile?: { power: number; refHeight: number; seaLevel?: number }
gust?: Gust

packages/flow/src/index.ts:35

VortexSource interface

interface VortexSource extends Base

A 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 | ChannelSource

packages/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