跳转至

Granular transport and contact

The bed uses paired transfers on eight links per cell. Cardinal weights are 2/3 and diagonal weights are 1/6: the resulting nine-point stencil has the same second-order diffusion scale as the former cardinal stencil, with isotropic fourth-order truncation error in the linear regime. Repose thresholds use the actual link length, including the diagonal sqrt(2) factor. This reduces grid-aligned flow bias; it is not a fully rotation-invariant granular continuum model.

All eight outgoing amounts share one available-height limiter. Integration gathers the opposite link from each valid neighbor, including at edges and corners. Never clamp the integrated height to hide a conservation failure. Particle launch reserves must subtract the sum of all eight transfers, and launch direction and bed velocity must include diagonal displacement.

Pointer motion is resampled by travelled bed distance rather than raw event count. The queue retains real DOM timestamps on the resulting short segments, so geometry follows the same path distance regardless of pointer reporting rate while velocity still reflects how quickly that path was travelled. Several distance-bounded segments can be consumed in one fixed simulation step, preventing fast gestures from lagging behind the visible pointer or losing old movement under a capped event queue.

Multiple pointer IDs are tracked independently. Each substep accepts a bounded batch of simultaneous contact segments and rasterizes them into one compact per-cell contact field before granular transport. Transport therefore sees combined mouse, pen, or multi-finger contact without running a separate full sand solve per pointer. Overlapping contacts select the strongest local pressure/motion response while preserving the broadest disturbed shell.

Pointer position by itself is observational only. The input queue emits tool contact only after a held pointer has actually moved. Hovering, moving the cursor without pressing, or holding a stationary press therefore cannot displace the bed; this is enforced before the GPU contact field is built rather than relying on a visual-state convention.

The finger remains a swept capsule over the existing heightfield transport, not a separate rigid-body or granular solver. Normal contact still uses the configured penetration depth and the established finite-rate correction: below the fast-impact threshold, the dense drawing path is unchanged. At high gesture speed a narrow impact core adds bounded penetration instead of deepening the full footprint, and its evacuation fraction rises toward 86%. This compensates for several short distance-resampled segments being represented in one fixed solve without turning ordinary drawing into a wider trench. The impact term also strengthens forward transport, while the disturbed shell and supercritical avalanche boost continue spreading the displaced material into a compact bow wave rather than a needle ridge. No height blur or post-simulation clamp is used; all smoothing remains mass-conserving transport.

Fast impact ejection is generated from the same current-substep contact field instead of waiting for the old bed velocity scan. Transport detects the advancing frontier from the coverage drop in the motion direction and writes a per-cell ejection budget from material that was actually mobilized there. At full impact the canonical ejection budget is 7.5% of the locally transported frontier material. Inactive grain threads sample only the current contact caps, atomically claim that budget, subtract the claimed mass through the existing fixed-point exchange buffer, and launch with current tool/transport direction plus local uphill deflection. Claims may consume the remaining fraction of a cell budget rather than requiring a full nominal parcel, so end-of-budget mass does not disappear through quantization. The atomic correction keeps concurrent claims bounded by the cell allocation.

The older agitation path remains for loose material away from active contact. Those grains still use bed momentum and conserved transport/activity on the bounded global scan. Cells currently in the fast-impact regime are excluded from that legacy launch path during the same substep so ambient and impact emitters cannot double-withdraw from one cell; slower contact keeps the previous agitation path unchanged. Impact grains use a denser parcel population and a centimeter-scale launch distribution with a sparse energetic tail. While airborne, velocity-dependent drag is applied before gravity. All loose grains still collide against the same two triangles used by the surface mesh, use interpolated height/normal contact, dissipate impact energy with restitution and Coulomb-style tangential friction, and return landed mass through the exchange buffer. This remains a heightfield/ballistic hybrid, not particle-particle contact dynamics or an MPM solver.

The flux buffer costs 8 MiB at 512 squared (4 MiB more than cardinal-only transport), and the per-cell impact budget adds 1 MiB. Simulation dispatch count, state buffers, and fixed-step budget remain unchanged. Frontier detection reuses the four axial contact samples already fetched by the eight-neighbor transport loop, so it does not add another neighborhood pass. Airborne rendering now adds one bounded world-space optical-depth shadow draw; its cost and design are documented in airborne-sand.md. Hardware tests check correctness, not a universal frame-time guarantee.

GPU regressions cover diagonal spread, symmetric diagonal neighbors, edge/corner conservation, bed floor bounds, signed-slope impacts, impact energy loss, redeposition, persistent grooves, immediate fast-impact depth/frontier ejection, gesture-speed momentum/ejection differences, and offscreen rendering. Browser visual review remains manual.