protected virtual Vector3 CalculateAngle(Vector3 originPoint, Vector3 originalGrabPoint, Vector3 currentGrabPoint)
        {
            float xRotated = (rotateAround == RotationAxis.xAxis ? CalculateAngle(originPoint, originalGrabPoint, currentGrabPoint, transform.right) : 0f);
            float yRotated = (rotateAround == RotationAxis.yAxis ? CalculateAngle(originPoint, originalGrabPoint, currentGrabPoint, transform.up) : 0f);
            float zRotated = (rotateAround == RotationAxis.zAxis ? CalculateAngle(originPoint, originalGrabPoint, currentGrabPoint, transform.forward) : 0f);

            float frictionMultiplier = VRTK_SharedMethods.DividerToMultiplier(rotationFriction);

            return(new Vector3(xRotated * frictionMultiplier, yRotated * frictionMultiplier, zRotated * frictionMultiplier));
        }
        protected virtual Vector3 GetNewRotation()
        {
            Vector3 grabbingObjectAngularVelocity = Vector3.zero;

            if (VRTK_ControllerReference.IsValid(grabbingObjectReference))
            {
                grabbingObjectAngularVelocity = VRTK_DeviceFinder.GetControllerAngularVelocity(grabbingObjectReference) * VRTK_SharedMethods.DividerToMultiplier(rotationFriction);
            }

            switch (rotationAction)
            {
            case RotationType.FollowAttachPoint:
                return(CalculateAngle(transform.position, previousAttachPointPosition, controllerAttachPoint.transform.position));

            case RotationType.FollowLongitudinalAxis:
                return(BuildFollowAxisVector(grabbingObjectAngularVelocity.x));

            case RotationType.FollowPerpendicularAxis:
                return(BuildFollowAxisVector(grabbingObjectAngularVelocity.y));

            case RotationType.FollowLateralAxis:
                return(BuildFollowAxisVector(grabbingObjectAngularVelocity.z));
            }

            return(Vector3.zero);
        }
        /// <summary>
        /// The SetRotation method sets the rotation on the Interactable Object to the given angle over the desired time.
        /// </summary>
        /// <param name="newAngle">The angle to rotate to through the current rotation axis.</param>
        /// <param name="transitionTime">The time in which the entire rotation operation will take place.</param>
        public virtual void SetRotation(float newAngle, float transitionTime = 0f)
        {
            newAngle = Mathf.Clamp(newAngle, angleLimits.minimum, angleLimits.maximum);
            Vector3 newCurrentRotation = currentRotation;

            switch (rotateAround)
            {
            case RotationAxis.xAxis:
                newCurrentRotation = new Vector3(newAngle, currentRotation.y, currentRotation.z);
                break;

            case RotationAxis.yAxis:
                newCurrentRotation = new Vector3(currentRotation.x, newAngle, currentRotation.z);
                break;

            case RotationAxis.zAxis:
                newCurrentRotation = new Vector3(currentRotation.x, currentRotation.y, newAngle);
                break;
            }

            if (transitionTime > 0f)
            {
                CancelUpdateRotation();
                updateRotationRoutine = StartCoroutine(RotateToAngle(newCurrentRotation, VRTK_SharedMethods.DividerToMultiplier(transitionTime)));
            }
            else
            {
                UpdateRotation(newCurrentRotation, false, false);
                currentRotation = newCurrentRotation;
            }
        }