public_hand_gesture.js

/* Copyright (C) 2022 Kraft, Royapally, Sarthi, Ramaswamy, Maduru, Harde- All Rights Reserved
 * You may use, distribute and modify this code under the
 * terms of the MIT license that can be found in the LICENSE file or
 * at https://opensource.org/licenses/MIT.
 * You should have received a copy of the MIT license with
 * this file. If not, please write to: develop.nak@gmail.com, or visit https://github.com/SiddarthR56/spark/blob/main/README.md.
 */

/* eslint-disable no-unused-vars */
var start_tracking = false;
var gesturesEnabled = true;
var x1 = 0;
var x2 = 0;

/**
 * Enum for the different types of gestures Spark recognizes.
 *
 * @type {Object}
 */
const Gesture = Object.freeze({
  RightSwipe: 1,
  LeftSwipe: 2,
  All5Fingers: 3,
  ThumbsDown: 4,
  ThumbsUp: 5,
  NoDetection: 6,
});

/**
 * Determines what gesture is being shown based on the given hand detection data.
 *
 * @function
 * @param {Object} results Hand detection data
 * @returns {Object} The gesture enum that was determined
 */
function onResults(results) {
  if (results.multiHandLandmarks.length != 0) {
    var [lsit, box] = findhandpos(results.multiHandLandmarks[0]);
  } else {
    lsit = [];
    if (start_tracking == true) {
      //Stop Tracking
      start_tracking = false;
      if (gesturesEnabled == true) {
        if (x1[0] > x2[0]) {
          //Gesture 1 'right swipe';
          return Gesture.RightSwipe;
        } else {
          //Gesture 2 'left swipe'
          return Gesture.LeftSwipe;
        }
      }
    }
  }

  if (lsit.length != 0) {
    var fings = detect_fingersup(lsit);

    if (fings[1] == true && fings[3] == false && fings[4] == false) {
      if (start_tracking == false) {
        //start tracking
        start_tracking = true;
        x1 = lsit[8].slice(1, 2);
        //console.log(x1)
      } else {
        x2 = lsit[8].slice(1, 2);
      }
    } else {
      start_tracking = false;

      if (fings[0] == true && fings[1] == true && fings[2] == true && fings[3] == true && fings[4] == true) {
        //Gesture 3 'all five fingers';
        return Gesture.All5Fingers;
      } else if (fings[0] == true && fings[1] == false && fings[2] == false && fings[3] == false && fings[4] == false) {
        var y1 = lsit[4].slice(2, 3);
        var y2 = lsit[2].slice(2, 3);
        if (gesturesEnabled == true) {
          if (y1[0] > y2[0]) {
            //Gesture 4 'Thumbs Down';
            return Gesture.ThumbsDown;
          } else {
            //Gesture 5 'Thumbs Up';
            return Gesture.ThumbsUp;
          }
        }
      }
    }
  }

  // Default to no recognizable detection
  return Gesture.NoDetection;
}

/**
 * Helper function to determine the x-y coordinates of the positions of the hand
 *
 * @function
 * @param {Object} landmarks
 * @returns {Array} x-y coordinates of the hand data
 */
function findhandpos(landmarks) {
  var xlist = [];
  var ylist = [];
  var lmlist = [];
  var bbox = [];
  for (const [index, element] of landmarks.entries()) {
    var [h, w, c] = [900, 1600, 3];
    var cx = parseInt(element.x * w);
    var cy = parseInt(element.y * h);
    xlist.push(cx);
    ylist.push(cy);
    lmlist.push([index, cx, cy]);
  }
  var xmin = Math.min(xlist);
  var xmax = Math.max(xlist);
  var ymin = Math.min(ylist);
  var ymax = Math.max(ylist); // Even if unused here, it is needed for the Hands result calculation
  bbox = [xmin, ymin, xmax, xmin];

  return [lmlist, bbox];
}

/**
 * Determines how many fingers are pointing "up", using arrays to store the result.
 *
 * @function
 * @param {Array} lmlist
 * @returns {Array} The fingers IDs that are found as "up"
 */
function detect_fingersup(lmlist) {
  var fingers = [];
  var ids = [4, 8, 12, 16, 20];
  if (lmlist[ids[0]][1] < lmlist[ids[0] - 2][1]) {
    fingers.push(1);
  } else {
    fingers.push(0);
  }
  for (let i = 1; i < 5; i++) {
    if (lmlist[ids[i]][2] < lmlist[ids[i] - 2][2]) {
      fingers.push(1);
    } else {
      fingers.push(0);
    }
  }
  return fingers;
}

// Necessary for client-side handling and for tests
try {
  module.exports = { onResults, Gesture };
} catch (error) {
  //pass
}