// MediaPipe hand landmark indices and the bone connections between them. // See: https://developers.google.com/mediapipe/solutions/vision/hand_landmarker // // 21 landmarks per hand, each normalized to [0,1] in image space. export const LANDMARK = { WRIST: 0, THUMB_CMC: 1, THUMB_MCP: 2, THUMB_IP: 3, THUMB_TIP: 4, INDEX_MCP: 5, INDEX_PIP: 6, INDEX_DIP: 7, INDEX_TIP: 8, MIDDLE_MCP: 9, MIDDLE_PIP: 10, MIDDLE_DIP: 11, MIDDLE_TIP: 12, RING_MCP: 13, RING_PIP: 14, RING_DIP: 15, RING_TIP: 16, PINKY_MCP: 17, PINKY_PIP: 18, PINKY_DIP: 19, PINKY_TIP: 20, } as const; /** Pairs of landmark indices that form the hand skeleton, for overlay drawing. */ export const HAND_CONNECTIONS: ReadonlyArray = [ // Thumb [0, 1], [1, 2], [2, 3], [3, 4], // Index [0, 5], [5, 6], [6, 7], [7, 8], // Middle [5, 9], [9, 10], [10, 11], [11, 12], // Ring [9, 13], [13, 14], [14, 15], [15, 16], // Pinky [13, 17], [17, 18], [18, 19], [19, 20], // Palm base [0, 17], ]; export interface NormalizedLandmark { x: number; y: number; z: number; visibility?: number; } /** Euclidean distance between two normalized landmarks (x/y plane). */ export function dist2d(a: NormalizedLandmark, b: NormalizedLandmark): number { const dx = a.x - b.x; const dy = a.y - b.y; return Math.hypot(dx, dy); } /** Midpoint of two normalized landmarks (x/y plane). */ export function midpoint(a: NormalizedLandmark, b: NormalizedLandmark): { x: number; y: number } { return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 }; }