From cf85bdea53ca5b16b134bcd0a3a84a1076587f4e Mon Sep 17 00:00:00 2001 From: Kevin O'Mara Date: Tue, 19 Sep 2017 19:34:41 -0700 Subject: [PATCH 1/2] Add optional non-simultaneous transform restriction Can now disallow multiple types of transformations to occur each time a gesture is used. For example, when a gesture is used to rotate it cannot scale or translate. Once the gesture ends it is reset and can again be used for any type of transformation. There is a checkbox in the inspector to select this option. It is under Advanced > General > Simultaneous Transforms. It works for both One- and Two- Point transformations, including two-finger translation, 2+ finger transformations, and clustered transforms. --- .../OnePointTransformGestureBaseEditor.cs | 3 +- .../Base/TransformGestureBaseEditor.cs | 4 +- .../TwoPointTransformGestureBaseEditor.cs | 3 +- .../Base/OnePointTrasformGestureBase.cs | 35 ++++++++++++ .../Base/TransformGestureBase.cs | 22 +++++++ .../Base/TwoPointTransformGestureBase.cs | 57 +++++++++++++++++++ .../TouchScript/Scripts/Utils/Geom/TwoD.cs | 12 ++++ .../Assets/TouchScript/Scripts/Utils/Lock.cs | 29 ++++++++++ 8 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 Source/Assets/TouchScript/Scripts/Utils/Lock.cs 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..4e4116230 100644 --- a/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/OnePointTrasformGestureBase.cs +++ b/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/OnePointTrasformGestureBase.cs @@ -174,6 +174,22 @@ protected override void pointersUpdated(IList pointers) } } + if (!simultaneousTransforms) + { + if (isTransforming) + { + var fixedType = getIndicatedType(screenCenter, oldScreenPos, newScreenPos, projectionParams); + transformLock.TrySetValue(fixedType); + } + + if (transformLock.Locked) + { + var singleType = transformLock.Value; + if (singleType != TransformGesture.TransformType.Rotation) dR = 0; + if (singleType != TransformGesture.TransformType.Scaling) dS = 1; + } + } + if (dR != 0) transformMask |= TransformGesture.TransformType.Rotation; if (dS != 1) transformMask |= TransformGesture.TransformType.Scaling; @@ -241,6 +257,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..1750dc981 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.Unlock(); setState(GestureState.Ended); break; } @@ -282,6 +303,7 @@ protected override void pointersReleased(IList pointers) { case GestureState.Began: case GestureState.Changed: + transformLock.Unlock(); 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..032af239d 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,33 @@ 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 && transformLock.Locked) + { + 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 (dP != Vector3.zero) transformMask |= TransformGesture.TransformType.Translation; if (dR != 0) transformMask |= TransformGesture.TransformType.Rotation; if (dS != 1) transformMask |= TransformGesture.TransformType.Scaling; @@ -320,6 +344,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..7ac599316 --- /dev/null +++ b/Source/Assets/TouchScript/Scripts/Utils/Lock.cs @@ -0,0 +1,29 @@ +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 and lock it. + /// + public void TrySetValue(T value) + { + if (!Locked) + { + Locked = true; + Value = value; + } + } + + public void Unlock() + { + Locked = false; + } + } +} \ No newline at end of file From 8c5984b779299aab0664d5db56de5ce8c0ca41a7 Mon Sep 17 00:00:00 2001 From: Kevin O'Mara Date: Fri, 22 Sep 2017 12:17:55 -0700 Subject: [PATCH 2/2] Fix nonsimultaneous locking into a disabled type Previously, if a transform type was not allowed (i.e. rotation), but the finger movement for that type was made, the gesture would still get locked into that type. --- .../Base/OnePointTrasformGestureBase.cs | 4 +--- .../TransformGestures/Base/TransformGestureBase.cs | 4 ++-- .../Base/TwoPointTransformGestureBase.cs | 3 ++- Source/Assets/TouchScript/Scripts/Utils/Lock.cs | 10 +++++++--- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/OnePointTrasformGestureBase.cs b/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/OnePointTrasformGestureBase.cs index 4e4116230..94c0d5448 100644 --- a/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/OnePointTrasformGestureBase.cs +++ b/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/OnePointTrasformGestureBase.cs @@ -180,13 +180,11 @@ protected override void pointersUpdated(IList pointers) { var fixedType = getIndicatedType(screenCenter, oldScreenPos, newScreenPos, projectionParams); transformLock.TrySetValue(fixedType); - } - if (transformLock.Locked) - { 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(); } } diff --git a/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/TransformGestureBase.cs b/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/TransformGestureBase.cs index 1750dc981..fb6bc62e3 100644 --- a/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/TransformGestureBase.cs +++ b/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/TransformGestureBase.cs @@ -282,7 +282,7 @@ protected override void pointersPressed(IList pointers) { case GestureState.Began: case GestureState.Changed: - transformLock.Unlock(); + transformLock.ClearLock(); setState(GestureState.Ended); break; } @@ -303,7 +303,7 @@ protected override void pointersReleased(IList pointers) { case GestureState.Began: case GestureState.Changed: - transformLock.Unlock(); + 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 032af239d..be23d32fa 100644 --- a/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/TwoPointTransformGestureBase.cs +++ b/Source/Assets/TouchScript/Scripts/Gestures/TransformGestures/Base/TwoPointTransformGestureBase.cs @@ -235,12 +235,13 @@ protected override void pointersUpdated(IList pointers) } } - if (!simultaneousTransforms && transformLock.Locked) + 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; diff --git a/Source/Assets/TouchScript/Scripts/Utils/Lock.cs b/Source/Assets/TouchScript/Scripts/Utils/Lock.cs index 7ac599316..bb0d6f8df 100644 --- a/Source/Assets/TouchScript/Scripts/Utils/Lock.cs +++ b/Source/Assets/TouchScript/Scripts/Utils/Lock.cs @@ -10,18 +10,22 @@ public class Lock public bool Locked { get; private set; } /// - /// If unlocked, set the value and lock it. + /// If unlocked, set the value. /// public void TrySetValue(T value) { if (!Locked) { - Locked = true; Value = value; } } - public void Unlock() + public void SetLock() + { + Locked = true; + } + + public void ClearLock() { Locked = false; }