import { d } from '../../gpu/kit/index'; type AtomicU32 = d.atomicU32; /** A mutable vec4f state array entry (`agents`, `pos`, `vel`, …). */ export type Vec4StateArray = d.v4f[]; /** A 3D force contribution: `(pos, vel, seed) → vec3f`. */ export type AgentForce3 = (pos: d.v3f, vel: d.v3f, seed: number) => d.v3f; /** A baked shape-local signed-distance field: `(pos) → f32`. */ 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; } /** Layout view for the volume family: vec4 pos/vel state + the density grid + params. */ export interface VolumeSwarmLayout { readonly $: { readonly pos: Vec4StateArray; readonly vel: Vec4StateArray; readonly dens: AtomicU32[]; readonly params: VolumeParamsView; }; } /** What `force.pressure` returns: the force part plus the density-grid machinery it owns. */ export interface PressureForce { force: AgentForce3; /** Full-grid clear, dispatched `fixed` over `cells`. */ clearKernel: (i: number) => void; /** Per-agent trilinear density splat. */ splatKernel: (i: number) => void; /** The density grid's cell count (the clear dispatch size). */ cells: number; } /** * Gas-pressure force over an auxiliary 3D density grid — even filling as an emergent property. * The part owns the grid ENTIRELY: the per-frame clear + trilinear splat kernels, the trilinear * sampler the force differentiates (piecewise-constant per-cell gradients herd agents onto the * cell lattice), and the R3 sub-cell dither contract (`params.gridOffX/Y/Z`, written per frame * from `agentFrame.r3SubCellOffset`) that keeps the lattice from standing in moiré with a shape * boundary. Layout needs: `pos`, `dens` (atomic u32, `gridDim³`), `params.{spread, gridOff*}`. */ declare function pressure(layout: VolumeSwarmLayout, cfg: { gridDim: number; /** The grid covers shape-local [−domain, domain]³. */ domain: number; names: { clear: string; splat: string; }; }): PressureForce; /** * The containment BUNDLE — the four forces that share one SDF evaluation (a signed distance, * its central-difference gradient, and the radial distance): the surface spring wall, the * far-field recall, the gradient-free homing, and the rotation entrainment. Bundled so the * shared intermediates are computed once, exactly as the hand-written integrator did. */ declare function containment(layout: VolumeSwarmLayout, cfg: { field: ShapeField3; /** Central-difference epsilon for the field gradient. */ gradEps: number; /** Spring through the surface: feathers in over [featherFrom, featherTo], plus a linear * outside term — strong outside, diffuse inside (a sharp feather packs a coherent shell). */ wall: { featherFrom: number; featherTo: number; base: number; springK: number; }; /** Smooth pull home once an agent leaves the domain (a hard clamp piles strays into lines). */ recall: { from: number; to: number; k: number; }; /** SDF-VALUE-driven pull toward the origin — direction-safe where the gradient degenerates. */ homing: { from: number; to: number; k: number; }; /** Drag toward the rotating container's feature velocity ω×r, feathered to inside. */ entrainment: { featherHalf: number; }; }): AgentForce3; /** The cursor magnet as an xy cylinder around the pointer ray (positive pushes away) — the * shared kit force field. Layout needs `params.{cursorX, cursorY, cursorForce, cursorRadSq}`. */ declare function cursorXY(layout: VolumeSwarmLayout): AgentForce3; /** Constant gravity (pre-rotated into shape space on the CPU). Needs `params.{gravX, gravY}`. */ declare function gravity(layout: VolumeSwarmLayout): AgentForce3; /** * Hash-turbulence kick, scaled by `params.agitation`. The large multipliers are load-bearing: * the fract-based hash needs its input to wrap many times to be white — at small ranges it * degrades into a smooth, near-symmetric function of position and the whole swarm gets herded * into coherent (mirror-symmetric) drift cells. */ declare function turbulence(layout: VolumeSwarmLayout, cfg: { posScale: number; timeX: number; timeY: number; seedScale: number; gain: number; }): AgentForce3; /** Fold a declared 3D force list into one part, preserving the declared sum order. */ export declare function composeForces3(parts: AgentForce3[]): AgentForce3; /** * The volume-family integrator: fold the declared forces, run the shared semi-implicit Euler * (drag + speed clamp), advance, and hard-clamp position as a distant safety net. State written * back as pos = (xyz, seed), vel = (xyz, |v|) — the resolve's speed ramp reads vel.w. */ declare function forces3d(layout: VolumeSwarmLayout, cfg: { forces: AgentForce3[]; maxSpeed: number; posClamp: number; name: string; }): (i: number) => void; /** A steering-force contribution: `(pos, vel, maxSpeedI) → vec2f`, clamped by the part. */ 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; } /** Layout view for the steering family. */ export interface SteeringLayout { readonly $: { readonly agents: Vec4StateArray; readonly agit: number[]; readonly params: SteeringParamsView; }; } /** * The Reynolds triple — separation / alignment / cohesion over ONE brute-force neighbour scan * (the static-MAX loop bound with the runtime-count early break is engine policy inside the * part: the count slider never recompiles). Weights and radii are runtime uniforms * (`sepW/aliW/cohW`, `perceptionSq/sepRadiusSq`); each steer is maxForce-clamped. */ declare function flocking(layout: SteeringLayout, cfg: { maxAgents: number; }): AgentSteer2; /** Cursor field: attract (mode 1) or repel/predator (mode 2), fading to zero at the radius. */ declare function cursorSteer(layout: SteeringLayout): AgentSteer2; /** Soft edge turn: steer back before reaching a wall so the flock curves away in an arc. * (At most one x and one y term is nonzero, so the per-axis sums are exact.) */ declare function wallTurn(layout: SteeringLayout): AgentSteer2; /** Fold a declared steering list into one part, preserving the declared sum order. */ export declare function composeSteer2(parts: AgentSteer2[]): AgentSteer2; /** * The steering-family integrator: per-agent cruise variation, the folded steering forces, the * agitation envelope (steering effort above the cruising baseline charges instantly, cools * exponentially — the resolve's rest→excited ramp reads it), then Euler with a speed clamp * that keeps a minimum cruise, and a reflective hard safety net at the domain walls. */ declare function steering2d(layout: SteeringLayout, cfg: { forces: AgentSteer2[]; agitation: { rest: number; gain: number; cool: number; }; /** Minimum cruise as a ratio of the per-agent max speed. */ cruiseFloor: number; /** Velocity retained (negated) on wall contact. */ wallRestitution: number; name: string; }): (i: number) => void; /** The field sampled at one agent: line direction (φ), strength, and the radial unit. */ export declare const FieldSample: d.WgslStruct<{ rhat: d.Vec2f; strength: d.F32; phi: d.F32; }>; export type FieldSampleValue = d.Infer; /** A torque contribution: `(field, θ, fi) → f32`. */ export type AgentTorque = (fld: FieldSampleValue, theta: number, fi: number) => number; /** A field sampler part: `(pos) → FieldSample`. */ export type AgentFieldAt = (pos: d.v2f) => FieldSampleValue; /** A derived home-position part: `(fi) → vec2f`. */ 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; } /** Layout view for the orientation family. */ export interface OrientationLayout { readonly $: { readonly agents: Vec4StateArray; readonly agit: number[]; readonly params: OrientationParamsView; }; } /** * The cursor field sample every torque/pull shares: r̂ from the pointer, the dipole-or-radial * line direction (`params.fieldType` selects at runtime), and a Gaussian strength falloff by * distance (`params.{strength, reachSq}`). The dipole axis is the CPU-smoothed cursor motion * (`params.{axisX, axisY}` — see `agentFrame.createMotionAxis`). */ declare function dipoleOrRadial(layout: OrientationLayout): AgentFieldAt; /** Each agent's rest orientation (radians): random per agent (mode 0), horizontal (1), * vertical (2) — `restMode` is a runtime uniform. */ export declare const restOrientationAngle: import('typegpu').TgpuFn<(fi: d.F32, restMode: d.F32) => d.F32>; /** Swing onto the local field line: nematic torque `alignK · strength · sin(2Δ)`. */ declare function alignToField(layout: OrientationLayout): AgentTorque; /** Weak nematic pull to the rest orientation — dominant only where the field is faint. */ declare function restTorque(layout: OrientationLayout): AgentTorque; /** Fold a declared torque list into one part, preserving the declared sum order. */ export declare function composeTorques(parts: AgentTorque[]): AgentTorque; /** * Rest (home) position on a jittered grid (`params.{gridCols, cellW, cellH, jitter}`), laid out * over an `overscan` border beyond the viewport so pulled-in edges backfill from off-screen. */ declare function jitteredGridHome(layout: OrientationLayout, cfg: { overscan: number; }): AgentHome2; /** * The orientation-family integrator: derive home + sample the field once, integrate the folded * torques with angular drag (underdamped → visible settling), charge the angular-activity * envelope from |ω|, then the gentle position dynamics — the field pull (−r̂·pullK·strength), * the exponential spring home, and the hard wander clamp. Orientation carries the effect. */ 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; } /** Layout view for the drift family. */ export interface DriftLayout { readonly $: { readonly agents: Vec4StateArray; readonly params: DriftParamsView; }; } /** A per-agent deterministic drift velocity part: `(fi) → vec2f`. */ 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; } /** * Shared-heading drift with per-agent variance: the shared heading varied per agent by * `params.angleVarRad` (± the half-range) and the shared speed by `params.speedVar` (±½), * both hashed from the index — a coherent stream that never reads robotic. Layout needs * `params.{driftBase, angleRad, speedVar, angleVarRad}`. */ declare function variedHeading(layout: { readonly $: { readonly params: HeadingDriftParamsView; }; }): AgentDrift; /** Cursor gust: a kit magnet impulse on the stored velocity (already ×dt), zero while the * force slider sits at 0 (a uniform-valued branch — fully coherent). */ declare function cursorGust(layout: DriftLayout): (pos: d.v2f) => d.v2f; /** * The drift-family integrator: gust impulse → exponential gust decay → advance by the * deterministic drift + gust → toroidal wrap over [0, domainX] × [0, 1] (floor-subtract * handles any overshoot in one step). */ 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; } /** Layout view for the relief family (`src` is the late-bound child RTT). */ export interface ReliefLayout { readonly $: { readonly pos: Vec4StateArray; readonly vel: Vec4StateArray; readonly col: Vec4StateArray; readonly src: d.Infer>; readonly params: ReliefParamsView; }; } /** A depth-channel extractor: `(rgb, a) → f32` over the UNPREMULTIPLIED child colour. */ export type AgentChannel = (rgb: d.v3f, a: number) => number; /** The extractor menu + its canonical mode numbering (0 luminance … 6 alpha). */ export declare const channel: { readonly luminance: AgentChannel; readonly luminanceInverted: AgentChannel; readonly red: AgentChannel; readonly green: AgentChannel; readonly blue: AgentChannel; readonly saturation: AgentChannel; readonly alpha: AgentChannel; readonly byMode: Record; }; /** * The view-space cursor magnet: project the agent's CURRENT position through the same camera * path as the splat (rotation → perspective·zoom → screen offset) so the cursor interacts with * what is actually under the pointer at any camera angle, apply the kit magnet in view space * (xy shove + z bulge), and rotate the force back into field space by Rᵀ. */ declare function cursorInView(layout: ReliefLayout, cfg: { perspK: number; }): (homeW: d.v2f, off: d.v2f, z: number) => d.v3f; /** * The relief-family integrator: bilinear-sample the live child at the home UV, unpremultiply * and store the colour, derive the depth target from the declared channel, then the spring * toward home (xy) / target (z) plus the view-space cursor magnet, through the shared * semi-implicit Euler. A particle SNAPS to its target until it has actually seen child content * (the vel.w latch) — no start lurch, no fly-in 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 (matches the splat's early-out). */ minAlpha: number; name: string; }): (x: number, y: number) => void; interface AdvectParamsView { readonly dt: number; readonly aspect: number; readonly advect: number; } /** Layout view for the advection family (`velTex` is the solved fluid velocity handoff). */ export interface AdvectLayout { readonly $: { readonly agents: Vec4StateArray; readonly velTex: d.Infer>; readonly params: AdvectParamsView; }; } /** * The advection-family integrator: sample the fluid grid bilinearly at the agent's screen-uv, * convert grid cells/sec to world units/sec, ease velocity toward the field (framerate- * independent inertia), add the weak R2 home spring (re-evens coverage once the flow dies — * the fluid box does not wrap), integrate, and clamp to the domain. */ declare function advect2d(layout: AdvectLayout, cfg: { gridN: number; inertiaRate: number; homeRate: number; name: string; }): (i: number) => void; export declare const force: { readonly pressure: typeof pressure; readonly containment: typeof containment; readonly cursorXY: typeof cursorXY; readonly gravity: typeof gravity; readonly turbulence: typeof turbulence; readonly flocking: typeof flocking; readonly cursorSteer: typeof cursorSteer; readonly wallTurn: typeof wallTurn; readonly cursorGust: typeof cursorGust; readonly cursorInView: typeof cursorInView; }; export declare const torque: { readonly alignToField: typeof alignToField; readonly rest: typeof restTorque; }; export declare const field: { readonly dipoleOrRadial: typeof dipoleOrRadial; readonly jitteredGridHome: typeof jitteredGridHome; }; export declare const drift: { readonly variedHeading: typeof variedHeading; }; export declare const integrator: { readonly forces3d: typeof forces3d; readonly steering2d: typeof steering2d; readonly orientation2d: typeof orientation2d; readonly drift2d: typeof drift2d; readonly relief: typeof relief; readonly advect2d: typeof advect2d; }; export {}; //# sourceMappingURL=agentForces.d.ts.map