// Fling detection for one-finger pans: a quick flick that ends the drag. import type { Dir } from '$lib/model/tree'; export interface Sample { x: number; y: number; t: number; } /** Only the last stretch of the drag counts. */ const WINDOW_MS = 100; /** px/ms; a slow drag that stops is not a fling. */ const MIN_SPEED = 0.6; /** The main axis must dominate by this much. */ const MIN_RATIO = 2; /** * Side of the canvas a fling heads for, or null. Moving the finger left pulls in * what's on the right, so a leftward flick returns 'right'. */ export function flingDir(samples: readonly Sample[]): Dir | null { const last = samples.at(-1); if (!last) return null; const first = samples.find((s) => last.t - s.t <= WINDOW_MS); if (!first || first === last) return null; const dt = last.t - first.t; if (dt <= 0) return null; const dx = last.x - first.x; const dy = last.y - first.y; if (Math.hypot(dx, dy) / dt < MIN_SPEED) return null; if (Math.abs(dx) >= Math.abs(dy) * MIN_RATIO) return dx < 0 ? 'right' : 'left'; if (Math.abs(dy) >= Math.abs(dx) * MIN_RATIO) return dy < 0 ? 'down' : 'up'; return null; } /** Keeps a short trail of pointer samples for flingDir. */ export function pushSample(samples: Sample[], s: Sample) { samples.push(s); while (samples.length > 2 && s.t - samples[0].t > WINDOW_MS * 2) samples.shift(); }