示例#1
0
        /**
         * Draws lines from elbow to wrist, wrist to palm, and normal to the palm.
         * Also draws the orthogonal basis vectors for the pinch and grab points.
         */
        protected void DrawDebugLines()
        {
            RuntimeGizmoDrawer gizmoDrawer;

            if (RuntimeGizmoManager.TryGetGizmoDrawer(gameObject, out gizmoDrawer))
            {
                Hand hand = GetLeapHand();
                gizmoDrawer.color = Color.red;
                gizmoDrawer.DrawLine(hand.Arm.ElbowPosition.ToVector3(), hand.Arm.WristPosition.ToVector3());
                gizmoDrawer.color = Color.white;
                gizmoDrawer.DrawLine(hand.WristPosition.ToVector3(), hand.PalmPosition.ToVector3());                                         //Wrist to palm line
                gizmoDrawer.color = Color.black;
                gizmoDrawer.DrawLine(hand.PalmPosition.ToVector3(), (hand.PalmPosition + hand.PalmNormal * hand.PalmWidth / 2).ToVector3()); //Hand Normal
                if (PinchPoint != null)
                {
                    DrawBasis(gizmoDrawer, PinchPoint.position, PinchPoint.GetLeapMatrix(), .01f); //Pinch basis
                }
                if (GrabPoint != null)
                {
                    DrawBasis(gizmoDrawer, GrabPoint.position, GrabPoint.GetLeapMatrix(), .01f); //Grab basis
                }
                for (int f = 0; f < 5; f++)                                                      //Fingers
                {
                    Finger finger = hand.Fingers[f];
                    for (int i = 0; i < 4; ++i)
                    {
                        Bone bone = finger.Bone((Bone.BoneType)i);
                        gizmoDrawer.color = colors[i];
                        gizmoDrawer.DrawLine(bone.PrevJoint.ToVector3(), bone.PrevJoint.ToVector3() + bone.Direction.ToVector3() * bone.Length);
                    }
                }
            }
        }
