예제 #1
0
    private float DuckPinchMetric(Leap.Hand hand)
    {
        Vector3 thumbDistal = hand.GetThumb().bones[3].PrevJoint.ToVector3();
        Vector3 thumbTip    = hand.GetThumb().TipPosition.ToVector3();

        Vector3 indexMetacarpal = hand.GetIndex().bones[0].PrevJoint.ToVector3();
        Vector3 indexProximal   = hand.GetIndex().bones[1].PrevJoint.ToVector3();
        Vector3 indexTip        = hand.GetIndex().TipPosition.ToVector3();

        Vector3 middleMetacarpal = hand.GetMiddle().bones[0].PrevJoint.ToVector3();
        Vector3 middleTip        = hand.GetMiddle().bones[3].PrevJoint.ToVector3();

        Vector3 ringMetacarpal = hand.GetMiddle().bones[0].PrevJoint.ToVector3();
        Vector3 ringTip        = hand.GetRing().bones[3].PrevJoint.ToVector3();


        // Project all except thumb onto a plane
        Vector3 projectionPlane = Vector3.Cross(hand.GetIndex().bones[2].Direction.ToVector3(), hand.PalmarAxis()).normalized;

        if (hand.IsRight)
        {
            // convert to left-handed-rule for Unity
            projectionPlane *= -1f;
        }

        Vector3 planeOrigin = indexProximal;

        indexProximal    = Vector3.zero;
        indexMetacarpal  = Vector3.ProjectOnPlane(indexMetacarpal - planeOrigin, projectionPlane) + planeOrigin;
        indexTip         = Vector3.ProjectOnPlane(indexTip - planeOrigin, projectionPlane) + planeOrigin;
        middleMetacarpal = Vector3.ProjectOnPlane(middleMetacarpal - planeOrigin, projectionPlane) + planeOrigin;
        middleTip        = Vector3.ProjectOnPlane(middleTip - planeOrigin, projectionPlane) + planeOrigin;
        ringMetacarpal   = Vector3.ProjectOnPlane(ringMetacarpal - planeOrigin, projectionPlane) + planeOrigin;
        ringTip          = Vector3.ProjectOnPlane(ringTip - planeOrigin, projectionPlane) + planeOrigin;


        // Limit thumb positions
        float thumbDistalOverlap = Vector3.Dot(thumbDistal - planeOrigin, projectionPlane);

        if (thumbDistalOverlap < 0)
        {
            thumbDistal += thumbDistalOverlap * projectionPlane;
        }
        float thumbTipOverlap = Vector3.Dot(thumbTip - planeOrigin, projectionPlane);

        if (thumbTipOverlap < 0)
        {
            thumbTip += thumbTipOverlap * projectionPlane;
        }

        float indexMetric  = SegmentDisplacement.SegmentToSegmentDistance(indexMetacarpal, indexTip, thumbDistal, thumbTip);
        float middleMetric = SegmentDisplacement.SegmentToSegmentDistance(middleMetacarpal, middleTip, thumbDistal, thumbTip);
        float ringMetric   = SegmentDisplacement.SegmentToSegmentDistance(ringMetacarpal, ringTip, thumbDistal, thumbTip);

        float wipMetric = (indexMetric + middleMetric + ringMetric) / (3.0f);

        metric = Mathf.Max(0f, wipMetric - 0.01f);

        return(metric);
    }
예제 #2
0
    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);
    }