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; } } }