示例#2
0
        public override bool IsGesturePoseHeld(Hand leftHand, Hand rightHand,
                                               out Vector3 positionOfInterest)
        {
            var leftHandStraightAmount  = 1f - leftHand.GetFistStrength();
            var rightHandStraightAmount = 1f - rightHand.GetFistStrength();

            var isLeftHandStraight  = leftHandStraightAmount > 0.80f;
            var isRightHandStraight = rightHandStraightAmount > 0.80f;

            if (drawHeldPoseDebug)
            {
                RuntimeGizmoDrawer drawer = null;
                if (RuntimeGizmoManager.TryGetGizmoDrawer(out drawer))
                {
                    drawer.DrawBar(leftHandStraightAmount,
                                   Vector3.down * 0.2f + Vector3.left * 0.20f,
                                   Vector3.up,
                                   isLeftHandStraight ?
                                   LeapColor.white
                        : LeapColor.amber,
                                   scale: 0.2f);
                    drawer.DrawBar(rightHandStraightAmount,
                                   Vector3.down * 0.2f + Vector3.left * 0.10f,
                                   Vector3.up,
                                   isRightHandStraight ?
                                   LeapColor.white
                         : LeapColor.brown,
                                   scale: 0.2f);
                }
            }

            var areThumbsParallel = Vector3.Angle(leftHand.RadialAxis(),
                                                  rightHand.RadialAxis()) < MAX_ALIGNED_ANGLE;

            var leftMiddleTip            = leftHand.Fingers[2].TipPosition.ToVector3();
            var rightMiddleTip           = rightHand.Fingers[2].TipPosition.ToVector3();
            var areMiddleFingersTouching = (leftMiddleTip - rightMiddleTip)
                                           .sqrMagnitude < MAX_TOUCHING_DISTANCE_SQR;

            var handsAngle = Vector3.SignedAngle(rightHand.DistalAxis(),
                                                 leftHand.DistalAxis(),
                                                 rightHand.RadialAxis());
            var areHandsInUpwardTriangle = handsAngle.IsBetween(30f, 135f);

            positionOfInterest = Vector3.zero;
            bool isGesturePoseHeld = isLeftHandStraight &&
                                     isRightHandStraight &&
                                     areThumbsParallel &&
                                     areMiddleFingersTouching &&
                                     areHandsInUpwardTriangle;

            if (isGesturePoseHeld)
            {
                positionOfInterest = (leftMiddleTip + rightMiddleTip) / 2f;
            }

            return(isGesturePoseHeld);
        }
        private void drawControllerRuntimeGizmoDecorator(SerializedProperty property)
        {
            if (property.boolValue && _runtimeGizmoManager == null)
            {
                _runtimeGizmoManager = FindObjectOfType <RuntimeGizmoManager>();

                if (_runtimeGizmoManager == null)
                {
                    EditorGUILayout.Space();
                    EditorGUILayout.Space();
                    EditorGUILayout.HelpBox("Draw Controller Runtime Gizmos is checked, but there "
                                            + "is no RuntimeGizmoManager in your scene, or it is "
                                            + "disabled.", MessageType.Warning);
                }
            }
        }
        private bool updateIsRelativeHandVelocityLow(Hand leftHand, Hand rightHand)
        {
            var leftHandPos  = leftHand.PalmPosition.ToVector3();
            var rightHandPos = rightHand.PalmPosition.ToVector3();
            var leftMinusRightHandPosition = leftHandPos - rightHandPos;

            leftMinusRightHandPosBuffer.Add(leftMinusRightHandPosition, Time.time);

            if (leftMinusRightHandPosBuffer.IsFull)
            {
                var relativeHandVelocity = leftMinusRightHandPosBuffer.Delta();

                if (drawHeldPoseDebug)
                {
                    RuntimeGizmoDrawer drawer = null;
                    if (RuntimeGizmoManager.TryGetGizmoDrawer(out drawer))
                    {
                        drawer.DrawBar(relativeHandVelocity.magnitude,
                                       (leftHandPos + rightHandPos) / 2f,
                                       Vector3.up, 0.1f);
                    }
                }

                if (relativeHandVelocity.sqrMagnitude < MAX_RELATIVE_HAND_VELOCITY_SQR)
                {
                    return(true);
                }
                else
                {
                    if (drawHeldPoseDebug)
                    {
                        DebugPing.Ping((leftHandPos + rightHandPos) / 2f, LeapColor.black, 0.2f);
                    }
                    return(false);
                }
            }

            if (drawHeldPoseDebug)
            {
                DebugPing.Ping((leftHandPos + rightHandPos) / 2f, LeapColor.gray, 0.1f);
            }

            return(false);
        }
示例#5
0
        private void updateCylinders()
        {
            if (cylinders == null || cylinders.Length == 0)
            {
                return;
            }


            var heightPerCylinder = height / cylinders.Length;

            RuntimeGizmoDrawer debugDrawer = null;

            {
                RuntimeGizmoManager.TryGetGizmoDrawer(out debugDrawer);
            }

            for (int i = 0; i < cylinders.Length; i++)
            {
                var cylinder = cylinders[i];

                if (cylinder == null)
                {
                    continue;
                }

                float cylinderIdxCoeff;
                if (cylinders.Length == 1)
                {
                    cylinderIdxCoeff = 1;
                }
                else
                {
                    cylinderIdxCoeff = (i / ((float)cylinders.Length - 1));
                }

                cylinder.height             = heightPerCylinder;
                cylinder.width              = Mathf.Lerp(bottomRadius, topRadius, cylinderIdxCoeff);
                cylinder.transform.position = this.transform.position
                                              - this.transform.up
                                              * (height - (heightPerCylinder * i) - heightPerCylinder / 2f);
            }
        }
