/*
* @author Valentin Simonov / http://va.lent.in/
*/
using System;
using System.Collections.Generic;
using TouchScript.Utils;
using TouchScript.Pointers;
using UnityEngine;
#if TOUCHSCRIPT_DEBUG
using TouchScript.Debugging.GL;
#endif
namespace TouchScript.Gestures.TransformGestures.Base
{
///
/// Abstract base class for Transform Gestures.
///
///
/// Relationship with component requires that if current object position is not exactly the one acquired by transformation events from this gesture (i.e. when smoothing is applied current transform is lagging a bit behind target transform), the gesture has to know about this to calculate translation properly. This is where method comes into play. has to call it after every transform event.
///
public abstract class TransformGestureBase : Gesture, ITransformGesture
{
#region Constants
///
/// Message name when gesture starts
///
public const string TRANSFORM_START_MESSAGE = "OnTransformStart";
///
/// Message name when gesture updates
///
public const string TRANSFORM_MESSAGE = "OnTransform";
///
/// Message name when gesture ends
///
public const string TRANSFORM_COMPLETE_MESSAGE = "OnTransformComplete";
#endregion
#region Events
///
public event EventHandler TransformStarted
{
add { transformStartedInvoker += value; }
remove { transformStartedInvoker -= value; }
}
///
public event EventHandler Transformed
{
add { transformedInvoker += value; }
remove { transformedInvoker -= value; }
}
///
public event EventHandler TransformCompleted
{
add { transformCompletedInvoker += value; }
remove { transformCompletedInvoker -= value; }
}
// Needed to overcome iOS AOT limitations
private EventHandler transformStartedInvoker, transformedInvoker, transformCompletedInvoker;
///
/// Unity event, occurs when the gesture starts.
///
public GestureEvent OnTransformStart = new GestureEvent();
///
/// Unity event, occurs when the gesture is updated.
///
public GestureEvent OnTransform = new GestureEvent();
///
/// Unity event, occurs when the gesture ends.
///
public GestureEvent OnTransformComplete = new GestureEvent();
#endregion
#region Public properties
///
/// Gets or sets types of transformation this gesture supports.
///
/// Type flags.
public TransformGesture.TransformType Type
{
get { return type; }
set
{
type = value;
updateType();
}
}
///
/// Gets or sets minimum distance in cm for pointers to move for gesture to begin.
///
/// Minimum value in cm user must move their fingers to start this gesture.
public float ScreenTransformThreshold
{
get { return screenTransformThreshold; }
set
{
screenTransformThreshold = value;
updateScreenTransformThreshold();
}
}
///
public TransformGesture.TransformType TransformMask
{
get { return transformMask; }
}
///
public Vector3 DeltaPosition
{
get { return deltaPosition; }
}
///
public float DeltaRotation
{
get { return deltaRotation; }
}
///
public float DeltaScale
{
get { return deltaScale; }
}
///
public Vector3 RotationAxis
{
get { return rotationAxis; }
}
#endregion
#region Private variables
///
/// in pixels.
///
protected float screenTransformPixelThreshold;
///
/// in pixels squared.
///
protected float screenTransformPixelThresholdSquared;
///
/// The bit mask of what transform operations happened this frame.
///
protected TransformGesture.TransformType transformMask;
///
/// Calculated delta position.
///
protected Vector3 deltaPosition;
///
/// Calculated delta rotation.
///
protected float deltaRotation;
///
/// Calculated delta scale.
///
protected float deltaScale;
///
/// Rotation axis to use with deltaRotation.
///
protected Vector3 rotationAxis = new Vector3(0, 0, 1);
///
/// Indicates whether transformation started;
///
protected bool isTransforming = false;
///
/// Indicates if current position is being overridden for the next frame. .
///
protected bool targetPositionOverridden = false;
///
/// Target overridden position. .
///
protected Vector3 targetPosition;
///
/// The type of the transforms this gesture can dispatch.
///
[SerializeField]
protected TransformGesture.TransformType type = TransformGesture.TransformType.Translation | TransformGesture.TransformType.Scaling |
TransformGesture.TransformType.Rotation;
[SerializeField]
private float screenTransformThreshold = 0.1f;
#endregion
#region Public methods
///
/// Overrides the target position used in calculations this frame. If used, has to be set after every transform event. .
///
/// Target position.
public void OverrideTargetPosition(Vector3 position)
{
targetPositionOverridden = true;
targetPosition = position;
}
#endregion
#region Unity methods
#if TOUCHSCRIPT_DEBUG
///
protected override void Awake()
{
base.Awake();
debugID = DebugHelper.GetDebugId(this);
debugPointerSize = Vector2.one*TouchManager.Instance.DotsPerCentimeter*1.1f;
}
#endif
///
protected override void OnEnable()
{
base.OnEnable();
updateScreenTransformThreshold();
updateType();
}
#endregion
#region Gesture callbacks
///
protected override void pointersPressed(IList pointers)
{
base.pointersPressed(pointers);
if (pointersNumState == PointersNumState.PassedMaxThreshold ||
pointersNumState == PointersNumState.PassedMinMaxThreshold)
{
switch (State)
{
case GestureState.Began:
case GestureState.Changed:
setState(GestureState.Ended);
break;
}
} else if (pointersNumState == PointersNumState.PassedMinThreshold)
{
setState(GestureState.Possible);
}
}
///
protected override void pointersReleased(IList pointers)
{
base.pointersReleased(pointers);
if (pointersNumState == PointersNumState.PassedMinThreshold)
{
switch (State)
{
case GestureState.Began:
case GestureState.Changed:
setState(GestureState.Ended);
break;
case GestureState.Possible:
setState(GestureState.Idle);
break;
}
}
}
///
protected override void onBegan()
{
base.onBegan();
if (transformStartedInvoker != null) transformStartedInvoker.InvokeHandleExceptions(this, EventArgs.Empty);
if (UseSendMessage && SendMessageTarget != null)
SendMessageTarget.SendMessage(TRANSFORM_START_MESSAGE, this, SendMessageOptions.DontRequireReceiver);
if (UseUnityEvents) OnTransformStart.Invoke(this);
}
///
protected override void onChanged()
{
base.onChanged();
targetPositionOverridden = false;
if (transformedInvoker != null) transformedInvoker.InvokeHandleExceptions(this, EventArgs.Empty);
if (UseSendMessage && SendMessageTarget != null)
SendMessageTarget.SendMessage(TRANSFORM_MESSAGE, this, SendMessageOptions.DontRequireReceiver);
if (UseUnityEvents) OnTransform.Invoke(this);
}
///
protected override void onRecognized()
{
base.onRecognized();
if (transformCompletedInvoker != null)
transformCompletedInvoker.InvokeHandleExceptions(this, EventArgs.Empty);
if (UseSendMessage && SendMessageTarget != null)
SendMessageTarget.SendMessage(TRANSFORM_COMPLETE_MESSAGE, this, SendMessageOptions.DontRequireReceiver);
if (UseUnityEvents) OnTransformComplete.Invoke(this);
}
///
protected override void reset()
{
base.reset();
resetValues();
isTransforming = false;
}
#endregion
#region Protected methods
///
/// Updates the type of the gesture.
///
protected virtual void updateType() {}
///
/// Resets the frame delta values.
///
protected void resetValues()
{
deltaPosition = Vector3.zero;
deltaRotation = 0f;
deltaScale = 1f;
transformMask = 0;
}
#if TOUCHSCRIPT_DEBUG
protected int debugID;
protected Coroutine debugCoroutine;
protected Vector2 debugPointerSize;
#endif
#endregion
#region Private functions
private void updateScreenTransformThreshold()
{
screenTransformPixelThreshold = screenTransformThreshold * touchManager.DotsPerCentimeter;
screenTransformPixelThresholdSquared = screenTransformPixelThreshold * screenTransformPixelThreshold;
}
#endregion
}
}