// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.UI
{
///
/// Controls how the standard indeterminate loader moves and behaves over time.
///
///
/// This loader is calculated dynamically based on Sine and Cosine
///
[AddComponentMenu("Scripts/MRTK/SDK/LoaderController")]
public class LoaderController : MonoBehaviour
{
///
/// Total strength of movement of the loading animation
///
[SerializeField]
[Tooltip("Total strength of movement of the loading animation")]
private float amplitude = 0.05f;
public float Amplitude
{
get => amplitude;
set => amplitude = value;
}
///
/// How often happens
///
[SerializeField]
[Tooltip("How often 'amplitude' happens")]
private float frequency = 3.0f;
public float Frequency
{
get => frequency;
set => frequency = value;
}
///
/// The base local position for the dots' parent
///
[SerializeField]
[Tooltip("The base local position for the dots' parent")]
private float dotOffset = 0.05f;
public float DotOffset
{
get => dotOffset;
set => dotOffset = value;
}
///
/// The base scale unit for the dots' parent
///
[SerializeField]
[Tooltip("The base scale unit for the dots' parent")]
private float dotSetScale = 0.06f;
public float DotSetScale
{
get => dotSetScale;
set => dotSetScale = value;
}
///
/// Use low frequency oscillation with the Sine calculation.
///
[SerializeField]
[Tooltip("Use low frequency oscillation with the Sine calculation.")]
private bool lFOsin = false;
public bool LFOsin
{
get => lFOsin;
set => lFOsin = value;
}
///
/// Use low frequency oscillation with the Cosine calculation.
///
[SerializeField]
[Tooltip("Use low frequency oscillation with the Cosine calculation.")]
private bool lFOcos = false;
public bool LFOcos
{
get => lFOcos;
set => lFOcos = value;
}
///
/// Low Frequency oscillation frequency
///
[SerializeField]
[Tooltip("Low frequency oscillation frequency")]
private float lFOfreq = 1.0f;
public float LFOfreq
{
get => lFOfreq;
set => lFOfreq = value;
}
///
/// Low Frequency oscillation amplitude
///
[SerializeField]
[Tooltip("Low Frequency oscillation amplitude")]
private float lFOamp = 0.1f;
public float LFOamp
{
get => lFOamp;
set => lFOamp = value;
}
///
/// Reverses dots' orbit rotation path
///
[SerializeField]
[Tooltip("Reverses dots' orbit rotation path")]
private bool reverseOrbit = false;
public bool ReverseOrbit
{
get => reverseOrbit;
set => reverseOrbit = value;
}
///
/// Inverts dots' position in orbit
///
[SerializeField]
[Tooltip("Inverts dots' position in orbit")]
private bool invertOrbitOffset = false;
public bool InvertOrbitOffset
{
get => invertOrbitOffset;
set => invertOrbitOffset = value;
}
///
/// Multiplier to dot's rotation calculation
///
[SerializeField]
[Tooltip("Multiplier to dot's rotation calculation")]
private float dotSpinMultiplier = 0.3f;
public float DotSpinMultiplier
{
get => dotSpinMultiplier;
set => dotSpinMultiplier = value;
}
///
/// Multiplier to dot's scale calculation
///
[SerializeField]
[Tooltip("Multiplier to dot's scale calculation")]
private float dotScaleMultipler = 0.0f;
public float DotScaleMultipler
{
get => dotScaleMultipler;
set => dotScaleMultipler = value;
}
///
/// When enabled, the dot scale uses Cosine to determine scale with including .
/// Otherwise, it will use Sine.
///
[SerializeField]
[Tooltip("When enabled, the dot scale uses Cosine to determine scale with including 'dotSetScale'. Otherwise, it will use Sine.")]
private bool sinCosSplitScale = false;
public bool SinCosSplitScale
{
get => sinCosSplitScale;
set => sinCosSplitScale = value;
}
///
/// Calculates the time cycle for the trigonometric functions
///
private float Cycles
{
get
{
if (reverseOrbit)
{
return (frequency < 0.0f) ? (Time.time / 0.000001f) * -1.0f : (Time.time / frequency) * -1.0f;
}
else
{
return (frequency < 0.0f) ? Time.time / 0.000001f : Time.time / frequency;
}
}
}
private float degPerSec;
private Vector3 parentNewPos = Vector3.zero;
private Transform dot01;
private Vector3 dot01NewPos = Vector3.zero;
private Vector3 dot01NewScale = Vector3.zero;
private Vector3 dot01NewRot = Vector3.zero;
private Transform dot02;
private Vector3 dot02NewPos = Vector3.zero;
private Vector3 dot02NewScale = Vector3.zero;
private Vector3 dot02NewRot = Vector3.zero;
private const float tau = Mathf.PI * 2.0f;
private void OnEnable()
{
if (dot01 == null)
{
dot01 = gameObject.transform.GetChild(0);
}
if (dot02 == null)
{
dot02 = gameObject.transform.GetChild(1);
}
}
private void Update()
{
degPerSec = Time.deltaTime * 360f;
AnimateParent();
AnimateDotTransforms();
}
private void AnimateParent()
{
float cosX = Mathf.Cos(Cycles * tau) * amplitude;
float sinY = Mathf.Sin(Cycles * tau) * amplitude;
if (invertOrbitOffset == true)
{
cosX = -cosX;
sinY = -sinY;
}
parentNewPos.Set(cosX, sinY, 0f);
transform.localPosition = parentNewPos;
if (lFOsin == true)
{
dotSpinMultiplier = Mathf.Sin(Time.time * lFOfreq) * lFOamp;
}
else if (lFOcos == true)
{
dotSpinMultiplier = Mathf.Cos(Time.time * lFOfreq) * lFOamp;
}
transform.Rotate(Vector3.forward * (degPerSec * dotSpinMultiplier));
}
private void AnimateDotTransforms()
{
// SaveLocal dot groups' scale
float sinScaleCalc = dotSetScale + Mathf.Sin(Cycles * tau / 2) * dotScaleMultipler;
float cosScaleCalc = dotSetScale + Mathf.Cos(Cycles * tau / 2) * dotScaleMultipler;
if (sinCosSplitScale == true)
{
dot01NewPos.Set(cosScaleCalc, cosScaleCalc, cosScaleCalc);
}
else
{
dot01NewPos.Set(sinScaleCalc, sinScaleCalc, sinScaleCalc);
}
dot01.localScale = dot01NewPos;
dot02NewPos.Set(sinScaleCalc, sinScaleCalc, sinScaleCalc);
dot02.localScale = dot02NewPos;
// SaveLocal dot groups' position Offset from Parent-Null Center
dot01NewPos.Set(dotOffset, dotOffset, 0f);
dot02NewPos.Set(-dotOffset, -dotOffset, 0f);
dot01.transform.localPosition = dot01NewPos;
dot02.transform.localPosition = dot02NewPos;
}
}
}