示例#6
0
        private void Update()
        {
            var targetAmount = _targetOn ? 1f : 0f;

            if (_onAmount != targetAmount)
            {
                var speed = 1 / openWidgetsTime * (_targetOn ? 1f : -1f);
                _onAmount = Mathf.Clamp01(_onAmount + speed * Time.deltaTime);
            }

            updateTransition(_onAmount, isActivating: _targetOn);

            // Optional line to each menu:
            if (_flashLine)
            {
                if (drawLineFromTransform != null)
                {
                    _lineAlpha = 1f;
                }
                _flashLine = false;
            }
            if (_lineAlpha > 0.001f)
            {
                RuntimeGizmoDrawer drawer;
                if (RuntimeGizmoManager.TryGetGizmoDrawer(out drawer))
                {
                    drawer.color = drawLinesColor.WithAlpha(_lineAlpha);
                    foreach (var widgetTransform in panelWidgetTransforms)
                    {
                        drawer.DrawDashedLine(drawLineFromTransform.position,
                                              widgetTransform.position,
                                              segmentsPerMeter: 20);
                    }
                }
                _lineAlpha = Mathf.Lerp(_lineAlpha, 0f, _lineDrawLerpCoeff * Time.deltaTime);
            }
        }
        protected virtual void FixedUpdate()
        {
            Frame frame = _leapProvider.CurrentFixedFrame;

            if (OnPrePhysicalUpdate != null)
            {
                OnPrePhysicalUpdate();
            }

            simulateFrame(frame);

            if (OnPostPhysicalUpdate != null)
            {
                OnPostPhysicalUpdate();
            }

            if (_showDebugLines)
            {
                RuntimeGizmoDrawer gizmoDrawer;
                if (RuntimeGizmoManager.TryGetGizmoDrawer(gameObject, out gizmoDrawer))
                {
                    InteractionC.GetDebugLines(ref _scene, _debugLines);
                    for (int i = 0; i < _debugLines.Count; i++)
                    {
                        var line = _debugLines[i];
                        gizmoDrawer.color = line.color.ToUnityColor();
                        gizmoDrawer.DrawLine(line.start.ToVector3(), line.end.ToVector3());
                    }
                }
            }

            if (_showDebugOutput)
            {
                InteractionC.GetDebugStrings(ref _scene, _debugOutput);
            }
        }
        private void LateUpdate()
        {
            bool isDiscontinuity = false;

            //Check if the previous frame said we were going to be discontinuous
            if (_willBeDiscontinuousNextFrame)
            {
                isDiscontinuity = true;
                _willBeDiscontinuousNextFrame = false;
            }

            //If we switched to a different asset
            if (_director.playableAsset != _prevAsset)
            {
                isDiscontinuity = true;
                recalculateClips();
            }

            //If we moved back in time
            if (_director.time < _prevTime)
            {
                isDiscontinuity = true;
                _willBeDiscontinuousNextFrame = true;
            }

            //Or if we moved forward in time more than we should have
            if (_director.time - _prevTime > Time.deltaTime * 3)
            {
                isDiscontinuity = true;
            }

            //Or if the current state is different from the previous state
            if (_director.state != _prevPlayState)
            {
                isDiscontinuity = true;
                _willBeDiscontinuousNextFrame = true;
            }

            //Or if we entered or left any clips that we are watching
            foreach (var clip in _clipsToWatch)
            {
                if (didEnterOrLeaveClip(clip))
                {
                    isDiscontinuity = true;
                    break;
                }
            }

            if (drawGizmoOnDiscontinuity)
            {
                if (isDiscontinuity)
                {
                    _gizmoRadius += 0.1f;
                }
                else
                {
                    _gizmoRadius *= 0.95f;
                }

                RuntimeGizmoDrawer drawer;
                if (RuntimeGizmoManager.TryGetGizmoDrawer(out drawer))
                {
                    drawer.color = Color.red;
                    drawer.DrawSphere(gizmoLocation.position, _gizmoRadius);
                }
            }

            if (OnUpdate != null)
            {
                OnUpdate(isDiscontinuity);
            }

            _prevTime      = _director.time;
            _prevAsset     = _director.playableAsset;
            _prevPlayState = _director.state;
        }
