private float GetAngle(Leap.Hand hand) { Vector3 proximalAxis = hand.DistalAxis() * -1f; Vector3 radialAxis = hand.RadialAxis(); if (hand.IsLeft) { radialAxis *= -1f; } List <float> fingerAngles = new List <float> { Vector3.SignedAngle(proximalAxis, hand.GetIndex().Direction.ToVector3(), radialAxis), Vector3.SignedAngle(proximalAxis, hand.GetMiddle().Direction.ToVector3(), radialAxis), Vector3.SignedAngle(proximalAxis, hand.GetRing().Direction.ToVector3(), radialAxis), Vector3.SignedAngle(proximalAxis, hand.GetPinky().Direction.ToVector3(), radialAxis) }; List <float> fingerAnglesShifted = new List <float>(); foreach (float angle in fingerAngles) { float shiftedAngle = angle; if (angle < -90f) { shiftedAngle += 360f; } fingerAnglesShifted.Add(shiftedAngle); } angle = 0.25f * (fingerAnglesShifted[0] + fingerAnglesShifted[1] + fingerAnglesShifted[2] + fingerAnglesShifted[3]); return(angle); }
bool CheckHandForActivation(Leap.Hand hand, bool wasEligibleLastCheck) { bool shouldActivate = false; float latestPinchDistance = GetCustomPinchDistance(hand); Vector3 palmDir = hand.PalmarAxis(); Vector3 middleDir = hand.GetMiddle().bones[1].Direction.ToVector3(); float signedMiddlePalmAngle = Vector3.SignedAngle(palmDir, middleDir, hand.RadialAxis()); if (hand.IsLeft) { signedMiddlePalmAngle *= -1f; } Vector3 ringDir = hand.GetRing().bones[1].Direction.ToVector3(); float signedRingPalmAngle = Vector3.SignedAngle(palmDir, ringDir, hand.RadialAxis()); if (hand.IsLeft) { signedRingPalmAngle *= -1f; } Vector3 indexDir = hand.GetIndex().bones[1].Direction.ToVector3(); float indexPalmAngle = Vector3.Angle(indexDir, palmDir); Vector3 thumbDir = hand.GetThumb().bones[2].Direction.ToVector3(); float thumbPalmAngle = Vector3.Angle(thumbDir, palmDir); // Eligibility checks-- necessary, but not sufficient conditions to start // a pinch, suitable for e.g. visual feedback on whether the gesture is // "able to occur" or "about to occur." if ( ((!wasEligibleLastCheck && signedMiddlePalmAngle >= minPalmMiddleAngle) || (wasEligibleLastCheck && signedMiddlePalmAngle >= minPalmMiddleAngle * ringMiddleSafetyHysteresisMult) || !requireMiddleAndRingSafetyPinch) && ((!wasEligibleLastCheck && signedRingPalmAngle >= minPalmRingAngle) || (wasEligibleLastCheck && signedRingPalmAngle >= minPalmRingAngle * ringMiddleSafetyHysteresisMult) || !requireMiddleAndRingSafetyPinch) // Index angle (eligibility state only) && ((!wasEligibleLastCheck && indexPalmAngle < maxIndexAngleForEligibilityActivation) || (wasEligibleLastCheck && indexPalmAngle < maxIndexAngleForEligibilityDeactivation)) // Thumb angle (eligibility state only) && ((!wasEligibleLastCheck && thumbPalmAngle < maxThumbAngleForEligibilityActivation) || (wasEligibleLastCheck && thumbPalmAngle < maxThumbAngleForEligibilityDeactivation)) // Must cross pinch threshold from a non-pinching / non-fist pose. && (!requiresRepinch) ) { // Conceptually, this should be true when all but the most essential // parameters for the gesture are satisfied, so the user can be notified // that the gesture is imminent. _isGestureEligible = true; } #region Update Pinch Strength // Update global "pinch strength". // If the gesture is eligible, we'll have a non-zero pinch strength. if (_isGestureEligible) { _latestPinchStrength = latestPinchDistance.Map(0f, pinchActivateDistance, 1f, 0f); } else { _latestPinchStrength = 0f; } #endregion #region Check: Pinch Distance if (_isGestureEligible // Absolute pinch strength. && (latestPinchDistance < pinchActivateDistance) ) { shouldActivate = true; } #endregion #region Hysteresis for Failed Pinches // "requiresRepinch" prevents a closed-finger configuration from beginning // a pinch when the index and thumb never actually actively close from a // valid position -- think, closed-fist to safety-pinch, as opposed to // open-hand to safety-pinch -- without introducing any velocity-based // requirement. if (latestPinchDistance < pinchActivateDistance && !shouldActivate) { requiresRepinch = true; } if (requiresRepinch && latestPinchDistance > failedPinchResetDistance) { requiresRepinch = false; } #endregion return(shouldActivate); }