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