示例#9
0
    protected virtual void Update()
    {
        _deltaTime = 1f / 5f;
        RuntimeGizmoManager.TryGetGizmoDrawer(out drawer);

        BeforeParticleUpdate();

        Array.Clear(collisionTimes, 0, collisionTimes.Length);
        for (int i = 0; i < _workerData.Length; i++)
        {
            _workerData[i].collisionChunkCounts.Clear();
            _workerData[i].socialChunkCounts.Clear();
        }

        using (new ProfilerSample("Destroy Particles")) {
            destroyParticles();
        }

        using (new ProfilerSample("Emit Particles")) {
            emitParticles();
        }

        if (_useMultithreading)
        {
            using (new ProfilerSample("Dispatch Simulation Jobs")) {
                _integrationForeach.Dispatch(_aliveParticles);
            }
        }
        else
        {
            using (new ProfilerSample("Integrate Particles")) {
                integrateParticles(0, 0, _aliveParticles);
            }
            Array.Copy(_particlesFront, _particlesBack, _aliveParticles);

            if (_useCollisionChunking)
            {
                using (new ProfilerSample("Accumulate And Sort")) {
                    sortIntoChunks(isCollision: true);
                }

                using (new ProfilerSample("Resolve Collisions")) {
                    resolveCollisionsChunked();
                }
            }
            else
            {
                using (new ProfilerSample("Resolve Collisions")) {
                    resolveCollisionsNaive(0, 0, _aliveParticles);
                }
            }

            if (_useSocialChunking)
            {
                using (new ProfilerSample("Accumulate And Sort")) {
                    sortIntoChunks(isCollision: false);
                }

                using (new ProfilerSample("Apply Forces")) {
                    doSocialForcesChunked();
                }
            }
            else
            {
                using (new ProfilerSample("Apply Forces")) {
                    doSocialForcesNaive(0, 0, _aliveParticles);
                }
            }

            using (new ProfilerSample("Apply Global Forces")) {
                applyGlobalForces(0, 0, _aliveParticles);
            }
        }

        if (Input.GetKeyDown(KeyCode.M))
        {
            useKMeans = true;
        }

        if (useKMeans)
        {
            int LEN = 20;
            if (_means == null)
            {
                _means  = new Vector3[LEN];
                _raddii = new float[LEN];
                for (int i = 0; i < LEN; i++)
                {
                    _means[i] = _particlesFront[UnityEngine.Random.Range(0, _aliveParticles)].position;
                }
            }

            var newMeans  = new Vector3[LEN];
            var newCounts = new int[LEN];

            _raddii = new float[LEN];

            for (int i = 0; i < _aliveParticles; i++)
            {
                Particle p = _particlesFront[i];

                float closestDist = float.MaxValue;
                int   index       = 0;
                for (int j = 0; j < LEN; j++)
                {
                    float dist = Vector3.Distance(p.position, _means[j]);
                    if (dist < closestDist)
                    {
                        closestDist = dist;
                        index       = j;
                    }
                }

                drawer.color = _randomColors[index];
                drawer.DrawWireSphere(p.position, 0.05f);

                newMeans[index]  += p.position;
                newCounts[index] += 1;
                _raddii[index]    = Mathf.Max(_raddii[index], closestDist);
            }

            for (int i = 0; i < LEN; i++)
            {
                if (newCounts[i] == 0)
                {
                    _means[i] = UnityEngine.Random.insideUnitSphere;
                }
                else
                {
                    _means[i] = newMeans[i] / newCounts[i];
                }

                drawer.color = _randomColors[i];
                drawer.DrawWireSphere(_means[i], _raddii[i]);
            }

            int totalInteractions = 0;
            for (int i = 0; i < _aliveParticles; i++)
            {
                Particle p = _particlesFront[i];

                float closestDist = float.MaxValue;
                int   index       = 0;
                for (int j = 0; j < LEN; j++)
                {
                    float dist = Vector3.Distance(p.position, _means[j]);
                    if (dist < closestDist)
                    {
                        closestDist = dist;
                        index       = j;
                    }
                }

                totalInteractions += newCounts[index];
                for (int j = 0; j < LEN; j++)
                {
                    if (j == index)
                    {
                        continue;
                    }

                    if (Vector3.Distance(p.position, _means[j]) < (0.5f + _raddii[j]))
                    {
                        totalInteractions += newCounts[j];
                    }
                }
            }

            Debug.Log(totalInteractions + " : " + (1024 * 1024));
        }
    }
