clone

an endless marble-race generator in the browser; the url is the seed, so every track can be shared

fix: accumulate trail samples during slow marble motion

Dami committed a8237c6
Keep a separate committed sampling anchor so a moving ribbon head does not
prevent slow motion from adding trail points. Reset the anchor on warps,
fades and explicit clears. Covered by a slow-motion regression.
commit
a8237c65e1c2260f12cdc98937f338c2f53a6756
parent
7e7a85f65097b1cb7e73fc3b5b18448127938bd1
author
Dami <dami.etoile@gmail.com>
2 files changed, +35 -2
Msrc/client/trail.ts21+++++++++++++++++++--
Mtests/trail_test.ts16++++++++++++++++
M src/client/trail.ts +19 -2
@@ -61,6 +61,12 @@ export function createTrail(
6161 6262 const tint = new THREE.Color(color);6363 const history: THREE.Vector3[] = []; // newest first64+ // `history[0]` follows the live marble every frame so the ribbon stays65+ // attached. Sampling distance must therefore use a separate committed66+ // anchor; comparing against the moving head prevents slow/kinematic motion67+ // from ever accumulating MIN_STEP and stretches one old segment forever.68+ const sampleAnchor = new THREE.Vector3();69+ let hasSampleAnchor = false;6470 let headWidth = 0.2;6571 6672 const camPos = new THREE.Vector3();
@@ -117,13 +123,22 @@ export function createTrail(
117123 },118124 update(pos: THREE.Vector3, camera: THREE.Camera): void {119125 const head = history[0];120- if (head && pos.distanceTo(head) > jumpReset) history.length = 0;126+ if (head && pos.distanceTo(head) > jumpReset) {127+ history.length = 0;128+ hasSampleAnchor = false;129+ }130+ if (!hasSampleAnchor) {131+ sampleAnchor.copy(pos);132+ hasSampleAnchor = true;133+ }121134 if (122- !history[0] || pos.distanceToSquared(history[0]) >= minStep * minStep135+ !history[0] || pos.distanceToSquared(sampleAnchor) >= minStep * minStep123136 ) {124137 history.unshift(pos.clone());138+ sampleAnchor.copy(pos);125139 if (history.length > maxPoints) history.length = maxPoints;126140 } else {141+ // Keep the visual head attached without moving the sampling anchor.127142 history[0].copy(pos);128143 }129144 rewrite(camera);
@@ -131,6 +146,7 @@ export function createTrail(
131146 fade(camera: THREE.Camera, opacity: number): void {132147 if (opacity <= 0) {133148 history.length = 0;149+ hasSampleAnchor = false;134150 geometry.setDrawRange(0, 0);135151 return;136152 }
@@ -138,6 +154,7 @@ export function createTrail(
138154 },139155 reset(): void {140156 history.length = 0;157+ hasSampleAnchor = false;141158 geometry.setDrawRange(0, 0);142159 },143160 dispose(): void {
M tests/trail_test.ts +16 -0