Optional ReadonlygpuWhen present, a simulation: "gpu" emitter may apply this field on
the device. Absent on every built-in except radialField.
OptionalsampleOptional fast path: the same contribution as ParticleForceField.sample, for count particles at once, added into
out (R-34, §27/§112).
sampleAll(positions, velocities, count, time, out) {
for (let i = 0; i < count; i += 1) {
const base = i * 3;
out[base] += -c * velocities[base];
out[base + 1] += -c * velocities[base + 1];
out[base + 2] += -c * velocities[base + 2];
}
}
Measured attribution in benchmarks/results/particles-100k.json: the
integrator alone costs 1.31 ms per 100 000 particles, and each field
adds ~5.15 ms — three fields consume the whole 16.67 ms fixed-step budget.
Almost none of that is the field's arithmetic. It is one megamorphic
sample() call, two Vector3.sets, and three property reads, per particle
per field. A field that loops internally pays the call once per step
instead of once per particle, and JIT-inlines its own monomorphic body.
out arrives carrying the emitter's gravity and
every earlier field's contribution; overwriting it deletes them. (This is
the one place this entry point departs from §27's sample, which
produces a value. Producing here would force the emitter to allocate a
second buffer and make a third pass over 3 × count floats per field,
which is most of the win.)out is a Float64Array, not Float32Array. The scalar path
accumulates in JavaScript numbers, i.e. binary64; a binary32 accumulator
would round after every field and make a batched run differ from a scalar
one in the last bits. §33 does not permit "fast, and also slightly
different".sample. Same arithmetic, same order of
operations, same special cases. A field that ships both owes a test that
pins them against each other — every built-in field has one.positions and velocities are the pool's live arrays, xyz at stride
3, valid for indices [0, 3 · count). Read, never write.sample: no clock, no Math.random.A field that does not implement this is not penalised: the emitter falls
back to sample for that field alone, in the same declaration order, with
the same result.
§27's
ForceField, as particles consume it. See the module note for the naming and the units.Contract for an implementation:
positionorvelocity. They are the caller's scratch copies of the particle's live state; writing to them corrupts the step.outwhen it is supplied and return it — the emitter always supplies one, and doing so is what keeps the hot path allocation-free (§7b, plan D7). Returning a different vector is legal (the caller reads the return value, neverout), but that vector must stay valid until the next call.outis not zeroed by the caller. Write every component.time, in seconds. NoMath.random— take aSeededRandomat construction if the field is procedural.