示例#10
0
        private void fillOpenMarblePositions(Vector3[] marblePositions)
        {
            var pullTabLength = this.transform.position.From(pullTabOrigin.transform.position).magnitude;

            var pullLength = pullTabIntObj.transform.position.From(pullTabOrigin.position).magnitude;

            var pullOutDir = pullTabOrigin.transform.right * -1;

            var     curveStart   = this.transform.position;
            Vector3 curveEnd     = pullTabIntObj.transform.position;
            var     controlPoint = curveStart + (pullOutDir * pullLength / 3f);

            var curveLength = estimateCurveLength(curveStart, curveEnd, controlPoint);

            //var lengthBetweenMarbles = marbleSpacing;
            var totalStripLength = marbleSpacing * (marblePositions.Length - 1 + 1);

            var beginPoint = curveStart;

            if (totalStripLength > curveLength)
            {
                beginPoint += (totalStripLength - curveLength) * -pullOutDir;
            }

            var tFraction_AllMarbles = 1 / (float)(marblePositions.Length - 1 + (1 /* leave extra space at end of curve */));

            var curveStripLenRatio = totalStripLength / curveLength;

            var tStart = 1 - (1 * curveStripLenRatio);
            var tStep  = tFraction_AllMarbles * curveStripLenRatio;

            if (tStep < 0.001f)
            {
                Debug.LogError("Bad tStep!! " + tStep, this);
                tStep = 0.001f;
            }

            RuntimeGizmoDrawer drawer = null;

            {
                RuntimeGizmoManager.TryGetGizmoDrawer(out drawer);
            }

            if (drawer != null && drawDebug)
            {
                drawer.color = LeapColor.forest;
                drawer.DrawWireSphere(beginPoint, 0.04f);

                drawer.color = LeapColor.blue;
                drawer.DrawWireSphere(curveStart, 0.04f);
                drawer.DrawWireSphere(curveEnd, 0.04f);
                drawer.DrawWireSphere(controlPoint, 0.04f);
            }

            var t = tStart;

            if (t > 0)
            {
                t = 0;
            }
            for (int i = 0; i < marblePositions.Length; i++)
            {
                Vector3 marblePos;
                if (t < 0)
                {
                    marblePos = beginPoint + pullOutDir * (marbleSpacing * i);
                }
                else
                {
                    marblePos = evalCurve(curveStart, curveEnd, controlPoint, t);
                }
                marblePositions[i] = marblePos;

                if (drawer != null && drawDebug)
                {
                    drawer.color = Color.white;
                    drawer.DrawWireSphere(marblePos, 0.03f);
                }

                t += tStep;
            }
        }
示例#11
0
 private static bool TryGetDrawer(out Drawer drawer)
 {
     return(RuntimeGizmoManager.TryGetGizmoDrawer(out drawer));
 }
