🖱 fix: orbit drag was dead — OrbitControls bound to the pointer-events:none label overlay; rebind to the WebGL canvas (verified by dispatched pointer drag: heading 0→-0.93 rad) (fable)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
monsterrobotparty 2026-07-16 01:19:09 +10:00
parent 228caf2270
commit 3e36a0c16a

View File

@ -166,6 +166,7 @@ const focus = {
fromId: CONFIG.camera.startFocus,
t: 1, // flight progress 0..1
camFrom: 0, camTo: 0,
fromOffset: { x: 0, y: 0, z: 0 }, // captured origin of the current flight
};
// bodyWorld[id] = absolute view-world position, plain float64 {x,y,z}.
@ -223,7 +224,9 @@ function moonLocalOffset(b) {
function effectiveE(id) {
const b = BODIES[id];
const baseE = id === 'sun' ? scale.getSunE() : scale.getPlanetE();
if (id !== focus.id || focus.t < 1) return baseE;
if (id !== focus.id) return baseE;
// Ramp E→1 with camera distance continuously — including DURING the fly-to — so
// the body doesn't pop when the flight finishes (camDist lerps camFrom→camTo).
const exaggR = scale.drawRadius(b.radiusKm, baseE);
const camDist = camera.position.length();
const f = lib.clamp(camDist / (exaggR * 8), 0, 1);
@ -246,6 +249,10 @@ function bodyTrail(id) {
function setFocus(id, animate = true) {
if (!BODIES[id]) return;
focus.fromId = focus.id;
// Snapshot the CURRENT (possibly mid-lerp) offset as the flight's origin, so
// re-focusing during a flight starts from where the view actually is — not from
// the previous destination body.
focus.fromOffset = { x: focusOffset.x, y: focusOffset.y, z: focusOffset.z };
focus.id = id;
focus.t = animate ? 0 : 1;
focus.camFrom = camera.position.length();
@ -322,7 +329,8 @@ let dragMoved = false, downPos = null;
renderer.domElement.addEventListener('pointerdown', (e) => { downPos = { x: e.clientX, y: e.clientY }; dragMoved = false; });
renderer.domElement.addEventListener('pointermove', (e) => { if (downPos && Math.hypot(e.clientX - downPos.x, e.clientY - downPos.y) > 5) dragMoved = true; });
renderer.domElement.addEventListener('pointerup', (e) => {
if (dragMoved) return; // was an orbit drag, not a click
const started = downPos; downPos = null; // consume this gesture
if (!started || dragMoved) return; // needs a canvas press that didn't drag
ndc.set((e.clientX / innerWidth) * 2 - 1, -(e.clientY / innerHeight) * 2 + 1);
raycaster.setFromCamera(ndc, camera);
raycaster.params.Points = { threshold: 0.5 };
@ -409,11 +417,13 @@ function animate(now) {
// positions (truth → view), one central pass
computeBodyWorld();
// focus flight: lerp offset between old & new focus (both live), ease cam dist
// focus flight: lerp offset from the captured start (fromOffset) to the live
// target body, ease cam dist. Starting from the snapshot (not bodyWorld[fromId])
// means re-focusing mid-flight doesn't jump to the previous destination.
if (focus.t < 1) {
focus.t = lib.clamp(focus.t + dtWall / CONFIG.camera.flightMs, 0, 1);
const e = lib.smoothstep(focus.t);
const a = bodyWorld[focus.fromId], b = bodyWorld[focus.id];
const a = focus.fromOffset, b = bodyWorld[focus.id];
focusOffset.x = lib.lerp(a.x, b.x, e); focusOffset.y = lib.lerp(a.y, b.y, e); focusOffset.z = lib.lerp(a.z, b.z, e);
camera.position.setLength(lib.lerp(focus.camFrom, focus.camTo, e));
if (focus.t >= 1) controls.enabled = true;