namespace VRTK.Prefabs.Locomotion.Movement.SpatialManipulation
{
using UnityEngine;
using Malimbe.XmlDocumentationAttribute;
using Malimbe.PropertySerializationAttribute;
using Zinnia.Extension;
///
/// Manipulates the scale of the given Target based on the distance between the two sources.
///
public class ScaleManipulator : SpatialManipulator
{
#region Scale Settings
///
/// The minimum the scaled value can reach.
///
[Serialized]
[field: Header("Scale Settings"), DocumentedByXml]
public Vector3 MinimumScale { get; set; } = Vector3.one;
///
/// The maximum the scaled value can reach.
///
[Serialized]
[field: DocumentedByXml]
public Vector3 MaximumScale { get; set; } = Vector3.one * Mathf.Infinity;
///
/// Whether to scale around the offset point.
///
[Serialized]
[field: DocumentedByXml]
public bool ScaleAroundOffset { get; set; } = true;
#endregion
///
/// The previous frame delta of the distance between the sources.
///
protected float previousDistanceDelta;
///
/// Processes the scale manipulation.
///
public override void Process()
{
if (ActivationAction == null || !ActivationAction.Value)
{
wasActivated = false;
return;
}
if (!wasActivated)
{
wasActivated = true;
previousDistanceDelta = GetDistanceDelta();
}
float currentDistanceDelata = GetDistanceDelta();
float frameDistanceDelta = currentDistanceDelata - previousDistanceDelta;
if (Mathf.Abs(frameDistanceDelta) >= ActivationThreshold)
{
bool validOffset = ScaleAroundOffset && IsObjectValid(Offset);
float initialScale = Target.transform.localScale.x;
Vector3 pivotDiff = validOffset ? Target.transform.position - Offset.transform.position : Vector3.zero;
Vector3 newScale = Target.transform.localScale + (Vector3.one * Time.deltaTime * Mathf.Sign(frameDistanceDelta) * Multiplier);
float relativeScale = newScale.x / Target.transform.localScale.x;
Vector3 finalPosition = validOffset ? Offset.transform.position + pivotDiff * relativeScale : Target.transform.position;
Target.transform.localScale = new Vector3(Mathf.Clamp(newScale.x, MinimumScale.x, MaximumScale.x), Mathf.Clamp(newScale.y, MinimumScale.y, MaximumScale.y), Mathf.Clamp(newScale.z, MinimumScale.z, MaximumScale.z));
if (!Target.transform.localScale.x.ApproxEquals(initialScale))
{
Target.transform.position = new Vector3(finalPosition.x, Target.transform.position.y, finalPosition.z);
}
}
previousDistanceDelta = currentDistanceDelata;
}
///
/// Gets the delta of the distance between sources from the previous frame to the current frame.
///
///
protected virtual float GetDistanceDelta()
{
bool primaryValid = IsObjectValid(PrimarySource);
bool secondaryValid = IsObjectValid(SecondarySource);
if (primaryValid && secondaryValid)
{
return Vector3.Distance(GetLocalPosition(PrimarySource), GetLocalPosition(SecondarySource));
}
if (IsObjectValid(Offset) && (primaryValid || secondaryValid))
{
return Vector3.Distance(primaryValid ? GetLocalPosition(PrimarySource) : GetLocalPosition(SecondarySource), Offset.transform.position);
}
return 0f;
}
}
}