示例#12
0
        // Update is called once per frame
        void Update()
        {
            RuntimeGizmoDrawer drawer;

            RuntimeGizmoManager.TryGetGizmoDrawer(out drawer);

            for (int i = 0; i < provider.points.Length; i++)
            {
                drawer.DrawSphere(provider.points[i], 0.005f);
            }

            if (provider.points.Length == 4)
            {
                transform.position = (provider.points[0] + provider.points[1] + provider.points[2] + provider.points[3]) / 4.0f;
                //transform.rotation = Quaternion.LookRotation(provider.points[0] - provider.points[1], provider.points[0] - provider.points[2]);

                /*
                 * bool leftUpdate = false, rightUpdate = false;
                 * if (Input.GetKey(AddLeftMeasurementKey)) {
                 * drawer.DrawLine(leftCanonicalPointTransforms.position, transform.position);
                 * leftOptimizer.drawCorrespondence(leftCanonicalPointTransforms.position, transform.position, drawer);
                 * } else if (Input.GetKey(AddRightMeasurementKey)) {
                 * drawer.DrawLine(rightCanonicalPointTransforms.position, transform.position);
                 * rightOptimizer.drawCorrespondence(rightCanonicalPointTransforms.position, transform.position, drawer);
                 * }
                 *
                 * if (Input.GetKeyUp(AddLeftMeasurementKey)) {
                 * leftOptimizer.AddPosition(leftCanonicalPointTransforms.position, transform.position);
                 * leftUpdate = true;
                 * } else if (Input.GetKeyUp(AddRightMeasurementKey)) {
                 * rightOptimizer.AddPosition(rightCanonicalPointTransforms.position, transform.position);
                 * rightUpdate = true;
                 * }
                 *
                 * //Advance the canonical point transforms
                 * if (leftUpdate) {
                 * Ray cameraRay = leftOptimizer.raytracer.eyePerspective.ViewportPointToRay(((Vector3)Random.insideUnitCircle * 0.4f) + (Vector3.one * 0.5f));
                 * leftCanonicalPointTransforms.position = cameraRay.origin + (cameraRay.direction * 0.25f);
                 * leftCanonicalPointTransforms.LookAt(cameraRay.origin);
                 * }
                 * if (rightUpdate) {
                 * Ray cameraRay = rightOptimizer.raytracer.eyePerspective.ViewportPointToRay(((Vector3)Random.insideUnitCircle * 0.4f) + (Vector3.one * 0.5f));
                 * rightCanonicalPointTransforms.position = cameraRay.origin + (cameraRay.direction * 0.25f);
                 * rightCanonicalPointTransforms.LookAt(cameraRay.origin);
                 * }*/

                if (Input.GetKeyUp(SaveMeasurementKey))
                {
                    rightOptimizer.Save();
                    leftOptimizer.Save();
                }

                /*
                 * float avgSideLength =
                 * (Vector3.Distance(provider.points[0], provider.points[1]) +
                 * Vector3.Distance(provider.points[1], provider.points[2]) +
                 * Vector3.Distance(provider.points[2], provider.points[3]) +
                 * Vector3.Distance(provider.points[3], provider.points[0])) / 4f;
                 * leftCanonicalPointTransforms.localScale = Vector3.one * avgSideLength;
                 * rightCanonicalPointTransforms.localScale = Vector3.one * avgSideLength;
                 *
                 * bool leftUpdate = false, rightUpdate = false;
                 * for (int i = 0; i < provider.points.Length; i++) {
                 * measuredPointTransforms[i].position = provider.points[i];
                 * if (Input.GetKey(AddLeftMeasurementKey)) {
                 *  drawer.DrawLine(leftCanonicalPointTransforms.GetChild(i).position, provider.points[i]);
                 *  leftOptimizer.drawCorrespondence(leftCanonicalPointTransforms.GetChild(i).position, provider.points[i], drawer);
                 * } else if (Input.GetKey(AddRightMeasurementKey)) {
                 *  drawer.DrawLine(rightCanonicalPointTransforms.GetChild(i).position, provider.points[i]);
                 *  rightOptimizer.drawCorrespondence(rightCanonicalPointTransforms.GetChild(i).position, provider.points[i], drawer);
                 * }
                 *
                 * if (Input.GetKeyUp(AddLeftMeasurementKey)) {
                 *  leftOptimizer.AddPosition(leftCanonicalPointTransforms.GetChild(i).position, provider.points[i]);
                 *  leftUpdate = true;
                 * } else if (Input.GetKeyUp(AddRightMeasurementKey)) {
                 *  rightOptimizer.AddPosition(rightCanonicalPointTransforms.GetChild(i).position, provider.points[i]);
                 *  rightUpdate = true;
                 * }
                 * }
                 *
                 * //Advance the canonical point transforms (also ensure they're at the same width as the measured aruco marker
                 * if (leftUpdate) {
                 * Ray cameraRay = leftOptimizer.raytracer.eyePerspective.ViewportPointToRay(((Vector3)Random.insideUnitCircle * 0.4f) + (Vector3.one * 0.5f));
                 * leftCanonicalPointTransforms.position = cameraRay.origin + (cameraRay.direction * 0.25f);
                 * leftCanonicalPointTransforms.LookAt(cameraRay.origin);
                 * }
                 * if (rightUpdate) {
                 * Ray cameraRay = rightOptimizer.raytracer.eyePerspective.ViewportPointToRay(((Vector3)Random.insideUnitCircle * 0.4f) + (Vector3.one * 0.5f));
                 * rightCanonicalPointTransforms.position = cameraRay.origin + (cameraRay.direction * 0.25f);
                 * rightCanonicalPointTransforms.LookAt(cameraRay.origin);
                 * }
                 *
                 * if (Input.GetKeyUp(SaveMeasurementKey)) {
                 * rightOptimizer.Save();
                 * leftOptimizer.Save();
                 * }*/
            }
        }
