import { d } from '../../gpu/kit/index'; type AtomicU32 = d.atomicU32; /** A per-agent buffer of four numbers each (`agents`, `pos`, `vel`, …). */ export type Vec4StateArray = d.v4f[]; /** A push on a 3D agent, from its position, velocity and per-agent seed. */ export type AgentForce3 = (pos: d.v3f, vel: d.v3f, seed: number) => d.v3f; /** The signed distance to a shape's surface at a shape-local 3D point, negative inside. Build one with `shapeField`. */ export type ShapeField3 = (pos: d.v3f) => number; interface VolumeParamsView { readonly dt: number; readonly time: number; readonly spread: number; readonly agitation: number; readonly dragMul: number; readonly gravX: number; readonly gravY: number; readonly cursorX: number; readonly cursorY: number; readonly cursorForce: number; readonly cursorRadSq: number; readonly omegaX: number; readonly omegaY: number; readonly omegaZ: number; readonly entrain: number; readonly gridOffX: number; readonly gridOffY: number; readonly gridOffZ: number; } /** What a 3D swarm's layout must name: `pos`, `vel`, the `dens` grid and the `params` values. */ export interface VolumeSwarmLayout { readonly $: { readonly pos: Vec4StateArray; readonly vel: Vec4StateArray; readonly dens: AtomicU32[]; readonly params: VolumeParamsView; }; } /** What `force.pressure` returns: the force, plus the two density-grid steps and the grid's cell count for the program. */ export interface PressureForce { force: AgentForce3; /** Clears the density grid. Dispatch it `fixed` over `cells`, first in the program. */ clearKernel: (i: number) => void; /** Adds each agent to the density grid. Dispatch it over `agents`, before the update. */ splatKernel: (i: number) => void; /** The density grid's cell count (the clear dispatch size). */ cells: number; } /** * A push away from crowding, so a swarm fills its shape evenly. Agents are counted into a * coarse 3D grid every frame and slide down its density. * * `gridDim` is the grid's side in cells, `domain` the half-width it covers in shape-local * units. Strength comes from `params.spread`. Put `clearKernel` and `splatKernel` in the * program before the update step. */ declare function pressure(layout: VolumeSwarmLayout, cfg: { gridDim: number; /** The grid covers shape-local [−domain, domain]³. */ domain: number; names: { clear: string; splat: string; }; }): PressureForce; /** * Everything that keeps a swarm inside its shape, as one force: a spring wall at the surface, * a pull back from far away, a pull toward the middle where the surface is ambiguous, and a * drag that carries agents along when the shape rotates. * * `field` is the shape's distance function (see `shapeField`). Distances and feathers are in * shape-local units, where the shape spans −0.5 to 0.5. */ declare function containment(layout: VolumeSwarmLayout, cfg: { field: ShapeField3; /** How far apart the two samples of the field's slope are taken. 0.02 is typical. */ gradEps: number; /** The spring at the surface: fades in from `featherFrom` to `featherTo` (signed distance), `base` push plus `springK` per unit outside. */ wall: { featherFrom: number; featherTo: number; base: number; springK: number; }; /** The pull home once an agent is `from`..`to` from the origin, with strength `k`. */ recall: { from: number; to: number; k: number; }; /** The pull toward the middle where the signed distance is `from`..`to`, with strength `k`. */ homing: { from: number; to: number; k: number; }; /** How wide a band around the surface the rotation drag fades over. */ entrainment: { featherHalf: number; }; }): AgentForce3; /** The cursor as a magnet on a 3D swarm: a push away from the pointer (or a pull, when `params.cursorForce` is negative) within `params.cursorRadSq`. */ declare function cursorXY(layout: VolumeSwarmLayout): AgentForce3; /** A constant pull in the direction of `params.gravX, gravY`, already rotated into the shape's space on the CPU. */ declare function gravity(layout: VolumeSwarmLayout): AgentForce3; /** * A random jitter per agent that keeps a swarm alive, scaled by `params.agitation`. * * Keep the scale constants large. Small ones make the noise smooth and the whole swarm drifts * in lockstep. */ declare function turbulence(layout: VolumeSwarmLayout, cfg: { posScale: number; timeX: number; timeY: number; seedScale: number; gain: number; }): AgentForce3; /** @internal Fold a declared 3D force list into one part, preserving the declared sum order. The integrator does this for you. */ export declare function composeForces3(parts: AgentForce3[]): AgentForce3; /** * The update step for a 3D swarm: sum the forces, apply drag (`params.dragMul`) and a speed * limit, move, and never let an agent past `posClamp` from the origin. * * Velocity's fourth value is the speed, which the volume render reads for its color ramp. */ declare function forces3d(layout: VolumeSwarmLayout, cfg: { forces: AgentForce3[]; maxSpeed: number; posClamp: number; name: string; }): (i: number) => void; /** A steering push on a 2D agent, from its position, velocity and its own top speed. */ export type AgentSteer2 = (pos: d.v2f, vel: d.v2f, maxSpeedI: number) => d.v2f; interface SteeringParamsView { readonly count: number; readonly dt: number; readonly domainX: number; readonly maxSpeed: number; readonly maxForce: number; readonly perceptionSq: number; readonly sepRadiusSq: number; readonly sepW: number; readonly aliW: number; readonly cohW: number; readonly cursorX: number; readonly cursorY: number; readonly cursorMode: number; readonly cursorRadius: number; readonly cursorRadiusSq: number; readonly cursorForce: number; readonly margin: number; readonly turnForce: number; } /** What a flock's layout must name: `agents`, the `agit` excitement buffer and the `params` values. */ export interface SteeringLayout { readonly $: { readonly agents: Vec4StateArray; readonly agit: number[]; readonly params: SteeringParamsView; }; } /** * Flocking: each agent keeps its distance from close neighbours, matches the heading of the * ones it can see, and drifts toward their centre. * * Weights and radii are live values: `params.sepW`, `aliW`, `cohW`, `perceptionSq`, * `sepRadiusSq`. `maxAgents` is the buffer size. Distances are in canvas heights. */ declare function flocking(layout: SteeringLayout, cfg: { maxAgents: number; }): AgentSteer2; /** The cursor on a flock: `params.cursorMode` 1 attracts, 2 scatters like a predator, 0 does nothing, fading to zero at `params.cursorRadius`. */ declare function cursorSteer(layout: SteeringLayout): AgentSteer2; /** A gentle turn back toward the canvas within `params.margin` of an edge, so the flock arcs away instead of bouncing. */ declare function wallTurn(layout: SteeringLayout): AgentSteer2; /** @internal Fold a declared steering list into one part, preserving the declared sum order. The integrator does this for you. */ export declare function composeSteer2(parts: AgentSteer2[]): AgentSteer2; /** * The update step for a flock: sum the steering, keep every agent cruising between * `cruiseFloor` and its own top speed, and bounce off the canvas edges as a last resort. * * It also keeps an excitement value per agent that jumps when an agent steers hard and cools * over time, which the oriented render turns into the rest-to-excited color. */ declare function steering2d(layout: SteeringLayout, cfg: { forces: AgentSteer2[]; /** Excitement: effort above `rest` (in max-force units) charges by `gain`, and cools at `cool` per second. */ agitation: { rest: number; gain: number; cool: number; }; /** Slowest cruise, as a fraction of each agent's top speed. */ cruiseFloor: number; /** Speed kept when an agent bounces off an edge (0–1). */ wallRestitution: number; name: string; }): (i: number) => void; /** * What a field sample carries: the direction away from the source (`rhat`), the `strength`, * and the line angle `phi` in radians. Build one when writing your own `AgentFieldAt`. * * @example * ```ts * return FieldSample({rhat, strength: fieldStr, phi: std.atan2(B.y, B.x)}) * ``` * @see field.dipoleOrRadial, torque */ export declare const FieldSample: d.WgslStruct<{ rhat: d.Vec2f; strength: d.F32; phi: d.F32; }>; /** A field sample's value: `rhat`, `strength`, `phi`. */ export type FieldSampleValue = d.Infer; /** A turn on a turning agent, from the field at its position, its angle and its index. */ export type AgentTorque = (fld: FieldSampleValue, theta: number, fi: number) => number; /** A sample of a field at a 2D position. */ export type AgentFieldAt = (pos: d.v2f) => FieldSampleValue; /** An agent's resting position, from its index. */ export type AgentHome2 = (fi: number) => d.v2f; interface OrientationParamsView { readonly dt: number; readonly cursorX: number; readonly cursorY: number; readonly axisX: number; readonly axisY: number; readonly fieldType: number; readonly restMode: number; readonly strength: number; readonly reachSq: number; readonly alignK: number; readonly damping: number; readonly restK: number; readonly pullK: number; readonly homeK: number; readonly omegaRef: number; readonly agitCool: number; readonly gridCols: number; readonly cellW: number; readonly cellH: number; readonly jitter: number; } /** What a turning-agent layout must name: `agents`, the `agit` excitement buffer and the `params` values. */ export interface OrientationLayout { readonly $: { readonly agents: Vec4StateArray; readonly agit: number[]; readonly params: OrientationParamsView; }; } /** * The field of a magnet at the cursor: a bar magnet along the pointer's travel direction * (`params.fieldType` 0) or a single pole (1), fading with distance by `params.reachSq`. */ declare function dipoleOrRadial(layout: OrientationLayout): AgentFieldAt; /** @internal Each agent's rest orientation (radians): random per agent (mode 0), horizontal (1), vertical (2). Used by `torque.rest`. */ export declare const restOrientationAngle: import('typegpu').TgpuFn<(fi: d.F32, restMode: d.F32) => d.F32>; /** A turn onto the field's line, like a compass needle, with strength `params.alignK` times the field's strength. */ declare function alignToField(layout: OrientationLayout): AgentTorque; /** A weak turn back to each agent's rest angle (`params.restMode`: 0 random, 1 horizontal, 2 vertical), felt only where the field is faint. */ declare function restTorque(layout: OrientationLayout): AgentTorque; /** @internal Fold a declared torque list into one part, preserving the declared sum order. The integrator does this for you. */ export declare function composeTorques(parts: AgentTorque[]): AgentTorque; /** * A resting position for each agent on a jittered grid that runs `overscan` past the canvas * on every side, so agents pulled inward are backfilled from off screen. * * Reads `params.gridCols`, `cellW`, `cellH` (from `agentFrame.fitJitteredGrid`) and `jitter`. */ declare function jitteredGridHome(layout: OrientationLayout, cfg: { overscan: number; }): AgentHome2; /** * The update step for agents that turn in place: sample the field once at each agent's home, * sum the torques, spin with angular drag (`params.damping`), and let the agent drift a little * toward the field and spring back home, never more than `maxOffset` away. * * Fast spinning charges the excitement value the oriented render colors by. */ declare function orientation2d(layout: OrientationLayout, cfg: { home: AgentHome2; fieldAt: AgentFieldAt; torques: AgentTorque[]; maxOffset: number; name: string; }): (i: number) => void; interface DriftParamsView { readonly dt: number; readonly domainX: number; readonly dragMul: number; readonly cursorX: number; readonly cursorY: number; readonly cursorRadSq: number; readonly cursorForce: number; } /** What a drifting-mote layout must name: `agents` and the `params` values. */ export interface DriftLayout { readonly $: { readonly agents: Vec4StateArray; readonly params: DriftParamsView; }; } /** A built-in velocity for an agent, from its index alone. */ export type AgentDrift = (fi: number) => d.v2f; /** Params view for the varied-heading drift part. */ interface HeadingDriftParamsView { readonly driftBase: number; readonly angleRad: number; readonly speedVar: number; readonly angleVarRad: number; } /** * A shared drift with per-agent variety: every mote moves along `params.angleRad` at * `params.driftBase`, each varied by its own hashed amount up to `params.angleVarRad` * (radians, ± half the range) and `params.speedVar` (± half). */ declare function variedHeading(layout: { readonly $: { readonly params: HeadingDriftParamsView; }; }): AgentDrift; /** A puff from the cursor on drifting motes: a push within `params.cursorRadSq` scaled by `params.cursorForce`, nothing at all while the force is 0. */ declare function cursorGust(layout: DriftLayout): (pos: d.v2f) => d.v2f; /** * The update step for drifting motes: add the gust, let it fade by `params.dragMul`, move by * the drift plus the gust, and wrap around the canvas edges so coverage stays even. */ declare function drift2d(layout: DriftLayout, cfg: { drift: AgentDrift; gust: (pos: d.v2f) => d.v2f; name: string; }): (i: number) => void; interface ReliefParamsView { readonly rowX: d.v4f; readonly rowY: d.v4f; readonly rowZ: d.v4f; readonly dt: number; readonly snap: number; readonly gridW: number; readonly gridH: number; readonly depth: number; readonly dragMul: number; readonly aspect: number; readonly zoom: number; readonly transX: number; readonly transY: number; readonly pointerX: number; readonly pointerY: number; readonly cursorForce: number; readonly cursorZForce: number; readonly cursorRadSq: number; } /** What an image-relief layout must name: `pos`, `vel`, `col`, the child picture `src` and the `params` values. */ export interface ReliefLayout { readonly $: { readonly pos: Vec4StateArray; readonly vel: Vec4StateArray; readonly col: Vec4StateArray; readonly src: d.Infer>; readonly params: ReliefParamsView; }; } /** One number in 0–1 read out of a color and its alpha: a brightness, a channel, a saturation. */ export type AgentChannel = (rgb: d.v3f, a: number) => number; /** * Which part of the child's color drives a particle: `luminance`, `luminanceInverted`, `red`, * `green`, `blue`, `saturation` or `alpha`, each a number in 0–1. `byMode` maps a select * prop's number (0 luminance … 6 alpha) to the same functions. * * @example * ```ts * integrator.relief(fieldLayout, {channel: channel.byMode[channelMode] ?? channel.luminance, cursor: force.cursorInView(fieldLayout, {perspK: 0.9}), stiffness: 18, maxSpeed: 4, minAlpha: 0.02, name: 'reliefUpdate'}) * ``` * @see integrator */ export declare const channel: { /** * The color's brightness, 0 black to 1 white. * * @example * ```ts * channel: channel.luminance * ``` */ readonly luminance: AgentChannel; /** * One minus the brightness, so dark areas rise. * * @example * ```ts * channel: channel.luminanceInverted * ``` */ readonly luminanceInverted: AgentChannel; /** * The red channel. * * @example * ```ts * channel: channel.red * ``` */ readonly red: AgentChannel; /** * The green channel. * * @example * ```ts * channel: channel.green * ``` */ readonly green: AgentChannel; /** * The blue channel. * * @example * ```ts * channel: channel.blue * ``` */ readonly blue: AgentChannel; /** * How colorful the pixel is, 0 grey to 1 pure. * * @example * ```ts * channel: channel.saturation * ``` */ readonly saturation: AgentChannel; /** * The pixel's alpha. * * @example * ```ts * channel: channel.alpha * ``` */ readonly alpha: AgentChannel; /** * The same seven by a select prop's number: 0 luminance, 1 inverted, 2 red, 3 green, * 4 blue, 5 saturation, 6 alpha. * * @example * ```ts * channel: channel.byMode[channelMode] ?? channel.luminance * ``` */ readonly byMode: Record; }; /** * The cursor on an image relief seen through a camera: the push is worked out where the * particle appears on screen, so the cursor moves what is actually under it at any camera * angle. `perspK` is the projection strength, the same value the relief render uses. */ declare function cursorInView(layout: ReliefLayout, cfg: { perspK: number; }): (homeW: d.v2f, off: d.v2f, z: number) => d.v3f; /** * The update step for particles standing on an image: each particle reads the child's color * at its home, rises to a height given by `channel`, springs back toward home with * `stiffness`, and answers the `cursor`. Dispatched over a 2D grid. * * A particle snaps straight to its target until it has seen real content, so nothing lurches * on load. */ declare function relief(layout: ReliefLayout, cfg: { channel: AgentChannel; cursor: (homeW: d.v2f, off: d.v2f, z: number) => d.v3f; stiffness: number; maxSpeed: number; /** Child alpha below which a particle is never latched. Match the render's `minAlpha`. */ minAlpha: number; name: string; }): (x: number, y: number) => void; interface AdvectParamsView { readonly dt: number; readonly aspect: number; readonly advect: number; } /** What a fluid-carried layout must name: `agents`, the fluid's velocity texture `velTex` and the `params` values. */ export interface AdvectLayout { readonly $: { readonly agents: Vec4StateArray; readonly velTex: d.Infer>; readonly params: AdvectParamsView; }; } /** * The update step for particles carried by a fluid: read the fluid's velocity under each * particle, ease toward it over about `1/inertiaRate` seconds, drift gently back to an even * spread at `homeRate` once the flow dies, and stay inside the canvas. * * `gridN` is the fluid grid's side in cells. `params.advect` scales how strongly the fluid * carries the particles. */ declare function advect2d(layout: AdvectLayout, cfg: { gridN: number; inertiaRate: number; homeRate: number; name: string; }): (i: number) => void; /** * The pushes an agent can feel. Each is built over your layout and returns a part the matching * integrator folds in list order. * * For a 3D swarm (`integrator.forces3d`): `pressure` (spread out evenly), `containment` (stay * inside the shape), `cursorXY`, `gravity`, `turbulence`. For a flock (`integrator.steering2d`): * `flocking`, `cursorSteer`, `wallTurn`. For drifting motes (`integrator.drift2d`): * `cursorGust`. For an image relief (`integrator.relief`): `cursorInView`. * * @example * ```ts * integrator.steering2d(simLayout, {forces: [force.flocking(simLayout, {maxAgents: 4096}), force.cursorSteer(simLayout), force.wallTurn(simLayout)], agitation: {rest: 1.5, gain: 0.45, cool: 1.3}, cruiseFloor: 0.35, wallRestitution: 0.6, name: 'boidsUpdate'}) * ``` * @tip Order matters only for readability. The forces are summed, so list them from the most important down. * @see integrator, torque, shapeField, agentFrame */ export declare const force: { /** * Spread a 3D swarm evenly by pushing agents away from crowded cells of a density grid. * Returns the force plus the two grid steps to put in the program before the update. * * @example * ```ts * const pressure = force.pressure(simLayout, {gridDim: 32, domain: 1, names: {clear: 'swarmClearDensity', splat: 'swarmDensity'}}) * ``` * @tip Register `pressure.clearKernel` as a `fixed` step over `pressure.cells` and `pressure.splatKernel` over `agents`, both before the update. * @see containment, integrator.forces3d */ readonly pressure: typeof pressure; /** * Keep a 3D swarm inside a shape: a spring at the surface, a pull back from far away, and a * drag when the shape rotates. * * @example * ```ts * forces: [pressure.force, force.containment(simLayout, {field: hexField, gradEps: 0.02, wall: {featherFrom: -0.1, featherTo: 0.01, base: 1.1, springK: 22}, recall: {from: 0.85, to: 1.1, k: 9}, homing: {from: 0.12, to: 0.45, k: 5}, entrainment: {featherHalf: 0.05}})] * ``` * @see shapeField, pressure */ readonly containment: typeof containment; /** * The cursor as a magnet on a 3D swarm, pushing away (or pulling in when the force is * negative). * * @example * ```ts * forces: [pressure.force, force.cursorXY(simLayout), force.gravity(simLayout)] * ``` * @see gravity, turbulence */ readonly cursorXY: typeof cursorXY; /** * A constant pull on a 3D swarm in the direction of `params.gravX, gravY`. * * @example * ```ts * forces: [pressure.force, force.cursorXY(simLayout), force.gravity(simLayout)] * ``` * @see cursorXY */ readonly gravity: typeof gravity; /** * A per-agent jitter that keeps a 3D swarm alive, scaled by `params.agitation`. * * @example * ```ts * forces: [pressure.force, force.turbulence(simLayout, {posScale: 193, timeX: 31.7, timeY: 27.3, seedScale: 997, gain: 5})] * ``` * @tip Keep the scale constants large. Small ones make the noise smooth and the whole swarm drifts in step. * @see cursorXY */ readonly turbulence: typeof turbulence; /** * Separation, alignment and cohesion for a flock, with live weights and radii. * * @example * ```ts * forces: [force.flocking(simLayout, {maxAgents: 4096}), force.cursorSteer(simLayout), force.wallTurn(simLayout)] * ``` * @see cursorSteer, wallTurn, integrator.steering2d */ readonly flocking: typeof flocking; /** * The cursor on a flock: attract, scatter like a predator, or nothing. * * @example * ```ts * forces: [force.flocking(simLayout, {maxAgents: 4096}), force.cursorSteer(simLayout)] * ``` * @see flocking */ readonly cursorSteer: typeof cursorSteer; /** * A soft turn back toward the canvas near an edge, so a flock arcs instead of bouncing. * * @example * ```ts * forces: [force.flocking(simLayout, {maxAgents: 4096}), force.wallTurn(simLayout)] * ``` * @see flocking */ readonly wallTurn: typeof wallTurn; /** * A puff from the cursor on drifting motes, zero while the force slider sits at 0. * * @example * ```ts * integrator.drift2d(simLayout, {drift: drift.variedHeading(simLayout), gust: force.cursorGust(simLayout), name: 'motesUpdate'}) * ``` * @see drift.variedHeading, integrator.drift2d */ readonly cursorGust: typeof cursorGust; /** * The cursor on an image relief, applied where each particle appears on screen. * * @example * ```ts * integrator.relief(fieldLayout, {channel: channel.luminance, cursor: force.cursorInView(fieldLayout, {perspK: 0.9}), stiffness: 18, maxSpeed: 4, minAlpha: 0.02, name: 'reliefUpdate'}) * ``` * @see integrator.relief, channel */ readonly cursorInView: typeof cursorInView; }; /** * The turns an agent that spins in place can feel, for `integrator.orientation2d`. * * @example * ```ts * torques: [torque.alignToField(simLayout), torque.rest(simLayout)] * ``` * @see field, integrator */ export declare const torque: { /** * Swing onto the field's line like a compass needle. * * @example * ```ts * torques: [torque.alignToField(simLayout), torque.rest(simLayout)] * ``` * @see rest, field.dipoleOrRadial */ readonly alignToField: typeof alignToField; /** * A weak swing back to each agent's rest angle, felt only where the field is faint. * * @example * ```ts * torques: [torque.alignToField(simLayout), torque.rest(simLayout)] * ``` * @see alignToField */ readonly rest: typeof restTorque; }; /** * What a turning agent samples at its position: the magnetic field at the cursor, and its * resting place on a jittered grid. * * @example * ```ts * integrator.orientation2d(simLayout, {home: field.jitteredGridHome(simLayout, {overscan: 0.1}), fieldAt: field.dipoleOrRadial(simLayout), torques: [torque.alignToField(simLayout), torque.rest(simLayout)], maxOffset: 0.02, name: 'filingsUpdate'}) * ``` * @see torque, integrator, agentFrame */ export declare const field: { /** * The field of a magnet at the cursor: a bar magnet along its travel, or a single pole. * * @example * ```ts * integrator.orientation2d(simLayout, {home, fieldAt: field.dipoleOrRadial(simLayout), torques, maxOffset: 0.12, name: 'filingsUpdate'}) * ``` * @see jitteredGridHome, torque.alignToField */ readonly dipoleOrRadial: typeof dipoleOrRadial; /** * Each agent's resting position on a jittered grid that runs past the canvas edges. * * @example * ```ts * const home = field.jitteredGridHome(simLayout, {overscan: 0.14}) * ``` * @tip The same home part goes to `renderAgents.orientedWorld` as `home`, so agents are drawn where they rest. * @see dipoleOrRadial */ readonly jitteredGridHome: typeof jitteredGridHome; }; /** * A built-in velocity for motes that glide, for `integrator.drift2d`. * * @example * ```ts * integrator.drift2d(simLayout, {drift: drift.variedHeading(simLayout), gust: force.cursorGust(simLayout), name: 'motesUpdate'}) * ``` * @see integrator */ export declare const drift: { /** * One shared heading and speed, each varied a little per mote. * * @example * ```ts * integrator.drift2d(simLayout, {drift: drift.variedHeading(simLayout), gust: force.cursorGust(simLayout), name: 'motesUpdate'}) * ``` * @see force.cursorGust, integrator.drift2d */ readonly variedHeading: typeof variedHeading; }; /** * The update step of an agent simulation: one per kind of agent. Each takes your layout and * the parts, and returns the step to register as `update` in `bake`. * * `forces3d` moves a 3D swarm under summed forces with drag and a speed limit. `steering2d` * moves a flock that keeps cruising and arcs off the edges. `orientation2d` turns agents in * place under summed torques. `drift2d` glides motes and wraps them around the canvas. * `relief` stands particles on the child's image. `advect2d` carries particles along a fluid. * * @example * ```ts * const update = integrator.forces3d(simLayout, {forces: [pressure.force, force.containment(simLayout, containmentCfg), force.cursorXY(simLayout), force.gravity(simLayout)], maxSpeed: 3, posClamp: 1.9, name: 'swarmUpdate'}) * ``` * @tip Drag arrives as a per-frame multiplier in `params.dragMul`. Compute it with `agentFrame.expDecay(rate, dt)` so it does not depend on frame rate. * @see force, torque, drift, field, renderAgents, agentSim */ export declare const integrator: { /** * A 3D swarm under summed forces, with drag, a speed limit and a position clamp. * * @example * ```ts * integrator.forces3d(simLayout, {forces: [pressure.force, force.containment(simLayout, containmentCfg), force.cursorXY(simLayout), force.gravity(simLayout)], maxSpeed: 3, posClamp: 1.9, name: 'swarmUpdate'}) * ``` * @see force.pressure, force.containment */ readonly forces3d: typeof forces3d; /** * A flock under summed steering, kept cruising, arcing off the canvas edges. * * @example * ```ts * integrator.steering2d(simLayout, {forces: [force.flocking(simLayout, {maxAgents: 4096}), force.cursorSteer(simLayout), force.wallTurn(simLayout)], agitation: {rest: 1.5, gain: 0.45, cool: 1.3}, cruiseFloor: 0.35, wallRestitution: 0.6, name: 'boidsUpdate'}) * ``` * @see force.flocking */ readonly steering2d: typeof steering2d; /** * Agents that turn in place under summed torques and drift a little toward the field. * * @example * ```ts * integrator.orientation2d(simLayout, {home: field.jitteredGridHome(simLayout, {overscan: 0.14}), fieldAt: field.dipoleOrRadial(simLayout), torques: [torque.alignToField(simLayout), torque.rest(simLayout)], maxOffset: 0.12, name: 'filingsUpdate'}) * ``` * @see torque, field */ readonly orientation2d: typeof orientation2d; /** * Motes that glide on a built-in drift plus a fading gust, wrapping around the canvas. * * @example * ```ts * integrator.drift2d(simLayout, {drift: drift.variedHeading(simLayout), gust: force.cursorGust(simLayout), name: 'motesUpdate'}) * ``` * @see drift.variedHeading, force.cursorGust */ readonly drift2d: typeof drift2d; /** * Particles standing on the child's image, rising by a color channel, springing home. * * @example * ```ts * integrator.relief(fieldLayout, {channel: channel.luminance, cursor: force.cursorInView(fieldLayout, {perspK: 0.9}), stiffness: 18, maxSpeed: 4, minAlpha: 0.02, name: 'reliefUpdate'}) * ``` * @see channel, force.cursorInView */ readonly relief: typeof relief; /** * Particles carried by a fluid's velocity texture, easing back to an even spread. * * @example * ```ts * integrator.advect2d(pfLayout, {gridN: 256, inertiaRate: 6, homeRate: 0.6, name: 'particleFlowIntegrate'}) * ``` * @see fluidSim */ readonly advect2d: typeof advect2d; }; export {}; //# sourceMappingURL=agentForces.d.ts.map