/* * @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 } }