示例#13
0
        public override bool IsGesturePoseHeld(Hand leftHand, Hand rightHand,
                                               out Vector3 positionOfInterest)
        {
            var leftThumb    = leftHand.GetThumb();
            var leftThumbTip = leftThumb.TipPosition.ToVector3();
            var leftThumbDir = leftThumb.Direction.ToVector3();

            var rightIndex    = rightHand.GetIndex();
            var rightIndexTip = rightIndex.TipPosition.ToVector3();
            var rightIndexDir = rightIndex.Direction.ToVector3();

            var tipsTouching = (leftThumbTip - rightIndexTip).sqrMagnitude
                               < MAX_TOUCHING_DISTANCE_SQR;

            if (drawHeldPoseDebug)
            {
                var touchingAmount = ((leftThumbTip - rightIndexTip).sqrMagnitude
                                      - MAX_TOUCHING_DISTANCE_SQR).Map(0,
                                                                       -MAX_TOUCHING_DISTANCE_SQR, 0, 1);
                RuntimeGizmoDrawer drawer = null;
                if (RuntimeGizmoManager.TryGetGizmoDrawer(out drawer))
                {
                    drawer.DrawBar(touchingAmount,
                                   Vector3.down * 0.2f + Vector3.right * 0.20f
                                   + Vector3.forward * 0.1f,
                                   Vector3.up,
                                   tipsTouching ?
                                   LeapColor.white
                         : LeapColor.teal,
                                   scale: 0.2f);
                }
            }

            var tipsAligned = Vector3.Dot(leftThumbDir, rightIndexDir) < -0.70f;

            if (drawHeldPoseDebug)
            {
                RuntimeGizmoDrawer drawer = null;
                if (RuntimeGizmoManager.TryGetGizmoDrawer(out drawer))
                {
                    drawer.DrawBar(Vector3.Dot(leftThumbDir, rightIndexDir)
                                   .Map(-1, 1, 1, 0),
                                   Vector3.down * 0.2f + Vector3.right * 0.20f,
                                   Vector3.up,
                                   tipsAligned ?
                                   LeapColor.white
                         : LeapColor.periwinkle,
                                   scale: 0.2f);
                }
            }

            var leftIndexPointAmount  = leftHand.GetIndexPointAmount();
            var rightIndexPointAmount = rightHand.GetIndexPointAmount();

            var leftIsIndexPointing  = leftIndexPointAmount > 0.80f;
            var rightIsIndexPointing = rightIndexPointAmount > 0.80f;

            if (drawHeldPoseDebug)
            {
                RuntimeGizmoDrawer drawer = null;
                if (RuntimeGizmoManager.TryGetGizmoDrawer(out drawer))
                {
                    drawer.DrawBar(leftIndexPointAmount,
                                   Vector3.down * 0.2f, Vector3.up,
                                   leftIsIndexPointing ?
                                   LeapColor.white
                         : LeapColor.lavender,
                                   scale: 0.2f);
                }
                if (RuntimeGizmoManager.TryGetGizmoDrawer(out drawer))
                {
                    drawer.DrawBar(rightIndexPointAmount,
                                   Vector3.down * 0.2f + Vector3.right * 0.10f,
                                   Vector3.up,
                                   rightIsIndexPointing ?
                                   LeapColor.white
                         : LeapColor.red,
                                   scale: 0.2f);
                }
            }

            positionOfInterest = Vector3.zero;
            bool isGesturePoseHeld = tipsTouching &&
                                     tipsAligned &&
                                     leftIsIndexPointing &&
                                     rightIsIndexPointing;

            if (isGesturePoseHeld)
            {
                var gesturePlaneDir = Vector3.Cross(
                    leftHand.Fingers[0].Direction.ToVector3(),
                    leftHand.Fingers[1].Direction.ToVector3());
                var upPlaneDir = Vector3.Cross(gesturePlaneDir,
                                               leftHand.Fingers[0].Direction.ToVector3()).normalized;

                positionOfInterest = (leftThumbTip + rightIndexTip) / 2f
                                     + upPlaneDir * (leftHand.GetIndex().bones[1].PrevJoint
                                                     - leftHand.GetIndex().TipPosition)
                                     .ToVector3().magnitude;
            }

            return(isGesturePoseHeld);
        }