// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Utilities
{
///
/// A ray whose position and direction are stabilized in a way similar to how gaze stabilization
/// works in HoloLens.
///
/// The ray uses Anatolie Gavrulic's "DynamicExpDecay" filter to stabilize the ray
/// this filter adjusts its smoothing factor based on the velocity of the filtered object
///
/// The formula is
/// Y_smooted += ∆𝑌_𝑟
/// where
/// 〖∆𝑌_𝑟=∆𝑌∗〖0.5〗^(∆𝑌/〖Halflife〗)
///
/// In code, LERP(signal, oldValue, POW(0.5, ABS(signal – oldValue) / hl)
///
public class StabilizedRay
{
///
/// Half life used for position decay calculations.
///
public float HalfLifePosition { get; } = 0.1f;
///
/// Half life used for velocity decay calculations.
///
public float HalfLifeDirection { get; } = 0.1f;
///
/// Computed Stabilized position.
///
public Vector3 StabilizedPosition { get; private set; }
///
/// Computed stabilized direction.
///
public Vector3 StabilizedDirection { get; private set; }
private bool isInitialized = false;
///
/// HalfLife closer to zero means lerp closer to one.
///
public StabilizedRay(float halfLife)
{
HalfLifePosition = halfLife;
HalfLifeDirection = halfLife;
}
///
/// Add sample to ray stabilizer.
///
/// New Sample used to update stabilized ray.
public void AddSample(Ray ray)
{
if (!isInitialized)
{
StabilizedPosition = ray.origin;
StabilizedDirection = ray.direction.normalized;
isInitialized = true;
}
else
{
StabilizedPosition = DynamicExpDecay(StabilizedPosition, ray.origin, HalfLifePosition);
StabilizedDirection = DynamicExpDecay(StabilizedDirection, ray.direction.normalized, HalfLifeDirection);
}
}
///
/// Compute dynamic exponential coefficient.
///
/// Half life
/// Distance delta
/// The dynamic exponential coefficient.
public static float DynamicExpCoefficient(float hLife, float delta)
{
if (hLife == 0)
{
return 1;
}
return 1.0f - Mathf.Pow(0.5f, delta / hLife);
}
///
/// Compute stabilized vector3 given a previously stabilized value, and a new sample, given a half life.
///
/// Previous stabilized Vector3.
/// New Vector3 sample.
/// Half life used for stabilization.
/// Stabilized Vector 3.
public static Vector3 DynamicExpDecay(Vector3 from, Vector3 to, float hLife)
{
return Vector3.Lerp(from, to, DynamicExpCoefficient(hLife, Vector3.Distance(to, from)));
}
}
}