Drawer drag: soften the grab spring + clamp held-piece velocity

Grabbing a buried piece built up spring tension against the pieces on
top, then broke free at ~9g (force cap 90*m, no speed limit) and batted
the whole pile out of the drawer. Softened the spring (K 165->55, damping
13->22, force cap 90->22) so it EASES toward the cursor, and added the
real fix: a hard velocity clamp (3.2 u/s) on the held piece so it can
never become a projectile — every downstream momentum transfer is bounded
by that one line.

Verified (harness drives the identical dragGrabbed path): a violent yank
now peaks the pile at 2.89 u/s with ZERO pieces flung out (was: everything
flew off screen); a controlled lift still raises a piece to y=2.09 over
the 1.47 rim and commits it out. Fishing still works, catapult gone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-19 13:31:30 +10:00
parent 7b6005cf86
commit 10b27e37fa

View File

@ -355,15 +355,16 @@ export class DrawerView implements View {
const pz = t.z + this.tmp.z; const pz = t.z + this.tmp.z;
// A spring, not a joint: a snagged piece has to fight the ones on top of it // A spring, not a joint: a snagged piece has to fight the ones on top of it
// and lose, rather than tunnel through them. // and lose, rather than tunnel through them. Soft K + heavy damping so it
// EASES toward the cursor; a snagged piece builds no catapult tension.
const v = p.body.linvel(); const v = p.body.linvel();
const m = p.body.mass(); const m = p.body.mass();
const K = 165 * m; const K = 55 * m;
const C = 13 * m; const C = 22 * m;
const fx = (this.target.x - px) * K - v.x * C; const fx = (this.target.x - px) * K - v.x * C;
const fy = (this.target.y - py) * K - v.y * C; const fy = (this.target.y - py) * K - v.y * C;
const fz = (this.target.z - pz) * K - v.z * C; const fz = (this.target.z - pz) * K - v.z * C;
const cap = 90 * m; const cap = 22 * m;
const mag = Math.hypot(fx, fy, fz); const mag = Math.hypot(fx, fy, fz);
const s = mag > cap ? cap / mag : 1; const s = mag > cap ? cap / mag : 1;
p.body.wakeUp(); p.body.wakeUp();
@ -372,6 +373,15 @@ export class DrawerView implements View {
{ x: px, y: py, z: pz }, { x: px, y: py, z: pz },
true, true,
); );
// The real anti-catapult: the held piece can NEVER move fast enough to bat
// the pile across the room. Clamp its speed hard — everything downstream
// (momentum into neighbours) is bounded by this one line.
const MAXV = 3.2;
const sp = Math.hypot(v.x, v.y, v.z);
if (sp > MAXV) {
const k = MAXV / sp;
p.body.setLinvel({ x: v.x * k, y: v.y * k, z: v.z * k }, true);
}
} }
dispose(): void { dispose(): void {