/* * @author Valentin Simonov / http://va.lent.in/ */ using System; using System.Collections.Generic; using TouchScript.Gestures.TransformGestures.Base; using TouchScript.Layers; using TouchScript.Utils; using TouchScript.Pointers; using UnityEngine.Profiling; #if TOUCHSCRIPT_DEBUG using TouchScript.Debugging.GL; #endif using UnityEngine; namespace TouchScript.Gestures.TransformGestures { /// /// Recognizes a transform gesture, i.e. translation, rotation, scaling or a combination of these. /// [AddComponentMenu("TouchScript/Gestures/Transform Gesture")] [HelpURL("http://touchscript.github.io/docs/html/T_TouchScript_Gestures_TransformGestures_TransformGesture.htm")] public class TransformGesture : TwoPointTransformGestureBase { #region Constants /// /// Types of transformation. /// [Flags] public enum TransformType { /// /// No transform. /// None = 0, /// /// Translation. /// Translation = 0x1, /// /// Rotation. /// Rotation = 0x2, /// /// Scaling. /// Scaling = 0x4 } /// /// Transform's projection type. /// public enum ProjectionType { /// /// Use a plane with normal vector defined by layer. /// Layer, /// /// Use a plane with certain normal vector in local coordinates. /// Object, /// /// Use a plane with certain normal vector in global coordinates. /// Global, } #endregion #region Public properties /// /// Gets or sets transform's projection type. /// /// Projection type. public ProjectionType Projection { get { return projection; } set { if (projection == value) return; projection = value; if (Application.isPlaying) updateProjectionPlane(); } } /// /// Gets or sets transform's projection plane normal. /// /// Projection plane normal. public Vector3 ProjectionPlaneNormal { get { if (projection == ProjectionType.Layer) return projectionLayer.WorldProjectionNormal; return projectionPlaneNormal; } set { if (projection == ProjectionType.Layer) projection = ProjectionType.Object; value.Normalize(); if (projectionPlaneNormal == value) return; projectionPlaneNormal = value; if (Application.isPlaying) updateProjectionPlane(); } } /// /// Plane where transformation occured. /// public Plane TransformPlane { get { return transformPlane; } } /// /// Gets delta position in local coordinates. /// /// Delta position between this frame and the last frame in local coordinates. public Vector3 LocalDeltaPosition { get { return TransformUtils.GlobalToLocalVector(cachedTransform, DeltaPosition); } } #endregion #region Private variables [SerializeField] private bool projectionProps; // Used in the custom inspector [SerializeField] private ProjectionType projection = ProjectionType.Layer; [SerializeField] private Vector3 projectionPlaneNormal = Vector3.forward; private TouchLayer projectionLayer; private Plane transformPlane; private CustomSampler gestureSampler; #endregion #region Public methods #endregion #region Unity methods /// protected override void Awake() { base.Awake(); transformPlane = new Plane(); gestureSampler = CustomSampler.Create("[TouchScript] Transform Gesture"); } /// protected override void OnEnable() { base.OnEnable(); updateProjectionPlane(); } [ContextMenu("Basic Editor")] private void switchToBasicEditor() { basicEditor = true; } #endregion #region Gesture callbacks /// protected override void pointersPressed(IList pointers) { gestureSampler.Begin(); base.pointersPressed(pointers); if (NumPointers == pointers.Count) { projectionLayer = activePointers[0].GetPressData().Layer; updateProjectionPlane(); } gestureSampler.End(); } /// protected override void pointersUpdated(IList pointers) { gestureSampler.Begin(); base.pointersUpdated(pointers); gestureSampler.End(); } /// protected override void pointersReleased(IList pointers) { gestureSampler.Begin(); base.pointersReleased(pointers); #if TOUCHSCRIPT_DEBUG if (getNumPoints() == 0) clearDebug(); else drawDebugDelayed(getNumPoints()); #endif gestureSampler.End(); } #endregion #region Protected methods /// /// Projects the point which was scaled and rotated. /// /// The point. /// Delta rotation. /// Delta scale. /// The projection parameters. /// protected Vector3 projectScaledRotated(Vector2 point, float dR, float dS, ProjectionParams projectionParams) { var center = targetPositionOverridden ? targetPosition : cachedTransform.position; var delta = projectionParams.ProjectTo(point, transformPlane) - center; if (dR != 0) delta = Quaternion.AngleAxis(dR, RotationAxis) * delta; if (dS != 0) delta = delta * dS; return center + delta; } /// protected override float doRotation(Vector2 oldScreenPos1, Vector2 oldScreenPos2, Vector2 newScreenPos1, Vector2 newScreenPos2, ProjectionParams projectionParams) { var newVector = projectionParams.ProjectTo(newScreenPos2, TransformPlane) - projectionParams.ProjectTo(newScreenPos1, TransformPlane); var oldVector = projectionParams.ProjectTo(oldScreenPos2, TransformPlane) - projectionParams.ProjectTo(oldScreenPos1, TransformPlane); var angle = Vector3.Angle(oldVector, newVector); if (Vector3.Dot(Vector3.Cross(oldVector, newVector), TransformPlane.normal) < 0) angle = -angle; return angle; } /// protected override float doScaling(Vector2 oldScreenPos1, Vector2 oldScreenPos2, Vector2 newScreenPos1, Vector2 newScreenPos2, ProjectionParams projectionParams) { var newVector = projectionParams.ProjectTo(newScreenPos2, TransformPlane) - projectionParams.ProjectTo(newScreenPos1, TransformPlane); var oldVector = projectionParams.ProjectTo(oldScreenPos2, TransformPlane) - projectionParams.ProjectTo(oldScreenPos1, TransformPlane); return newVector.magnitude / oldVector.magnitude; } /// protected override Vector3 doOnePointTranslation(Vector2 oldScreenPos, Vector2 newScreenPos, ProjectionParams projectionParams) { if (isTransforming) { return projectionParams.ProjectTo(newScreenPos, TransformPlane) - projectionParams.ProjectTo(oldScreenPos, TransformPlane); } screenPixelTranslationBuffer += newScreenPos - oldScreenPos; if (screenPixelTranslationBuffer.sqrMagnitude > screenTransformPixelThresholdSquared) { isTransforming = true; return projectionParams.ProjectTo(newScreenPos, TransformPlane) - projectionParams.ProjectTo(newScreenPos - screenPixelTranslationBuffer, TransformPlane); } return Vector3.zero; } /// protected override Vector3 doTwoPointTranslation(Vector2 oldScreenPos1, Vector2 oldScreenPos2, Vector2 newScreenPos1, Vector2 newScreenPos2, float dR, float dS, ProjectionParams projectionParams) { if (isTransforming) { return projectionParams.ProjectTo(newScreenPos1, TransformPlane) - projectScaledRotated(oldScreenPos1, dR, dS, projectionParams); } screenPixelTranslationBuffer += newScreenPos1 - oldScreenPos1; if (screenPixelTranslationBuffer.sqrMagnitude > screenTransformPixelThresholdSquared) { isTransforming = true; return projectionParams.ProjectTo(newScreenPos1, TransformPlane) - projectScaledRotated(newScreenPos1 - screenPixelTranslationBuffer, dR, dS, projectionParams); } return Vector3.zero; } #if TOUCHSCRIPT_DEBUG protected override void clearDebug() { base.clearDebug(); GLDebug.RemoveFigure(debugID + 3); } protected override void drawDebug(int pointers) { base.drawDebug(pointers); if (!DebugMode) return; switch (pointers) { case 1: if (projection == ProjectionType.Global || projection == ProjectionType.Object) { GLDebug.DrawPlaneWithNormal(debugID + 3, cachedTransform.position, RotationAxis, 4f, GLDebug.MULTIPLY, float.PositiveInfinity); } break; default: if (projection == ProjectionType.Global || projection == ProjectionType.Object) { GLDebug.DrawPlaneWithNormal(debugID + 3, cachedTransform.position, RotationAxis, 4f, GLDebug.MULTIPLY, float.PositiveInfinity); } break; } } #endif #endregion #region Private functions /// /// Updates projection plane based on options set. /// private void updateProjectionPlane() { if (!Application.isPlaying) return; switch (projection) { case ProjectionType.Layer: if (projectionLayer == null) transformPlane = new Plane(cachedTransform.TransformDirection(Vector3.forward), cachedTransform.position); else transformPlane = new Plane(projectionLayer.WorldProjectionNormal, cachedTransform.position); break; case ProjectionType.Object: transformPlane = new Plane(cachedTransform.TransformDirection(projectionPlaneNormal), cachedTransform.position); break; case ProjectionType.Global: transformPlane = new Plane(projectionPlaneNormal, cachedTransform.position); break; } rotationAxis = transformPlane.normal; } #endregion } }