{"version":3,"file":"index.mjs","sources":["../../src/constants/public.ts","../../src/analyzers/body-pose/index.ts","../../src/analyzers/hand-pose/index.ts","../../src/analyzers/hand-gesture/index.ts","../../src/complex-hand-gesture/index.ts","../../src/analyzers/base-classes/index.ts","../../src/analyzers/hand-pose/utils.ts","../../src/drawing-utils/draw-connectors/index.ts","../../src/drawing-utils/draw-landmarks/index.ts","../../src/hand-gesture-snapshot/index.ts"],"sourcesContent":["/**\n * - A replacement for `import { HAND_CONNECTIONS } from '@mediapipe/hands'`\n * - \"TypeError: connections is not iterable\" is thrown when trying to use it.\n * - No idea why the f••• it is `undefined`.\n * @public\n */\nexport const HAND_CONNECTIONS = [[0, 1], [1, 2], [2, 3], [3, 4], [0, 5], [5, 6], [6, 7], [7, 8], [5, 9], [9, 10], [10, 11], [11, 12], [9, 13], [13, 14], [14, 15], [15, 16], [13, 17], [0, 17], [17, 18], [18, 19], [19, 20]] as Array<[start: number, end: number]>\n\n/**\n * @public\n */\nexport enum VisionAnalyzerState {\n  CREATED,\n  INITIALIZING,\n  STANDBY,\n  ACTIVE,\n  DISPOSED,\n}\n","import { LazyValue, reflect1D } from '@glyph-cat/swiss-army-knife'\nimport { NormalizedLandmark, PoseLandmarker, PoseLandmarkerResult } from '@mediapipe/tasks-vision'\nimport { BaseLandmarkAnalyzer } from '../base-classes'\n\n/**\n * @public\n */\nexport type OnePersonBodyPoseAnalyzerResult = Array<NormalizedLandmark>\n\n/**\n * @public\n */\nexport class OnePersonBodyPoseAnalyzer extends BaseLandmarkAnalyzer<PoseLandmarker, OnePersonBodyPoseAnalyzerResult> {\n\n  /**\n   * @internal\n   */\n  private static M$taskRunnerGetter = new LazyValue(async () => {\n    return PoseLandmarker.createFromOptions(\n      await BaseLandmarkAnalyzer.getVision(),\n      {\n        baseOptions: {\n          modelAssetPath: '/mediapipe/models/pose_landmarker_lite.task',\n          delegate: 'GPU',\n        },\n        numPoses: 1,\n        runningMode: 'VIDEO',\n      }\n    )\n  })\n\n  constructor(videoElement: HTMLVideoElement) {\n    super(videoElement, new Array(Object.keys(BodyPoseLandmark).length / 2).fill({\n      x: 0,\n      y: 0,\n      z: 0,\n      visibility: 0,\n    }), OnePersonBodyPoseAnalyzer.M$taskRunnerGetter, 'OnePersonBodyPoseAnalyzer')\n  }\n\n  protected getProcessedResult(rawResult: PoseLandmarkerResult): OnePersonBodyPoseAnalyzerResult {\n    const processedLandmarks: OnePersonBodyPoseAnalyzerResult = []\n    if (rawResult.landmarks.length <= 0) { return }\n    const landmarks = rawResult.landmarks[0]\n    for (const landmark of landmarks) {\n      processedLandmarks.push({\n        ...landmark,\n        x: reflect1D(landmark.x, 0.5), // These values range between 0 to 1\n        visibility: 1,\n      })\n    }\n    return processedLandmarks\n  }\n\n}\n\n/**\n * @public\n */\nexport enum BodyPoseLandmark {\n  NOSE,\n  LEFT_EYE_INNER,\n  LEFT_EYE,\n  LEFT_EYE_OUTER,\n  RIGHT_EYE_INNER,\n  RIGHT_EYE,\n  RIGHT_EYE_OUTER,\n  LEFT_EAR,\n  RIGHT_EAR,\n  MOUTH_LEFT,\n  MOUTH_RIGHT,\n  LEFT_SHOULDER,\n  RIGHT_SHOULDER,\n  LEFT_ELBOW,\n  RIGHT_ELBOW,\n  LEFT_WRIST,\n  RIGHT_WRIST,\n  LEFT_PINKY,\n  RIGHT_PINKY,\n  LEFT_INDEX,\n  RIGHT_INDEX,\n  LEFT_THUMB,\n  RIGHT_THUMB,\n  LEFT_HIP,\n  RIGHT_HIP,\n  LEFT_KNEE,\n  RIGHT_KNEE,\n  LEFT_ANKLE,\n  RIGHT_ANKLE,\n  LEFT_HEEL,\n  RIGHT_HEEL,\n  LEFT_FOOT_INDEX,\n  RIGHT_FOOT_INDEX,\n}\n","import { LazyValue, reflect1D } from '@glyph-cat/swiss-army-knife'\nimport { HandLandmarker, HandLandmarkerResult, NormalizedLandmark } from '@mediapipe/tasks-vision'\nimport { BaseLandmarkAnalyzer } from '../base-classes'\nimport { BodyPoseLandmark, OnePersonBodyPoseAnalyzer } from '../body-pose'\nimport { getHandedness } from './utils'\n\n/**\n * @public\n */\nexport type OnePersonHandPoseAnalyzerHandResult = Array<NormalizedLandmark>\n\n/**\n * @public\n */\nexport interface OnePersonHandPoseAnalyzerResult {\n  L?: OnePersonHandPoseAnalyzerHandResult\n  R?: OnePersonHandPoseAnalyzerHandResult\n}\n\n/**\n * @public\n */\nexport class OnePersonHandPoseAnalyzer extends BaseLandmarkAnalyzer<HandLandmarker, OnePersonHandPoseAnalyzerResult> {\n\n  /**\n   * @internal\n   */\n  private static M$taskRunnerGetter = new LazyValue(async () => {\n    return HandLandmarker.createFromOptions(\n      await BaseLandmarkAnalyzer.getVision(),\n      {\n        baseOptions: {\n          modelAssetPath: '/mediapipe/models/hand_landmarker.task',\n          delegate: 'GPU',\n        },\n        numHands: 2,\n        runningMode: 'VIDEO',\n      }\n    )\n  })\n\n  constructor(private readonly bodyPoseAnalyzer: OnePersonBodyPoseAnalyzer) {\n    super(\n      bodyPoseAnalyzer.videoElement,\n      {},\n      OnePersonHandPoseAnalyzer.M$taskRunnerGetter,\n      'OnePersonHandPoseAnalyzer',\n    )\n  }\n\n  protected getProcessedResult(rawResult: HandLandmarkerResult): OnePersonHandPoseAnalyzerResult {\n    const bodyPoseResult = this.bodyPoseAnalyzer.result.get()\n    const processedLandmarks: OnePersonHandPoseAnalyzerResult = {}\n    for (const landmarks of rawResult.landmarks) {\n      const subLandmark: Array<NormalizedLandmark> = []\n      let wrist: NormalizedLandmark\n      for (let i = 0; i < landmarks.length; i++) {\n        const landmark = landmarks[i]\n        const flippedLandmark = {\n          ...landmark,\n          x: reflect1D(landmark.x, 0.5),\n          visibility: 1,\n        }\n        if (i === HandPoseLandmark.WRIST) {\n          wrist = flippedLandmark\n        }\n        subLandmark.push(flippedLandmark)\n      }\n      const handedness = getHandedness(\n        wrist,\n        bodyPoseResult[BodyPoseLandmark.LEFT_WRIST],\n        bodyPoseResult[BodyPoseLandmark.RIGHT_WRIST],\n      )\n      processedLandmarks[handedness] = subLandmark\n    }\n    return processedLandmarks\n  }\n\n}\n\n/**\n * @public\n */\nexport enum HandPoseLandmark {\n  WRIST,\n  THUMB_CMC,\n  THUMB_MCP,\n  THUMB_IP,\n  THUMB_TIP,\n  INDEX_FINGER_MCP,\n  INDEX_FINGER_PIP,\n  INDEX_FINGER_DIP,\n  INDEX_FINGER_TIP,\n  MIDDLE_FINGER_MCP,\n  MIDDLE_FINGER_PIP,\n  MIDDLE_FINGER_DIP,\n  MIDDLE_FINGER_TIP,\n  RING_FINGER_MCP,\n  RING_FINGER_PIP,\n  RING_FINGER_DIP,\n  RING_FINGER_TIP,\n  PINKY_FINGER_MCP,\n  PINKY_FINGER_PIP,\n  PINKY_FINGER_DIP,\n  PINKY_FINGER_TIP,\n}\n","import { LazyValue, reflect1D } from '@glyph-cat/swiss-army-knife'\nimport {\n  GestureRecognizer,\n  GestureRecognizerResult,\n  NormalizedLandmark,\n} from '@mediapipe/tasks-vision'\nimport { BaseVisionAnalyzer } from '../base-classes'\nimport { BodyPoseLandmark, OnePersonBodyPoseAnalyzer } from '../body-pose'\nimport { HandPoseLandmark } from '../hand-pose'\nimport { getHandedness } from '../hand-pose/utils'\n\n/**\n * @public\n */\nexport type OnePersonHandGestureAnalyzerHandResult = [\n  gesture: HandGesture,\n  landmarks: Array<NormalizedLandmark>,\n]\n\n/**\n * @public\n */\nexport interface OnePersonHandGestureAnalyzerResult {\n  L?: OnePersonHandGestureAnalyzerHandResult\n  R?: OnePersonHandGestureAnalyzerHandResult\n}\n\n/**\n * @public\n */\nexport class OnePersonHandGestureAnalyzer extends BaseVisionAnalyzer<GestureRecognizer, OnePersonHandGestureAnalyzerResult> {\n\n  /**\n   * @internal\n   */\n  private static M$taskRunnerGetter = new LazyValue(async () => {\n    return GestureRecognizer.createFromOptions(\n      await BaseVisionAnalyzer.getVision(),\n      {\n        baseOptions: {\n          modelAssetPath: '/mediapipe/models/gesture_recognizer.task',\n          delegate: 'GPU',\n        },\n        numHands: 2,\n        runningMode: 'VIDEO',\n      }\n    )\n  })\n\n  constructor(private readonly bodyPoseAnalyzer: OnePersonBodyPoseAnalyzer) {\n    super(\n      bodyPoseAnalyzer.videoElement,\n      {},\n      OnePersonHandGestureAnalyzer.M$taskRunnerGetter,\n      'recognizeForVideo',\n      'OnePersonHandGestureAnalyzer',\n    )\n  }\n\n  protected getProcessedResult(rawResult: GestureRecognizerResult): OnePersonHandGestureAnalyzerResult {\n    const bodyPoseResult = this.bodyPoseAnalyzer.result.get()\n    const processedResults: OnePersonHandGestureAnalyzerResult = {}\n    for (const landmarksIndex in rawResult.landmarks) {\n      const landmarks = rawResult.landmarks[landmarksIndex]\n      const subLandmark: Array<NormalizedLandmark> = []\n      let wrist: NormalizedLandmark\n      for (let i = 0; i < landmarks.length; i++) {\n        const landmark = landmarks[i]\n        const flippedLandmark = {\n          ...landmark,\n          x: reflect1D(landmark.x, 0.5),\n          visibility: 1,\n        }\n        if (i === HandPoseLandmark.WRIST) {\n          wrist = flippedLandmark\n        }\n        subLandmark.push(flippedLandmark)\n      }\n      const handedness = getHandedness(\n        wrist,\n        bodyPoseResult[BodyPoseLandmark.LEFT_WRIST],\n        bodyPoseResult[BodyPoseLandmark.RIGHT_WRIST],\n      )\n      processedResults[handedness] = [\n        rawResult.gestures[landmarksIndex][0].categoryName as HandGesture ?? HandGesture.NONE,\n        subLandmark,\n      ]\n    }\n    return processedResults\n  }\n\n}\n\n/**\n * @public\n */\nexport enum HandGesture {\n  NONE = 'None',\n  CLOSED_FIST = 'Closed_Fist',\n  OPEN_PALM = 'Open_Palm',\n  POINTING_UP = 'Pointing_Up',\n  THUMB_DOWN = 'Thumb_Down',\n  THUMB_UP = 'Thumb_Up',\n  VICTORY = 'Victory',\n  I_LOVE_YOU = 'ILoveYou',\n}\n","import {\n  degToRad,\n  fullyEnumerate,\n  getAngleFromPointsIn3D,\n  hasProperty,\n  NumericDataSet,\n  PartialRecord,\n} from '@glyph-cat/swiss-army-knife'\nimport { NormalizedLandmark } from '@mediapipe/hands'\nimport { HandPoseLandmark } from '../analyzers'\n\n/**\n * @public\n */\nexport enum Finger {\n  THUMB = 'T',\n  INDEX = 'I',\n  MIDDLE = 'M',\n  RING = 'R',\n  PINKY = 'P',\n}\nfullyEnumerate(Finger)\n\n/**\n * @public\n */\nexport enum FingerCurl {\n  STRAIGHT = 1,\n  HALF,\n  FULL,\n}\n\n/**\n * @public\n */\nexport type FingerCurlExpression = {\n  is: FingerCurl\n  isOneOf?: never\n  isNot?: never\n  isNotOneOf?: never\n} | {\n  is?: never\n  isOneOf: Array<FingerCurl>\n  isNot?: never\n  isNotOneOf?: never\n} | {\n  is?: never\n  isOneOf?: never\n  isNot: FingerCurl\n  isNotOneOf?: never\n} | {\n  is?: never\n  isOneOf?: never\n  isNot?: never\n  isNotOneOf: Array<FingerCurl>\n}\n\ninterface SimplifiedFingerCurlExpression {\n  curlStates: Set<FingerCurl>\n  is: boolean\n}\n\n/**\n * @public\n */\nexport class ComplexHandGesture {\n\n  static getFingerCurlAngles(\n    hand: Array<NormalizedLandmark>,\n    finger: Finger\n  ): Array<number> {\n    const fingerConnection = FingerConnections[finger]\n    const angles: Array<number> = []\n    for (let i = 0; i < (fingerConnection.length - 2); i++) {\n      const pointA = hand[fingerConnection[i]]\n      const midPoint = hand[fingerConnection[i + 1]]\n      const pointB = hand[fingerConnection[i + 2]]\n      angles.push(getAngleFromPointsIn3D(pointA, midPoint, pointB, midPoint))\n    }\n    return angles\n  }\n\n  static determineFingerCurl(\n    hand: Array<NormalizedLandmark>,\n    finger: Finger\n  ): FingerCurl {\n    const data = new NumericDataSet(this.getFingerCurlAngles(hand, finger))\n    if (finger !== Finger.THUMB && data.mean >= DEFAULT_STRAIGHT_THRESHOLD) {\n      return FingerCurl.STRAIGHT\n    } else if (data.mean >= THUMB_STRAIGHT_THRESHOLD) {\n      return FingerCurl.STRAIGHT\n    }\n    return diffDoesNotExceedDelta(data.values, FULL_CURL_ANGLE_LOOKUP[finger], 10)\n      ? FingerCurl.FULL\n      : FingerCurl.HALF\n  }\n\n  /**\n   * @internal\n   */\n  private readonly M$compactFingerCurlExpression: PartialRecord<Finger, Readonly<SimplifiedFingerCurlExpression>>\n\n  constructor(fingerCurlStates: PartialRecord<Finger, FingerCurlExpression>) {\n    const compactFingerCurlExpression: PartialRecord<Finger, Readonly<SimplifiedFingerCurlExpression>> = {}\n    for (const curlState in fingerCurlStates) {\n      const curlDefinition = fingerCurlStates[curlState as Finger]\n      if (hasProperty(curlDefinition, 'is')) {\n        compactFingerCurlExpression[curlState as Finger] = {\n          curlStates: new Set([curlDefinition.is]),\n          is: true,\n        }\n      } else if (hasProperty(curlDefinition, 'isOneOf')) {\n        compactFingerCurlExpression[curlState as Finger] = {\n          curlStates: new Set([...curlDefinition.isOneOf]),\n          is: true,\n        }\n      } else if (hasProperty(curlDefinition, 'isNot')) {\n        compactFingerCurlExpression[curlState as Finger] = {\n          curlStates: new Set([curlDefinition.isNot]),\n          is: false,\n        }\n      } else if (hasProperty(curlDefinition, 'isNotOneOf')) {\n        compactFingerCurlExpression[curlState as Finger] = {\n          curlStates: new Set([...curlDefinition.isNotOneOf]),\n          is: false,\n        }\n      }\n    }\n    this.M$compactFingerCurlExpression = compactFingerCurlExpression\n  }\n\n  isMatchedBy(hand: Array<NormalizedLandmark>): boolean {\n    for (const curlState in this.M$compactFingerCurlExpression) {\n      if (!hasProperty(this.M$compactFingerCurlExpression, curlState)) { continue }\n      const fingerCurlResult = ComplexHandGesture.determineFingerCurl(hand, curlState as Finger)\n      const curlDefinition = this.M$compactFingerCurlExpression[curlState as Finger]\n      if (curlDefinition.is) {\n        if (!curlDefinition.curlStates.has(fingerCurlResult)) {\n          return false\n        }\n      } else {\n        if (curlDefinition.curlStates.has(fingerCurlResult)) {\n          return false\n        }\n      }\n    }\n    return true\n  }\n\n}\n\nconst DEFAULT_STRAIGHT_THRESHOLD = degToRad(170)\nconst THUMB_STRAIGHT_THRESHOLD = degToRad(150)\n\n// TODO: We can further refine this based on pitch/roll/yaw of the hand\n// the lookup table below is just for front-facing gestures\nconst FULL_CURL_ANGLE_LOOKUP: Record<Finger, [number, number, number]> = {\n  [Finger.THUMB]: [degToRad(150), degToRad(160), degToRad(140)],\n  [Finger.INDEX]: [degToRad(155), degToRad(40), degToRad(175)],\n  [Finger.MIDDLE]: [degToRad(155), degToRad(40), degToRad(130)],\n  [Finger.RING]: [degToRad(145), degToRad(40), degToRad(125)],\n  [Finger.PINKY]: [degToRad(150), degToRad(35), degToRad(145)],\n}\n\n// TODO: better name\nfunction diffDoesNotExceedDelta(\n  a: Array<number> | ReadonlyArray<number>,\n  b: Array<number> | ReadonlyArray<number>,\n  delta: number\n): boolean {\n  for (let i = 0; i < a.length; i++) {\n    if (Math.abs(a[i] - b[i]) > delta) {\n      return false\n    }\n  }\n  return true\n}\n\n// TOFIX: may be we should not consider angle at DIP\n// Only consider angles at PIP and MCP, if both fulfill then consider full curl, if only one then half curl\n\nconst FingerConnections = {\n  [Finger.THUMB]: [\n    HandPoseLandmark.WRIST,\n    HandPoseLandmark.THUMB_CMC,\n    HandPoseLandmark.THUMB_MCP,\n    HandPoseLandmark.THUMB_IP,\n    HandPoseLandmark.THUMB_TIP,\n  ],\n  [Finger.INDEX]: [\n    HandPoseLandmark.WRIST,\n    HandPoseLandmark.INDEX_FINGER_MCP,\n    HandPoseLandmark.INDEX_FINGER_PIP,\n    HandPoseLandmark.INDEX_FINGER_DIP,\n    HandPoseLandmark.INDEX_FINGER_TIP,\n  ],\n  [Finger.MIDDLE]: [\n    HandPoseLandmark.WRIST,\n    HandPoseLandmark.MIDDLE_FINGER_MCP,\n    HandPoseLandmark.MIDDLE_FINGER_PIP,\n    HandPoseLandmark.MIDDLE_FINGER_DIP,\n    HandPoseLandmark.MIDDLE_FINGER_TIP,\n  ],\n  [Finger.RING]: [\n    HandPoseLandmark.WRIST,\n    HandPoseLandmark.RING_FINGER_MCP,\n    HandPoseLandmark.RING_FINGER_PIP,\n    HandPoseLandmark.RING_FINGER_DIP,\n    HandPoseLandmark.RING_FINGER_TIP,\n  ],\n  [Finger.PINKY]: [\n    HandPoseLandmark.WRIST,\n    HandPoseLandmark.PINKY_FINGER_MCP,\n    HandPoseLandmark.PINKY_FINGER_PIP,\n    HandPoseLandmark.PINKY_FINGER_DIP,\n    HandPoseLandmark.PINKY_FINGER_TIP,\n  ],\n} as const\n","import {\n  Awaitable,\n  createEnumToStringConverter,\n  LazyValue,\n  NotImplementedError,\n  StringRecord,\n} from '@glyph-cat/swiss-army-knife'\nimport { FilesetResolver } from '@mediapipe/tasks-vision'\nimport { SimpleFiniteStateManager, SimpleStateManager } from 'cotton-box'\nimport { VisionLandmarker, WasmFileset } from '../../abstractions'\nimport { VisionAnalyzerState } from '../../constants'\n\n/**\n * @public\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport class BaseVisionAnalyzer<TaskRunner extends StringRecord<any>, Result> {\n\n  private static _vision: Promise<WasmFileset>\n\n  static async getVision(): Promise<WasmFileset> {\n    if (!this._vision) {\n      this._vision = FilesetResolver.forVisionTasks(\n        // 'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm'\n        '/mediapipe/wasm'\n      )\n    }\n    return this._vision\n  }\n\n  protected taskRunner: TaskRunner\n  protected lastRequestedAnimationFrame: number\n  readonly result: SimpleStateManager<Result>\n  readonly state: SimpleFiniteStateManager<VisionAnalyzerState>\n\n  constructor(\n    readonly videoElement: HTMLVideoElement,\n    initialResult: Result,\n    taskRunnerGetter: LazyValue<Awaitable<TaskRunner>>,\n    readonly detectionMethodName: 'detectForVideo' | 'recognizeForVideo',\n    displayName: string,\n  ) {\n    this.start = this.start.bind(this)\n    this.stop = this.stop.bind(this)\n    this.getProcessedResult = this.getProcessedResult.bind(this)\n    this.dispose = this.dispose.bind(this)\n\n    // NOTE: `initialResult` was originally an extendable/inheritable property\n    // https://stackoverflow.com/a/43595944/5810737\n    this.result = new SimpleStateManager<Result>(initialResult)\n\n    this.state = new SimpleFiniteStateManager(VisionAnalyzerState.CREATED, [\n      [VisionAnalyzerState.CREATED, VisionAnalyzerState.INITIALIZING],\n      [VisionAnalyzerState.CREATED, VisionAnalyzerState.DISPOSED],\n      [VisionAnalyzerState.INITIALIZING, VisionAnalyzerState.STANDBY],\n      [VisionAnalyzerState.ACTIVE, VisionAnalyzerState.STANDBY],\n      [VisionAnalyzerState.STANDBY, VisionAnalyzerState.ACTIVE],\n      [VisionAnalyzerState.STANDBY, VisionAnalyzerState.DISPOSED],\n    ], {\n      // TODO: change `detectionMethodName` in constructor to visionAnalyzerName, then from there decide whether to call 'detectForVideo' or 'recognizeForVideo'; so that we can use the visionAnalyzerName for the state as well.\n      name: 'VisionAnalyzer',\n      serializeState: createEnumToStringConverter(VisionAnalyzerState),\n    })\n\n    const asyncCb = async () => {\n      this.state.set(VisionAnalyzerState.INITIALIZING)\n      this.taskRunner = await taskRunnerGetter.value\n      // In case state changed (for example, to disposed, halfway), then keep that state.\n      this.state.set((s) => s === VisionAnalyzerState.INITIALIZING ? VisionAnalyzerState.STANDBY : s)\n    }; asyncCb()\n  }\n\n  async start(): Promise<void> {\n    if (this.state.get() < VisionAnalyzerState.STANDBY) {\n      await this.state.wait((s) => s > VisionAnalyzerState.STANDBY)\n    } else if (this.state.get() !== VisionAnalyzerState.STANDBY) {\n      return // Early exit\n    }\n    this.state.set(VisionAnalyzerState.ACTIVE)\n    this.lastRequestedAnimationFrame = requestAnimationFrame(this.performAnalysis)\n  }\n\n  async stop(): Promise<void> {\n    await this.state.wait((s) => s === VisionAnalyzerState.CREATED || s > VisionAnalyzerState.STANDBY)\n    if (this.state.get() === VisionAnalyzerState.DISPOSED) { return } // Early exit\n    cancelAnimationFrame(this.lastRequestedAnimationFrame)\n    this.state.set(VisionAnalyzerState.STANDBY)\n  }\n\n  async dispose(): Promise<void> {\n    // NOTE: `taskRunner.close()` is not called and is meant to be kept until\n    // the app closes. Calling `taskRunner.close()` on a class lifecycle basis\n    // causes a lot of issues:\n    // - it seems like there is memory leakage even when `.close()` is called\n    // - even if there is no memory leakage, having to fetch and close repeatedly\n    //   make the app very slow\n    // - This is caught from within React's StrictMode + rapidly repeated soft reloads\n    // - This can be a problem when the user intentionally stops and resumes\n    //   the session multiple times\n    await this.state.wait((s) => s !== VisionAnalyzerState.INITIALIZING)\n    this.result.dispose()\n    this.state.set(VisionAnalyzerState.DISPOSED)\n    this.state.dispose()\n  }\n\n  private performAnalysis = async (): Promise<void> => {\n    if (this.state.get() !== VisionAnalyzerState.ACTIVE) { return } // Early exit\n    const result = this.taskRunner[this.detectionMethodName](this.videoElement, performance.now())\n    const processedResult = this.getProcessedResult(result as ReturnType<TaskRunner[typeof this.detectionMethodName]>)\n    if (processedResult) { this.result.set(processedResult) }\n    this.lastRequestedAnimationFrame = requestAnimationFrame(this.performAnalysis)\n  }\n\n  protected getProcessedResult(\n    // eslint-disable-next-line @typescript-eslint/no-unused-vars\n    rawResult: ReturnType<TaskRunner[typeof this.detectionMethodName]>\n  ): Result {\n    throw new NotImplementedError()\n  }\n\n}\n\n/**\n * @public\n */\nexport class BaseLandmarkAnalyzer<Landmarker extends VisionLandmarker, Result> extends BaseVisionAnalyzer<Landmarker, Result> {\n\n  constructor(\n    videoElement: HTMLVideoElement,\n    initialResult: Result,\n    taskRunnerGetter: LazyValue<Awaitable<Landmarker>>,\n    displayName: string,\n  ) {\n    super(\n      videoElement,\n      initialResult,\n      taskRunnerGetter,\n      'detectForVideo',\n      displayName,\n    )\n  }\n\n}\n","import { getDistance2DByCoordinates, Value3D } from '@glyph-cat/swiss-army-knife'\nimport type { OnePersonHandPoseAnalyzerResult } from '.'\n\n/**\n * @public\n */\nexport function getHandedness(\n  currentWrist: Value3D,\n  leftWrist: Value3D,\n  rightWrist: Value3D,\n): keyof OnePersonHandPoseAnalyzerResult {\n  // z-position of hand landmark can be up to `4`, which screws up the distance calculation\n  // since we only care about what's being translated on screen, x and y should be enough.\n  const leftWristDelta = getDistance2DByCoordinates(currentWrist, leftWrist)\n  const rightWristDelta = getDistance2DByCoordinates(currentWrist, rightWrist)\n  return leftWristDelta < rightWristDelta ? 'L' : 'R'\n}\n","import { Value2D } from '@glyph-cat/swiss-army-knife'\n\n/**\n * @public\n */\nexport interface DrawConnectorOptions {\n  /**\n   * @defaultValue `'#ffffff'`\n   */\n  color?: string\n  /**\n   * @defaultValue `1`\n   */\n  lineWidth?: number\n}\n\nconst defaultDrawConnectorOptions: Readonly<Required<DrawConnectorOptions>> = {\n  color: '#ffffff',\n  lineWidth: 1,\n}\n\n/**\n * @public\n */\nexport function drawConnectors(\n  ctx: CanvasRenderingContext2D,\n  landmarks: Array<Value2D>,\n  connections: Array<[start: number, end: number]>,\n  style?: DrawConnectorOptions\n): void {\n  const mergedStyle = { ...defaultDrawConnectorOptions, ...style }\n  for (const connection of connections) {\n    const [landmarkKeyA, landmarkKeyB] = connection\n    const pointA = landmarks[landmarkKeyA]\n    const pointB = landmarks[landmarkKeyB]\n    ctx.beginPath()\n    ctx.moveTo(pointA.x * ctx.canvas.width, pointA.y * ctx.canvas.height)\n    ctx.lineTo(pointB.x * ctx.canvas.width, pointB.y * ctx.canvas.height)\n    ctx.lineWidth = mergedStyle.lineWidth\n    ctx.strokeStyle = mergedStyle.color\n    ctx.stroke()\n  }\n}\n","import { Value2D } from '@glyph-cat/swiss-army-knife'\n\n/**\n * @public\n */\nexport interface DrawLandmarkOptions {\n  /**\n   * @defaultValue `'#ffffff'`\n   */\n  color?: string\n  /**\n   * @defaultValue `3`\n   */\n  radius?: number\n}\n\nconst defaultDrawLandmarkOptions: Readonly<Required<DrawLandmarkOptions>> = {\n  color: '#ffffff',\n  radius: 3,\n}\n\n/**\n * @public\n */\nexport function drawLandmarks(\n  ctx: CanvasRenderingContext2D,\n  landmarks: Array<Value2D>,\n  style?: DrawLandmarkOptions\n): void {\n  const mergedStyle = { ...defaultDrawLandmarkOptions, ...style }\n  for (const landmark of landmarks) {\n    const { x, y } = landmark\n    ctx.beginPath()\n    ctx.arc(x * ctx.canvas.width, y * ctx.canvas.height, style.radius, 0, 2 * Math.PI)\n    ctx.fillStyle = mergedStyle.color\n    ctx.fill()\n  }\n}\n","import {\n  getAngleFromPointsIn3D,\n  isInRange,\n  NumericDataSet,\n  StringRecord,\n} from '@glyph-cat/swiss-army-knife'\nimport { NormalizedLandmark } from '@mediapipe/hands'\nimport { HandPoseLandmark } from '../analyzers'\nimport { Finger } from '../complex-hand-gesture'\n\n// KIV: Still experimental\n\ninterface ProcessedDataPoint {\n  mean: number\n  stdDev: number\n}\n\n/**\n * @internal\n */\nexport class HandGestureSnapshot {\n\n  static getFingerCurlAngles(\n    hand: Array<NormalizedLandmark>,\n    finger: Finger\n  ): StringRecord<number> {\n    const fingerConnection = FingerConnections[finger]\n    const angles: StringRecord<number> = {}\n    for (let i = 0; i < (fingerConnection.length - 2); i++) {\n      const connectionA = fingerConnection[i]\n      const connectionMidpoint = fingerConnection[i + 1]\n      const connectionB = fingerConnection[i + 2]\n      const jointConnectionKey = [connectionA, connectionMidpoint, connectionB].join('-')\n      const pointA = hand[connectionA]\n      const midPoint = hand[connectionMidpoint]\n      const pointB = hand[connectionB]\n      angles[jointConnectionKey] = getAngleFromPointsIn3D(pointA, midPoint, pointB, midPoint)\n    }\n    return angles\n  }\n\n  private readonly processedPoints: StringRecord<ProcessedDataPoint>\n\n  constructor(landmarkSnapshots: Array<Array<NormalizedLandmark>>) {\n    const halfProcessedPoints: StringRecord<Array<number>> = {}\n    for (const landmarkSnapshot of landmarkSnapshots) {\n      for (const finger in FingerConnections) {\n        const angles = HandGestureSnapshot.getFingerCurlAngles(landmarkSnapshot, finger as Finger)\n        for (const jointConnectionKey in angles) {\n          if (!halfProcessedPoints[jointConnectionKey]) {\n            halfProcessedPoints[jointConnectionKey] = []\n          }\n          halfProcessedPoints[jointConnectionKey].push(angles[jointConnectionKey])\n        }\n      }\n    }\n    const processedPoints: StringRecord<ProcessedDataPoint> = {}\n    for (const jointConnectionKey in halfProcessedPoints) {\n      const numericDataSet = new NumericDataSet(halfProcessedPoints[jointConnectionKey])\n      processedPoints[jointConnectionKey] = {\n        mean: numericDataSet.mean,\n        stdDev: numericDataSet.stddev,\n      }\n    }\n    this.processedPoints = processedPoints\n  }\n\n  isMatchedBy(hand: Array<NormalizedLandmark>, sigma: number = 2): boolean {\n    for (const finger in FingerConnections) {\n      const angles = HandGestureSnapshot.getFingerCurlAngles(hand, finger as Finger)\n      for (const jointConnectionKey in angles) {\n        const { mean, stdDev } = this.processedPoints[jointConnectionKey]\n        // temp\n        // console.log(`stdDev for ${jointConnectionKey}`, stdDev)\n        // console.log('='.repeat(20))\n        // console.log(`angle for ${jointConnectionKey}`, angles[jointConnectionKey])\n        // console.log([mean - (stdDev * sigma), mean, mean + (stdDev * sigma)].join(' <-> '))\n        // console.log('is in range', isInRange(\n        //   angles[jointConnectionKey],\n        //   mean - (stdDev * sigma),\n        //   mean + (stdDev * sigma),\n        // ))\n        if (!isInRange(\n          angles[jointConnectionKey],\n          mean - (stdDev * sigma),\n          mean + (stdDev * sigma),\n        )) { return false }\n      }\n    }\n    return true\n  }\n\n}\n\nconst FingerConnections = {\n  [Finger.THUMB]: [\n    HandPoseLandmark.WRIST,\n    HandPoseLandmark.THUMB_CMC,\n    HandPoseLandmark.THUMB_MCP,\n    HandPoseLandmark.THUMB_IP,\n    HandPoseLandmark.THUMB_TIP,\n  ],\n  [Finger.INDEX]: [\n    HandPoseLandmark.WRIST,\n    HandPoseLandmark.INDEX_FINGER_MCP,\n    HandPoseLandmark.INDEX_FINGER_PIP,\n    HandPoseLandmark.INDEX_FINGER_DIP,\n    HandPoseLandmark.INDEX_FINGER_TIP,\n  ],\n  [Finger.MIDDLE]: [\n    HandPoseLandmark.WRIST,\n    HandPoseLandmark.MIDDLE_FINGER_MCP,\n    HandPoseLandmark.MIDDLE_FINGER_PIP,\n    HandPoseLandmark.MIDDLE_FINGER_DIP,\n    HandPoseLandmark.MIDDLE_FINGER_TIP,\n  ],\n  [Finger.RING]: [\n    HandPoseLandmark.WRIST,\n    HandPoseLandmark.RING_FINGER_MCP,\n    HandPoseLandmark.RING_FINGER_PIP,\n    HandPoseLandmark.RING_FINGER_DIP,\n    HandPoseLandmark.RING_FINGER_TIP,\n  ],\n  [Finger.PINKY]: [\n    HandPoseLandmark.WRIST,\n    HandPoseLandmark.PINKY_FINGER_MCP,\n    HandPoseLandmark.PINKY_FINGER_PIP,\n    HandPoseLandmark.PINKY_FINGER_DIP,\n    HandPoseLandmark.PINKY_FINGER_TIP,\n  ],\n} as const\n"],"names":["HAND_CONNECTIONS","VisionAnalyzerState","BodyPoseLandmark","HandPoseLandmark","HandGesture","Finger","FingerCurl","BaseVisionAnalyzer","videoElement","detectionMethodName","static","getVision","this","_vision","FilesetResolver","forVisionTasks","taskRunner","lastRequestedAnimationFrame","result","state","constructor","initialResult","taskRunnerGetter","displayName","start","bind","stop","getProcessedResult","dispose","SimpleStateManager","SimpleFiniteStateManager","CREATED","INITIALIZING","DISPOSED","STANDBY","ACTIVE","name","serializeState","createEnumToStringConverter","async","set","value","s","asyncCb","get","wait","requestAnimationFrame","performAnalysis","cancelAnimationFrame","performance","now","processedResult","rawResult","NotImplementedError","BaseLandmarkAnalyzer","super","OnePersonBodyPoseAnalyzer","LazyValue","PoseLandmarker","createFromOptions","baseOptions","modelAssetPath","delegate","numPoses","runningMode","Array","Object","keys","length","fill","x","y","z","visibility","M$taskRunnerGetter","processedLandmarks","landmarks","landmark","push","reflect1D","getHandedness","currentWrist","leftWrist","rightWrist","getDistance2DByCoordinates","OnePersonHandPoseAnalyzer","bodyPoseAnalyzer","HandLandmarker","numHands","bodyPoseResult","subLandmark","wrist","i","flippedLandmark","WRIST","LEFT_WRIST","RIGHT_WRIST","OnePersonHandGestureAnalyzer","GestureRecognizer","processedResults","landmarksIndex","gestures","categoryName","NONE","fullyEnumerate","ComplexHandGesture","getFingerCurlAngles","hand","finger","fingerConnection","FingerConnections","angles","pointA","midPoint","pointB","getAngleFromPointsIn3D","determineFingerCurl","data","NumericDataSet","THUMB","mean","DEFAULT_STRAIGHT_THRESHOLD","THUMB_STRAIGHT_THRESHOLD","STRAIGHT","a","b","delta","Math","abs","diffDoesNotExceedDelta","values","FULL_CURL_ANGLE_LOOKUP","FULL","HALF","M$compactFingerCurlExpression","fingerCurlStates","compactFingerCurlExpression","curlState","curlDefinition","hasProperty","curlStates","Set","is","isOneOf","isNot","isNotOneOf","isMatchedBy","fingerCurlResult","has","degToRad","INDEX","MIDDLE","RING","PINKY","THUMB_CMC","THUMB_MCP","THUMB_IP","THUMB_TIP","INDEX_FINGER_MCP","INDEX_FINGER_PIP","INDEX_FINGER_DIP","INDEX_FINGER_TIP","MIDDLE_FINGER_MCP","MIDDLE_FINGER_PIP","MIDDLE_FINGER_DIP","MIDDLE_FINGER_TIP","RING_FINGER_MCP","RING_FINGER_PIP","RING_FINGER_DIP","RING_FINGER_TIP","PINKY_FINGER_MCP","PINKY_FINGER_PIP","PINKY_FINGER_DIP","PINKY_FINGER_TIP","defaultDrawConnectorOptions","color","lineWidth","drawConnectors","ctx","connections","style","mergedStyle","connection","landmarkKeyA","landmarkKeyB","beginPath","moveTo","canvas","width","height","lineTo","strokeStyle","stroke","defaultDrawLandmarkOptions","radius","drawLandmarks","arc","PI","fillStyle","HandGestureSnapshot","connectionA","connectionMidpoint","connectionB","jointConnectionKey","join","processedPoints","landmarkSnapshots","halfProcessedPoints","landmarkSnapshot","numericDataSet","stdDev","stddev","sigma","isInRange"],"mappings":"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"}