diff --git a/src/core.c b/src/core.c index cf6fcf335..1c9e16ae2 100644 --- a/src/core.c +++ b/src/core.c @@ -253,6 +253,7 @@ static void InitGamepad(void); // Init raw gamepad inpu static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods); // GLFW3 Mouse Button Callback, runs on mouse button pressed +static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); // GLFW3 Cursor Position Callback, runs on mouse move static void CharCallback(GLFWwindow *window, unsigned int key); // GLFW3 Char Key Callback, runs on key pressed (get char value) static void ScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area @@ -1415,6 +1416,7 @@ static void InitDisplay(int width, int height) glfwSetCursorEnterCallback(window, CursorEnterCallback); glfwSetKeyCallback(window, KeyCallback); glfwSetMouseButtonCallback(window, MouseButtonCallback); + glfwSetCursorPosCallback(window, MouseCursorPosCallback); // Track mouse position changes glfwSetCharCallback(window, CharCallback); glfwSetScrollCallback(window, ScrollCallback); glfwSetWindowIconifyCallback(window, WindowIconifyCallback); @@ -1677,7 +1679,7 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int // Register touch actions if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) gestureEvent.touchAction = TOUCH_DOWN; - else if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) gestureEvent.touchAction = TOUCH_MOVE; + //else if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) gestureEvent.touchAction = TOUCH_MOVE; else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) gestureEvent.touchAction = TOUCH_UP; // Register touch points count @@ -1685,7 +1687,28 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int // Register touch points position, only one point registered gestureEvent.position[0] = GetMousePosition(); + + // Gesture data is sent to gestures system for processing + ProcessGestureEvent(gestureEvent); +#endif +} + +// GLFW3 Cursor Position Callback, runs on mouse move +static void MouseCursorPosCallback(GLFWwindow *window, double x, double y) +{ +#define ENABLE_MOUSE_GESTURES +#if defined(ENABLE_MOUSE_GESTURES) + // Process mouse events as touches to be able to use mouse-gestures + GestureEvent gestureEvent; + + gestureEvent.touchAction = TOUCH_MOVE; + + // Register touch points count + gestureEvent.pointCount = 1; + // Register touch points position, only one point registered + gestureEvent.position[0] = (Vector2){ (float)x, (float)y }; + // Gesture data is sent to gestures system for processing ProcessGestureEvent(gestureEvent); #endif @@ -1934,7 +1957,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) // Register touch points count gestureEvent.pointCount = AMotionEvent_getPointerCount(event); - // Register touch points id DESKTOP + // Register touch points id gestureEvent.pointerId[0] = AMotionEvent_getPointerId(event, 0); gestureEvent.pointerId[1] = AMotionEvent_getPointerId(event, 1); @@ -2496,7 +2519,7 @@ static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent // Register touch points count gestureEvent.pointCount = touchEvent->numTouches; - // Register touch points id WEB + // Register touch points id gestureEvent.pointerId[0] = touchEvent->touches[0].identifier; gestureEvent.pointerId[1] = touchEvent->touches[1].identifier; diff --git a/src/gestures.c b/src/gestures.c index ea744555a..59f2c5b75 100644 --- a/src/gestures.c +++ b/src/gestures.c @@ -46,7 +46,11 @@ // Defines and Macros //---------------------------------------------------------------------------------- #define FORCE_TO_SWIPE 20 -#define TAP_TIMEOUT 300 +#define FORCE_TO_DRAG 20 +#define FORCE_TO_PINCH 5 +#define TAP_TIMEOUT 300 // Time in milliseconds +#define PINCH_TIMEOUT 300 // Time in milliseconds +#define DOUBLETAP_RANGE 30 //#define MAX_TOUCH_POINTS 4 //---------------------------------------------------------------------------------- @@ -61,10 +65,6 @@ typedef enum { //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -static GestureType gestureType = TYPE_MOTIONLESS; -static double eventTime = 0; -//static int32_t touchId; // Not used... - // Tap gesture variables static Vector2 initialTapPosition = { 0, 0 }; @@ -78,6 +78,21 @@ static Vector2 endDragPosition = { 0, 0 }; static Vector2 lastDragPosition = { 0, 0 }; static Vector2 dragVector = { 0, 0 }; +// Albert&Ian +static Vector2 touchDownPosition = { 0, 0 }; +static Vector2 touchDownPosition2 = { 0, 0 }; +static Vector2 touchUpPosition = { 0, 0 }; +static Vector2 moveDownPosition = { 0, 0 }; +static Vector2 moveDownPosition2 = { 0, 0 }; + +static int numTap = 0; +static int numHold = 0; +static int numPinch = 0; +static int pointCount = 0; +static int touchId = -1; + +static double eventTime = 0; + static float magnitude = 0; // Distance traveled dragging static float angle = 0; // Angle direction of the drag static float intensity = 0; // How fast we did the drag (pixels per frame) @@ -95,7 +110,7 @@ static int previousGesture = GESTURE_NONE; static int currentGesture = GESTURE_NONE; // Enabled gestures flags, all gestures enabled by default -static unsigned int enabledGestures = 0b0000011111111111; +static unsigned int enabledGestures = 0b0000001111111111; //---------------------------------------------------------------------------------- // Module specific Functions Declaration @@ -105,6 +120,7 @@ static float CalculateAngle(Vector2 initialPosition, Vector2 actualPosition, flo static float VectorDistance(Vector2 v1, Vector2 v2); static float VectorDotProduct(Vector2 v1, Vector2 v2); static double GetCurrentTime(); +static float Vector2Distance(); //---------------------------------------------------------------------------------- // Module Functions Definition @@ -119,173 +135,160 @@ void ProcessGestureEvent(GestureEvent event) previousGesture = currentGesture; - switch (gestureType) - { - case TYPE_MOTIONLESS: // Detect TAP, DOUBLE_TAP and HOLD events + pointCount = event.pointCount; + + // Albert&Ian + if (pointCount < 2) + { + touchId = event.pointerId[0]; + if (event.touchAction == TOUCH_DOWN) { - if (event.touchAction == TOUCH_DOWN) + numTap++; // Tap counter + + // Detect GESTURE_DOUBLE_TAP + if ((currentGesture == GESTURE_NONE) && (numTap >= 2) && ((GetCurrentTime() - eventTime) < TAP_TIMEOUT) && (GetMagnitude(touchDownPosition, event.position[0]) < DOUBLETAP_RANGE)) { - if (event.pointCount > 1) InitPinchGesture(event.position[0], event.position[1]); - else - { - // Set the press position - initialTapPosition = event.position[0]; - - // If too much time have passed, we reset the double tap - if (GetCurrentTime() - eventTime > TAP_TIMEOUT) untap = false; - - // If we are in time, we detect the double tap - if (untap) doubleTapping = true; - - // Update our event time - eventTime = GetCurrentTime(); - - // Set hold - if (doubleTapping) currentGesture = GESTURE_DOUBLETAP; - else currentGesture = GESTURE_TAP; - } + currentGesture = GESTURE_DOUBLETAP; + numTap = 0; } - else if (event.touchAction == TOUCH_UP) + else // Detect GESTURE_TAP { - currentGesture = GESTURE_NONE; - - // Detect that we are tapping instead of holding - if (GetCurrentTime() - eventTime < TAP_TIMEOUT) - { - if (doubleTapping) untap = false; - else untap = true; - } - - // Tap finished - doubleTapping = false; - - // Update our event time - eventTime = GetCurrentTime(); + numTap = 1; + currentGesture = GESTURE_TAP; } - // Begin dragging - else if (event.touchAction == TOUCH_MOVE) + + touchDownPosition = event.position[0]; + + touchUpPosition = touchDownPosition; + eventTime = GetCurrentTime(); + } + else if (event.touchAction == TOUCH_UP) + { + if (currentGesture = GESTURE_DRAG) { - if (event.pointCount > 1) InitPinchGesture(event.position[0], event.position[1]); - else - { - // Set the drag starting position - initialDragPosition = initialTapPosition; - endDragPosition = initialDragPosition; - - // Initialize drag - draggingTimeCounter = 0; - gestureType = TYPE_DRAG; - currentGesture = GESTURE_NONE; - } + touchUpPosition = event.position[0]; } - } break; - case TYPE_DRAG: // Detect DRAG and SWIPE events - { - // end of the drag - if (event.touchAction == TOUCH_UP) + + // Calculate for swipe + magnitude = GetMagnitude(touchDownPosition, touchUpPosition); + intensity = magnitude / (float)draggingTimeCounter; + + // Detect GESTURE_SWIPE + if ((intensity > FORCE_TO_SWIPE) && (touchId == 0)) + { + angle = CalculateAngle(touchDownPosition, touchUpPosition, magnitude); + if ((angle < 30) || (angle > 330)) currentGesture = GESTURE_SWIPE_RIGHT; // Right + else if ((angle > 30) && (angle < 120)) currentGesture = GESTURE_SWIPE_UP; // Up + else if ((angle > 120) && (angle < 210)) currentGesture = GESTURE_SWIPE_LEFT; // Left + else if ((angle > 210) && (angle < 300)) currentGesture = GESTURE_SWIPE_DOWN; // Down + else currentGesture = GESTURE_NONE; + } + else { - // Return Swipe if we have enough sensitivity - if (intensity > FORCE_TO_SWIPE) - { - if (angle < 30 || angle > 330) currentGesture = GESTURE_SWIPE_RIGHT; // Right - else if (angle > 60 && angle < 120) currentGesture = GESTURE_SWIPE_UP; // Up - else if (angle > 150 && angle < 210) currentGesture = GESTURE_SWIPE_LEFT; // Left - else if (angle > 240 && angle < 300) currentGesture = GESTURE_SWIPE_DOWN; // Down - } - magnitude = 0; angle = 0; intensity = 0; - gestureType = TYPE_MOTIONLESS; + currentGesture = GESTURE_NONE; } - // Update while we are dragging - else if (event.touchAction == TOUCH_MOVE) + + draggingTimeCounter = 0; + } + else if (event.touchAction == TOUCH_MOVE) + { + if (GetMagnitude(moveDownPosition, event.position[0]) > 5) eventTime = GetCurrentTime(); + moveDownPosition = event.position[0]; + + if (currentGesture == GESTURE_HOLD) { - if (event.pointCount > 1) InitPinchGesture(event.position[0], event.position[1]); - else - { - lastDragPosition = endDragPosition; - endDragPosition = event.position[0]; - - //endDragPosition.x = AMotionEvent_getX(event, 0); - //endDragPosition.y = AMotionEvent_getY(event, 0); - - // Calculate attributes - dragVector = (Vector2){ endDragPosition.x - lastDragPosition.x, endDragPosition.y - lastDragPosition.y }; - magnitude = sqrt(pow(endDragPosition.x - initialDragPosition.x, 2) + pow(endDragPosition.y - initialDragPosition.y, 2)); - angle = CalculateAngle(initialDragPosition, endDragPosition, magnitude); - intensity = magnitude / (float)draggingTimeCounter; - - // Check if drag movement is less than minimum to keep it as hold state or switch to drag state - if(magnitude > FORCE_TO_SWIPE) - { - currentGesture = GESTURE_DRAG; - draggingTimeCounter++; - } - else currentGesture = GESTURE_HOLD; - } + if (numHold == 1) touchDownPosition = event.position[0]; + + numHold = 2; + + magnitude = GetMagnitude(touchDownPosition, moveDownPosition); + + // Detect GESTURE_DRAG + if (magnitude >= FORCE_TO_DRAG) currentGesture = GESTURE_DRAG; } - } break; - case TYPE_DUAL_INPUT: + + draggingTimeCounter++; + } + } + else + { + // two fingers + + if (event.touchAction == TOUCH_DOWN) + { + touchDownPosition = event.position[0]; + touchDownPosition2 = event.position[1]; + + currentGesture = GESTURE_HOLD; + } + else if (event.touchAction == TOUCH_MOVE) { - if (event.touchAction == TOUCH_UP) + magnitude = GetMagnitude(moveDownPosition, moveDownPosition2); + + touchDownPosition = moveDownPosition; + touchDownPosition2 = moveDownPosition2; + + moveDownPosition = event.position[0]; + moveDownPosition2 = event.position[1]; + + if ( (GetMagnitude(touchDownPosition, moveDownPosition) > FORCE_TO_PINCH) || (GetMagnitude(touchDownPosition2, moveDownPosition2) > FORCE_TO_PINCH)) { - if (event.pointCount == 1) - { - // Set the drag starting position - initialTapPosition = event.position[0]; - } - gestureType = TYPE_MOTIONLESS; + if ((GetMagnitude(moveDownPosition, moveDownPosition2) - magnitude) < 0) currentGesture = GESTURE_PINCH_IN; + else currentGesture = GESTURE_PINCH_OUT; } - else if (event.touchAction == TOUCH_MOVE) + else { - // Adapt the ending position of the inputs - firstEndPinchPosition = event.position[0]; - secondEndPinchPosition = event.position[1]; - - // If there is no more than two inputs - if (event.pointCount == 2) - { - // Calculate distances - float initialDistance = VectorDistance(firstInitialPinchPosition, secondInitialPinchPosition); - float endDistance = VectorDistance(firstEndPinchPosition, secondEndPinchPosition); - - // Calculate Vectors - Vector2 firstTouchVector = { firstEndPinchPosition.x - firstInitialPinchPosition.x, firstEndPinchPosition.y - firstInitialPinchPosition.y }; - Vector2 secondTouchVector = { secondEndPinchPosition.x - secondInitialPinchPosition.x, secondEndPinchPosition.y - secondInitialPinchPosition.y }; - - // Detect the pinch gesture - if (VectorDotProduct(firstTouchVector, secondTouchVector) < -0.5) pinchDelta = initialDistance - endDistance; - else pinchDelta = 0; - - // Pinch gesture resolution - if (pinchDelta != 0) - { - if (pinchDelta > 0) currentGesture = GESTURE_PINCH_IN; - else currentGesture = GESTURE_PINCH_OUT; - } - } - else - { - // Set the drag starting position - initialTapPosition = event.position[0]; - - gestureType = TYPE_MOTIONLESS; - } - - // Readapt the initial position of the inputs - firstInitialPinchPosition = firstEndPinchPosition; - secondInitialPinchPosition = secondEndPinchPosition; + currentGesture = GESTURE_HOLD; } - } break; + } + else if (event.touchAction == TOUCH_UP) + { + currentGesture = GESTURE_NONE; + } + } +} + +// Update gestures detected (must be called every frame) +void UpdateGestures(void) +{ + // NOTE: Gestures are processed through system callbacks on touch events + + // Detect GESTURE_HOLD + if (((currentGesture == GESTURE_TAP) || (currentGesture == GESTURE_DOUBLETAP)) && pointCount < 2) currentGesture = GESTURE_HOLD; + if ((GetCurrentTime() - eventTime) > TAP_TIMEOUT && (currentGesture == GESTURE_DRAG) && pointCount < 2) + { + currentGesture = GESTURE_HOLD; + numHold = 1; } + + // Detect GESTURE_NONE + if ((currentGesture == GESTURE_SWIPE_RIGHT) || (currentGesture == GESTURE_SWIPE_UP) || (currentGesture == GESTURE_SWIPE_LEFT) || (currentGesture == GESTURE_SWIPE_DOWN)) + { + currentGesture = GESTURE_NONE; + } +} + +// Calculate distance between two vectors +float Vector2Distance(Vector2 v1, Vector3 v2) +{ + float result; + + float dx = v2.x - v1.x; + float dy = v2.y - v1.y; + + result = sqrt(dx*dx + dy*dy); + + return result; } // Check if a gesture have been detected bool IsGestureDetected(void) { - if (currentGesture != GESTURE_NONE) return true; + if ((enabledGestures & currentGesture) != GESTURE_NONE) return true; else return false; } @@ -298,7 +301,7 @@ int GetGestureType(void) void SetGesturesEnabled(unsigned int gestureFlags) { - enabledGestures = enabledGestures | gestureFlags; + enabledGestures = gestureFlags; } // Get drag intensity (pixels per frame) @@ -440,6 +443,7 @@ static float VectorDotProduct(Vector2 v1, Vector2 v2) return result; } +// Time measure returned are milliseconds static double GetCurrentTime() { double time = 0; diff --git a/src/raylib.h b/src/raylib.h index 6c1a8999e..a22c3f834 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -435,17 +435,17 @@ typedef enum { BLEND_ALPHA = 0, BLEND_ADDITIVE, BLEND_MULTIPLIED } BlendMode; // Gestures type // NOTE: It could be used as flags to enable only some gestures typedef enum { - GESTURE_NONE = 1, - GESTURE_TAP = 2, - GESTURE_DOUBLETAP = 4, - GESTURE_HOLD = 8, - GESTURE_DRAG = 16, - GESTURE_SWIPE_RIGHT = 32, - GESTURE_SWIPE_LEFT = 64, - GESTURE_SWIPE_UP = 128, - GESTURE_SWIPE_DOWN = 256, - GESTURE_PINCH_IN = 512, - GESTURE_PINCH_OUT = 1024 + GESTURE_NONE = 0, + GESTURE_TAP = 1, + GESTURE_DOUBLETAP = 2, + GESTURE_HOLD = 4, + GESTURE_DRAG = 8, + GESTURE_SWIPE_RIGHT = 16, + GESTURE_SWIPE_LEFT = 32, + GESTURE_SWIPE_UP = 64, + GESTURE_SWIPE_DOWN = 128, + GESTURE_PINCH_IN = 256, + GESTURE_PINCH_OUT = 512 } Gestures; typedef enum { TOUCH_UP, TOUCH_DOWN, TOUCH_MOVE } TouchAction; @@ -781,7 +781,6 @@ bool CheckCollisionRaySphereEx(Ray ray, Vector3 spherePosition, float sphereRadi bool CheckCollisionRayBox(Ray ray, Vector3 minBBox, Vector3 maxBBox); // Detect collision between ray and box Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *playerPosition, float radius); // Detect collision of player radius with cubicmap // NOTE: Return the normal vector of the impacted surface - //------------------------------------------------------------------------------------ // Shaders System Functions (Module: rlgl) // NOTE: This functions are useless when using OpenGL 1.1