namespace Zinnia.Tracking.Modification
{
using UnityEngine;
using Zinnia.Data.Type;
using Zinnia.Extension;
using Zinnia.Process;
///
/// Scales a given target based on the distance between two points.
///
public class PinchScaler : MonoBehaviour, IProcessable
{
[Header("Scale Settings")]
[Tooltip("The target to scale.")]
[SerializeField]
private GameObject target;
///
/// The target to scale.
///
public GameObject Target
{
get
{
return target;
}
set
{
target = value;
}
}
[Tooltip("The point to determine distance from.")]
[SerializeField]
private GameObject primaryPoint;
///
/// The point to determine distance from.
///
public GameObject PrimaryPoint
{
get
{
return primaryPoint;
}
set
{
primaryPoint = value;
if (this.IsMemberChangeAllowed())
{
OnAfterPrimaryPointChange();
}
}
}
[Tooltip("The point to determine distance to.")]
[SerializeField]
private GameObject secondaryPoint;
///
/// The point to determine distance to.
///
public GameObject SecondaryPoint
{
get
{
return secondaryPoint;
}
set
{
secondaryPoint = value;
if (this.IsMemberChangeAllowed())
{
OnAfterSecondaryPointChange();
}
}
}
[Tooltip("Determines whether to use local or global scale.")]
[SerializeField]
private bool useLocalScale = true;
///
/// Determines whether to use local or global scale.
///
public bool UseLocalScale
{
get
{
return useLocalScale;
}
set
{
useLocalScale = value;
}
}
[Tooltip("Determines whether to calculate the multiplier using Mathf.Pow(float, float).")]
[SerializeField]
private bool calculateByPower;
///
/// Determines whether to calculate the multiplier using .
///
public bool CalculateByPower
{
get
{
return calculateByPower;
}
set
{
calculateByPower = value;
}
}
[Tooltip("A scale factor multiplier.")]
[SerializeField]
private float multiplier = 1f;
///
/// A scale factor multiplier.
///
public float Multiplier
{
get
{
return multiplier;
}
set
{
multiplier = value;
}
}
[Header("Restriction Settings")]
[Tooltip("Determines which axes to apply the modification on>.")]
[SerializeField]
private Vector3State applyScaleOnAxis = Vector3State.True;
///
/// Determines which axes to apply the modification on>.
///
public Vector3State ApplyScaleOnAxis
{
get
{
return applyScaleOnAxis;
}
set
{
applyScaleOnAxis = value;
}
}
[Tooltip("The minimum allowed scale.")]
[SerializeField]
private Vector3 minimumScaleLimit = Vector3.one * float.NegativeInfinity;
///
/// The minimum allowed scale.
///
public Vector3 MinimumScaleLimit
{
get
{
return minimumScaleLimit;
}
set
{
minimumScaleLimit = value;
}
}
[Tooltip("The maximum allowed scale.")]
[SerializeField]
private Vector3 maximumScaleLimit = Vector3.one * float.PositiveInfinity;
///
/// The maximum allowed scale.
///
public Vector3 MaximumScaleLimit
{
get
{
return maximumScaleLimit;
}
set
{
maximumScaleLimit = value;
}
}
///
/// The previous distance between and .
///
protected float? previousDistance;
///
/// The original scale of .
///
protected Vector3 originalScale;
///
/// Clears .
///
public virtual void ClearTarget()
{
if (!this.IsValidState())
{
return;
}
Target = default;
}
///
/// Clears .
///
public virtual void ClearPrimaryPoint()
{
if (!this.IsValidState())
{
return;
}
PrimaryPoint = default;
}
///
/// Clears .
///
public virtual void ClearSecondaryPoint()
{
if (!this.IsValidState())
{
return;
}
SecondaryPoint = default;
}
///
/// Processes the current scale factor onto the target.
///
public virtual void Process()
{
if (!this.IsValidState() || Target == null || PrimaryPoint == null || SecondaryPoint == null)
{
return;
}
Scale();
}
///
/// Saves the existing target scale.
///
public virtual void SaveCurrentScale()
{
originalScale = GetTargetScale();
}
///
/// Restores the saved target scale.
///
public virtual void RestoreSavedScale()
{
if (UseLocalScale)
{
Target.transform.localScale = originalScale;
}
else
{
Target.transform.SetGlobalScale(originalScale);
}
}
protected virtual void OnDisable()
{
previousDistance = null;
}
///
/// Attempts to scale the target.
///
protected virtual void Scale()
{
previousDistance = previousDistance == null ? GetDistance() : previousDistance;
if (CalculateByPower)
{
float previousDistanceValue = (float)previousDistance;
if (!previousDistanceValue.ApproxEquals(0))
{
ScaleByPower();
}
}
else
{
ScaleByMultiplier();
}
previousDistance = GetDistance();
}
///
/// Applies the scale limits to the given scale value.
///
/// The scale value to apply limits to.
/// The scale value with limits applied.
protected virtual Vector3 ApplyLimits(Vector3 scale)
{
return new Vector3(
ApplyScaleOnAxis.xState ? Mathf.Clamp(scale.x, minimumScaleLimit.x, maximumScaleLimit.x) : GetTargetScale().x,
ApplyScaleOnAxis.yState ? Mathf.Clamp(scale.y, minimumScaleLimit.y, maximumScaleLimit.y) : GetTargetScale().y,
ApplyScaleOnAxis.zState ? Mathf.Clamp(scale.z, minimumScaleLimit.z, maximumScaleLimit.z) : GetTargetScale().z
);
}
///
/// Scales the object by the distance delta multiplied against the multiplier.
///
protected virtual void ScaleByMultiplier()
{
float distanceDelta = GetDistance() - (float)previousDistance;
Vector3 newScale = Vector3.one * distanceDelta * Multiplier;
if (UseLocalScale)
{
Target.transform.localScale = ApplyLimits(Target.transform.localScale + newScale);
}
else
{
Target.transform.SetGlobalScale(ApplyLimits(Target.transform.lossyScale + newScale));
}
}
///
/// Scales the object using a power of the multiplier.
///
protected virtual void ScaleByPower()
{
float scaleRatio = GetDistance() / (float)previousDistance;
Vector3 scaleVector = Vector3.one * Mathf.Pow(scaleRatio, Multiplier);
if (UseLocalScale)
{
Target.transform.localScale = ApplyLimits(Vector3.Scale(Target.transform.localScale, scaleVector));
}
else
{
Target.transform.SetGlobalScale(ApplyLimits(Vector3.Scale(Target.transform.lossyScale, scaleVector)));
}
}
///
/// Gets the distance between the primary point and secondary point;
///
/// The distance between the points.
protected virtual float GetDistance()
{
return Vector3.Distance(PrimaryPoint.transform.position, SecondaryPoint.transform.position);
}
///
/// Gets the scale of the target in either local or global scale.
///
/// The scale of the target.
protected virtual Vector3 GetTargetScale()
{
return UseLocalScale ? Target.transform.localScale : Target.transform.lossyScale;
}
///
/// Called after has been changed.
///
protected virtual void OnAfterPrimaryPointChange()
{
previousDistance = null;
}
///
/// Called after has been changed.
///
protected virtual void OnAfterSecondaryPointChange()
{
previousDistance = null;
}
}
}