diff --git a/Source/Assets/TouchScript/Editor/Gestures/TransformGestures/Base/OnePointTransformGestureBaseEditor.cs b/Source/Assets/TouchScript/Editor/Gestures/TransformGestures/Base/OnePointTransformGestureBaseEditor.cs index b2016ecfd..d4cca7ba7 100644 --- a/Source/Assets/TouchScript/Editor/Gestures/TransformGestures/Base/OnePointTransformGestureBaseEditor.cs +++ b/Source/Assets/TouchScript/Editor/Gestures/TransformGestures/Base/OnePointTransformGestureBaseEditor.cs @@ -49,7 +49,8 @@ protected override void drawGeneral() type.intValue = newType; EditorGUI.indentLevel++; - EditorGUIUtility.labelWidth = 160; + EditorGUIUtility.labelWidth = 200; + EditorGUILayout.PropertyField(simultaneousTransform, TEXT_SIMULTANEOUS_TRANSFORM); EditorGUILayout.PropertyField(screenTransformThreshold, TEXT_SCREEN_TRANSFORM_THRESHOLD); base.drawGeneral(); diff --git a/Source/Assets/TouchScript/Editor/Gestures/TransformGestures/Base/TransformGestureBaseEditor.cs b/Source/Assets/TouchScript/Editor/Gestures/TransformGestures/Base/TransformGestureBaseEditor.cs index fec57b9a4..df0268291 100644 --- a/Source/Assets/TouchScript/Editor/Gestures/TransformGestures/Base/TransformGestureBaseEditor.cs +++ b/Source/Assets/TouchScript/Editor/Gestures/TransformGestures/Base/TransformGestureBaseEditor.cs @@ -17,6 +17,7 @@ internal class TransformGestureBaseEditor : GestureEditor public static readonly GUIContent TEXT_TYPE_TRANSLATION = new GUIContent("Translation", "Dragging with one ore more fingers."); public static readonly GUIContent TEXT_TYPE_ROTATION = new GUIContent("Rotation", "Rotating with two or more fingers."); public static readonly GUIContent TEXT_TYPE_SCALING = new GUIContent("Scaling", "Scaling with two or more fingers."); + public static readonly GUIContent TEXT_SIMULTANEOUS_TRANSFORM = new GUIContent("Simultaneous Transformations", "Whether multiple types of transformations can occur each time this gesture is used."); public static readonly GUIContent TEXT_MIN_SCREEN_POINTS_DISTANCE = new GUIContent("Min Points Distance (cm)", "Minimum distance between two pointers (clusters) in cm to consider this gesture started. Used to prevent fake pointers spawned near real ones on cheap multitouch hardware to mess everything up."); public static readonly GUIContent TEXT_SCREEN_TRANSFORM_THRESHOLD = new GUIContent("Movement Threshold (cm)", "Minimum distance in cm pointers must move for the gesture to begin."); @@ -27,7 +28,7 @@ internal class TransformGestureBaseEditor : GestureEditor public static readonly GUIContent TEXT_PROJECTION_NORMAL = new GUIContent("Projection Normal", "Normal of the plane in 3d space where pointers' positions are projected."); - protected SerializedProperty type, minScreenPointsDistance, screenTransformThreshold; + protected SerializedProperty type, simultaneousTransform, minScreenPointsDistance, screenTransformThreshold; protected SerializedProperty OnTransformStart, OnTransform, OnTransformComplete; public SerializedProperty projection, projectionPlaneNormal; @@ -41,6 +42,7 @@ internal class TransformGestureBaseEditor : GestureEditor protected override void OnEnable() { type = serializedObject.FindProperty("type"); + simultaneousTransform = serializedObject.FindProperty("simultaneousTransforms"); minScreenPointsDistance = serializedObject.FindProperty("minScreenPointsDistance"); screenTransformThreshold = serializedObject.FindProperty("screenTransformThreshold"); OnTransformStart = serializedObject.FindProperty("OnTransformStart"); diff --git a/Source/Assets/TouchScript/Editor/Gestures/TransformGestures/Base/TwoPointTransformGestureBaseEditor.cs b/Source/Assets/TouchScript/Editor/Gestures/TransformGestures/Base/TwoPointTransformGestureBaseEditor.cs index 4cb6492f4..f1ea99de1 100644 --- a/Source/Assets/TouchScript/Editor/Gestures/TransformGestures/Base/TwoPointTransformGestureBaseEditor.cs +++ b/Source/Assets/TouchScript/Editor/Gestures/TransformGestures/Base/TwoPointTransformGestureBaseEditor.cs @@ -60,7 +60,8 @@ protected override void drawGeneral() EditorGUI.indentLevel++; - EditorGUIUtility.labelWidth = 160; + EditorGUIUtility.labelWidth = 200; + EditorGUILayout.PropertyField(simultaneousTransform, TEXT_SIMULTANEOUS_TRANSFORM); EditorGUILayout.PropertyField(minScreenPointsDistance, TEXT_MIN_SCREEN_POINTS_DISTANCE); EditorGUILayout.PropertyField(screenTransformThreshold, TEXT_SCREEN_TRANSFORM_THRESHOLD); diff --git a/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/OnePointTrasformGestureBase.cs b/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/OnePointTrasformGestureBase.cs index 40abbf73a..94c0d5448 100644 --- a/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/OnePointTrasformGestureBase.cs +++ b/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/OnePointTrasformGestureBase.cs @@ -174,6 +174,20 @@ protected override void pointersUpdated(IList pointers) } } + if (!simultaneousTransforms) + { + if (isTransforming) + { + var fixedType = getIndicatedType(screenCenter, oldScreenPos, newScreenPos, projectionParams); + transformLock.TrySetValue(fixedType); + + var singleType = transformLock.Value; + if (singleType != TransformGesture.TransformType.Rotation) dR = 0; + if (singleType != TransformGesture.TransformType.Scaling) dS = 1; + if (singleType != 0 && type.HasFlag(singleType)) transformLock.SetLock(); + } + } + if (dR != 0) transformMask |= TransformGesture.TransformType.Rotation; if (dS != 1) transformMask |= TransformGesture.TransformType.Scaling; @@ -241,6 +255,25 @@ protected virtual float doScaling(Vector3 center, Vector2 oldScreenPos, Vector2 return 1; } + /// + /// Return the indicated by the finger's movement. + /// + /// Center screen position. + /// Pointer old screen position. + /// Pointer new screen position. + /// Layer projection parameters. + /// TransformType indicated by the movement of the pointer. + protected virtual TransformGesture.TransformType getIndicatedType(Vector2 screenCenter, Vector2 oldScreenPos, Vector2 newScreenPos, ProjectionParams projectionParams) + { + var centerLine = oldScreenPos - screenCenter; + var pointerDelta = newScreenPos - oldScreenPos; + + if (TwoD.IsPerpendicular(centerLine, pointerDelta)) + return TransformGesture.TransformType.Rotation; + else + return TransformGesture.TransformType.Scaling; + } + /// /// Checks if there are pointers in the list which matter for the gesture. /// diff --git a/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/TransformGestureBase.cs b/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/TransformGestureBase.cs index 13acb87bc..fb6bc62e3 100644 --- a/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/TransformGestureBase.cs +++ b/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/TransformGestureBase.cs @@ -100,6 +100,15 @@ public TransformGesture.TransformType Type } } + /// + /// Gets or sets whether multiple types of transformations can occur each time this gesture is used. + /// + public bool SimultaneousTransforms + { + get { return simultaneousTransforms; } + set { simultaneousTransforms = value; } + } + /// /// Gets or sets minimum distance in cm for pointers to move for gesture to begin. /// @@ -163,6 +172,11 @@ public Vector3 RotationAxis /// protected TransformGesture.TransformType transformMask; + /// + /// The single transform type this gesture is restricted to. + /// + protected Lock transformLock = new Lock(); + /// /// Calculated delta position. /// @@ -206,6 +220,12 @@ public Vector3 RotationAxis protected TransformGesture.TransformType type = TransformGesture.TransformType.Translation | TransformGesture.TransformType.Scaling | TransformGesture.TransformType.Rotation; + /// + /// Whether multiple types of transformations can occur each time this gesture is used. + /// + [SerializeField] + protected bool simultaneousTransforms = true; + [SerializeField] private float screenTransformThreshold = 0.1f; @@ -262,6 +282,7 @@ protected override void pointersPressed(IList pointers) { case GestureState.Began: case GestureState.Changed: + transformLock.ClearLock(); setState(GestureState.Ended); break; } @@ -282,6 +303,7 @@ protected override void pointersReleased(IList pointers) { case GestureState.Began: case GestureState.Changed: + transformLock.ClearLock(); setState(GestureState.Ended); break; case GestureState.Possible: diff --git a/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/TwoPointTransformGestureBase.cs b/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/TwoPointTransformGestureBase.cs index 1f7884624..be23d32fa 100644 --- a/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/TwoPointTransformGestureBase.cs +++ b/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/TwoPointTransformGestureBase.cs @@ -141,6 +141,11 @@ protected override void pointersUpdated(IList pointers) // translate using one point dP = doOnePointTranslation(getPointPreviousScreenPosition(0), getPointScreenPosition(0), projectionParams); + + if (!simultaneousTransforms && isTransforming) + { + transformLock.TrySetValue(TransformGesture.TransformType.Translation); + } } else { @@ -211,14 +216,34 @@ protected override void pointersUpdated(IList pointers) else dP = doTwoPointTranslation(oldScreenPos1, oldScreenPos2, newScreenPos1, newScreenPos2, dR, dS, projectionParams); } + + if (!simultaneousTransforms && isTransforming) + { + var fixedType = getIndicatedType(oldScreenPos1, oldScreenPos2, newScreenPos1, newScreenPos2, projectionParams); + transformLock.TrySetValue(fixedType); + } } else if (translationEnabled) { // points are too close, translate using one point dP = doOnePointTranslation(oldScreenPos1, newScreenPos1, projectionParams); + + if (!simultaneousTransforms && isTransforming) + { + transformLock.TrySetValue(TransformGesture.TransformType.Translation); + } } } + if (!simultaneousTransforms && isTransforming) + { + var singleType = transformLock.Value; + if (singleType != TransformGesture.TransformType.Translation) dP = Vector3.zero; + if (singleType != TransformGesture.TransformType.Rotation) dR = 0; + if (singleType != TransformGesture.TransformType.Scaling) dS = 1; + if (singleType != 0 && type.HasFlag(singleType)) transformLock.SetLock(); + } + if (dP != Vector3.zero) transformMask |= TransformGesture.TransformType.Translation; if (dR != 0) transformMask |= TransformGesture.TransformType.Rotation; if (dS != 1) transformMask |= TransformGesture.TransformType.Scaling; @@ -320,6 +345,39 @@ protected virtual Vector3 doTwoPointTranslation(Vector2 oldScreenPos1, Vector2 o return Vector3.zero; } + /// + /// Return the indicated by the both fingers' movement. + /// + /// Finger one old screen position. + /// Finger two old screen position. + /// Finger one new screen position. + /// Finger two new screen position. + /// Layer projection parameters. + /// TransformType indicated by the movement of both fingers. + protected virtual TransformGesture.TransformType getIndicatedType(Vector2 oldScreenPos1, Vector2 oldScreenPos2, + Vector2 newScreenPos1, Vector2 newScreenPos2, + ProjectionParams projectionParams) + { + var pointerDelta1 = newScreenPos1 - oldScreenPos1; + var pointerDelta2 = newScreenPos2 - oldScreenPos2; + var deg = Vector2.Angle(pointerDelta1, pointerDelta2); + + if (deg < 90) + { + // pointers moved in same direction + return TransformGesture.TransformType.Translation; + } + else + { + // pointers moved in opposite directions + var oldScreenDelta = oldScreenPos2 - oldScreenPos1; + if (TwoD.IsPerpendicular(pointerDelta1, oldScreenDelta) && TwoD.IsPerpendicular(pointerDelta2, oldScreenDelta)) + return TransformGesture.TransformType.Rotation; + else + return TransformGesture.TransformType.Scaling; + } + } + /// /// Gets the number of points. /// diff --git a/Source/Assets/TouchScript/Scripts/Utils/Geom/TwoD.cs b/Source/Assets/TouchScript/Scripts/Utils/Geom/TwoD.cs index 1c74320ea..7d64d102f 100644 --- a/Source/Assets/TouchScript/Scripts/Utils/Geom/TwoD.cs +++ b/Source/Assets/TouchScript/Scripts/Utils/Geom/TwoD.cs @@ -58,5 +58,17 @@ public static Vector2 Rotate(Vector2 point, float angle) var sin = Mathf.Sin(rad); return new Vector2(point.x * cos - point.y * sin, point.x * sin + point.y * cos); } + + /// + /// Determines if two lines are approximately perpendicular. + /// + /// Line to check. + /// Line to check. + /// true if both lines are approximately perpendicular; false otherwise. + public static bool IsPerpendicular(Vector2 line1, Vector2 line2) + { + var deg = Vector2.Angle(line1, line2); + return (45 < deg) && (deg < 135); + } } } \ No newline at end of file diff --git a/Source/Assets/TouchScript/Scripts/Utils/Lock.cs b/Source/Assets/TouchScript/Scripts/Utils/Lock.cs new file mode 100644 index 000000000..bb0d6f8df --- /dev/null +++ b/Source/Assets/TouchScript/Scripts/Utils/Lock.cs @@ -0,0 +1,33 @@ +namespace TouchScript.Utils +{ + /// + /// Holds a value which can only be changed when unlocked. + /// + public class Lock + { + public T Value { get; private set; } + + public bool Locked { get; private set; } + + /// + /// If unlocked, set the value. + /// + public void TrySetValue(T value) + { + if (!Locked) + { + Value = value; + } + } + + public void SetLock() + { + Locked = true; + } + + public void ClearLock() + { + Locked = false; + } + } +} \ No newline at end of file