aboutsummaryrefslogtreecommitdiff
path: root/raylib/src/rgestures.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--raylib/src/rgestures.h555
1 files changed, 555 insertions, 0 deletions
diff --git a/raylib/src/rgestures.h b/raylib/src/rgestures.h
new file mode 100644
index 0000000..b5624be
--- /dev/null
+++ b/raylib/src/rgestures.h
@@ -0,0 +1,555 @@
1/**********************************************************************************************
2*
3* rgestures - Gestures system, gestures processing based on input events (touch/mouse)
4*
5* CONFIGURATION:
6* #define RGESTURES_IMPLEMENTATION
7* Generates the implementation of the library into the included file.
8* If not defined, the library is in header only mode and can be included in other headers
9* or source files without problems. But only ONE file should hold the implementation.
10*
11* #define RGESTURES_STANDALONE
12* If defined, the library can be used as standalone to process gesture events with
13* no external dependencies.
14*
15* CONTRIBUTORS:
16* Marc Palau: Initial implementation (2014)
17* Albert Martos: Complete redesign and testing (2015)
18* Ian Eito: Complete redesign and testing (2015)
19* Ramon Santamaria: Supervision, review, update and maintenance
20*
21*
22* LICENSE: zlib/libpng
23*
24* Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
25*
26* This software is provided "as-is", without any express or implied warranty. In no event
27* will the authors be held liable for any damages arising from the use of this software.
28*
29* Permission is granted to anyone to use this software for any purpose, including commercial
30* applications, and to alter it and redistribute it freely, subject to the following restrictions:
31*
32* 1. The origin of this software must not be misrepresented; you must not claim that you
33* wrote the original software. If you use this software in a product, an acknowledgment
34* in the product documentation would be appreciated but is not required.
35*
36* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
37* as being the original software.
38*
39* 3. This notice may not be removed or altered from any source distribution.
40*
41**********************************************************************************************/
42
43#ifndef RGESTURES_H
44#define RGESTURES_H
45
46#ifndef PI
47 #define PI 3.14159265358979323846
48#endif
49
50//----------------------------------------------------------------------------------
51// Defines and Macros
52//----------------------------------------------------------------------------------
53#ifndef MAX_TOUCH_POINTS
54 #define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported
55#endif
56
57//----------------------------------------------------------------------------------
58// Types and Structures Definition
59// NOTE: Below types are required for standalone usage
60//----------------------------------------------------------------------------------
61// Boolean type
62#if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800)
63 #include <stdbool.h>
64#elif !defined(__cplusplus) && !defined(bool) && !defined(RL_BOOL_TYPE)
65 typedef enum bool { false = 0, true = !false } bool;
66#endif
67
68#if !defined(RL_VECTOR2_TYPE)
69// Vector2 type
70typedef struct Vector2 {
71 float x;
72 float y;
73} Vector2;
74#endif
75
76#if defined(RGESTURES_STANDALONE)
77// Gestures type
78// NOTE: It could be used as flags to enable only some gestures
79typedef enum {
80 GESTURE_NONE = 0,
81 GESTURE_TAP = 1,
82 GESTURE_DOUBLETAP = 2,
83 GESTURE_HOLD = 4,
84 GESTURE_DRAG = 8,
85 GESTURE_SWIPE_RIGHT = 16,
86 GESTURE_SWIPE_LEFT = 32,
87 GESTURE_SWIPE_UP = 64,
88 GESTURE_SWIPE_DOWN = 128,
89 GESTURE_PINCH_IN = 256,
90 GESTURE_PINCH_OUT = 512
91} Gesture;
92#endif
93
94typedef enum {
95 TOUCH_ACTION_UP = 0,
96 TOUCH_ACTION_DOWN,
97 TOUCH_ACTION_MOVE,
98 TOUCH_ACTION_CANCEL
99} TouchAction;
100
101// Gesture event
102typedef struct {
103 int touchAction;
104 int pointCount;
105 int pointId[MAX_TOUCH_POINTS];
106 Vector2 position[MAX_TOUCH_POINTS];
107} GestureEvent;
108
109//----------------------------------------------------------------------------------
110// Global Variables Definition
111//----------------------------------------------------------------------------------
112//...
113
114//----------------------------------------------------------------------------------
115// Module Functions Declaration
116//----------------------------------------------------------------------------------
117
118#if defined(__cplusplus)
119extern "C" { // Prevents name mangling of functions
120#endif
121
122void ProcessGestureEvent(GestureEvent event); // Process gesture event and translate it into gestures
123void UpdateGestures(void); // Update gestures detected (must be called every frame)
124
125#if defined(RGESTURES_STANDALONE)
126void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags
127bool IsGestureDetected(int gesture); // Check if a gesture have been detected
128int GetGestureDetected(void); // Get latest detected gesture
129
130float GetGestureHoldDuration(void); // Get gesture hold time in seconds
131Vector2 GetGestureDragVector(void); // Get gesture drag vector
132float GetGestureDragAngle(void); // Get gesture drag angle
133Vector2 GetGesturePinchVector(void); // Get gesture pinch delta
134float GetGesturePinchAngle(void); // Get gesture pinch angle
135#endif
136
137#if defined(__cplusplus)
138}
139#endif
140
141#endif // RGESTURES_H
142
143/***********************************************************************************
144*
145* RGESTURES IMPLEMENTATION
146*
147************************************************************************************/
148
149#if defined(RGESTURES_IMPLEMENTATION)
150
151#if defined(RGESTURES_STANDALONE)
152#if defined(_WIN32)
153 #if defined(__cplusplus)
154 extern "C" { // Prevents name mangling of functions
155 #endif
156 // Functions required to query time on Windows
157 int __stdcall QueryPerformanceCounter(unsigned long long int *lpPerformanceCount);
158 int __stdcall QueryPerformanceFrequency(unsigned long long int *lpFrequency);
159 #if defined(__cplusplus)
160 }
161 #endif
162#elif defined(__linux__)
163 #if _POSIX_C_SOURCE < 199309L
164 #undef _POSIX_C_SOURCE
165 #define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext.
166 #endif
167 #include <sys/time.h> // Required for: timespec
168 #include <time.h> // Required for: clock_gettime()
169
170 #include <math.h> // Required for: sqrtf(), atan2f()
171#endif
172#if defined(__APPLE__) // macOS also defines __MACH__
173 #include <mach/clock.h> // Required for: clock_get_time()
174 #include <mach/mach.h> // Required for: mach_timespec_t
175#endif
176#endif
177
178//----------------------------------------------------------------------------------
179// Defines and Macros
180//----------------------------------------------------------------------------------
181#define FORCE_TO_SWIPE 0.2f // Swipe force, measured in normalized screen units/time
182#define MINIMUM_DRAG 0.015f // Drag minimum force, measured in normalized screen units (0.0f to 1.0f)
183#define DRAG_TIMEOUT 0.3f // Drag minimum time for web, measured in seconds
184#define MINIMUM_PINCH 0.005f // Pinch minimum force, measured in normalized screen units (0.0f to 1.0f)
185#define TAP_TIMEOUT 0.3f // Tap minimum time, measured in seconds
186#define PINCH_TIMEOUT 0.3f // Pinch minimum time, measured in seconds
187#define DOUBLETAP_RANGE 0.03f // DoubleTap range, measured in normalized screen units (0.0f to 1.0f)
188
189//----------------------------------------------------------------------------------
190// Types and Structures Definition
191//----------------------------------------------------------------------------------
192
193// Gestures module state context [136 bytes]
194typedef struct {
195 unsigned int current; // Current detected gesture
196 unsigned int enabledFlags; // Enabled gestures flags
197 struct {
198 int firstId; // Touch id for first touch point
199 int pointCount; // Touch points counter
200 double eventTime; // Time stamp when an event happened
201 Vector2 upPosition; // Touch up position
202 Vector2 downPositionA; // First touch down position
203 Vector2 downPositionB; // Second touch down position
204 Vector2 downDragPosition; // Touch drag position
205 Vector2 moveDownPositionA; // First touch down position on move
206 Vector2 moveDownPositionB; // Second touch down position on move
207 Vector2 previousPositionA; // Previous position A to compare for pinch gestures
208 Vector2 previousPositionB; // Previous position B to compare for pinch gestures
209 int tapCounter; // TAP counter (one tap implies TOUCH_ACTION_DOWN and TOUCH_ACTION_UP actions)
210 } Touch;
211 struct {
212 bool resetRequired; // HOLD reset to get first touch point again
213 double timeDuration; // HOLD duration in seconds
214 } Hold;
215 struct {
216 Vector2 vector; // DRAG vector (between initial and current position)
217 float angle; // DRAG angle (relative to x-axis)
218 float distance; // DRAG distance (from initial touch point to final) (normalized [0..1])
219 float intensity; // DRAG intensity, how far why did the DRAG (pixels per frame)
220 } Drag;
221 struct {
222 double startTime; // SWIPE start time to calculate drag intensity
223 } Swipe;
224 struct {
225 Vector2 vector; // PINCH vector (between first and second touch points)
226 float angle; // PINCH angle (relative to x-axis)
227 float distance; // PINCH displacement distance (normalized [0..1])
228 } Pinch;
229} GesturesData;
230
231//----------------------------------------------------------------------------------
232// Global Variables Definition
233//----------------------------------------------------------------------------------
234static GesturesData GESTURES = {
235 .Touch.firstId = -1,
236 .current = GESTURE_NONE, // No current gesture detected
237 .enabledFlags = 0b0000001111111111 // All gestures supported by default
238};
239
240//----------------------------------------------------------------------------------
241// Module specific Functions Declaration
242//----------------------------------------------------------------------------------
243static float rgVector2Angle(Vector2 initialPosition, Vector2 finalPosition);
244static float rgVector2Distance(Vector2 v1, Vector2 v2);
245static double rgGetCurrentTime(void);
246
247//----------------------------------------------------------------------------------
248// Module Functions Definition
249//----------------------------------------------------------------------------------
250
251// Enable only desired gestures to be detected
252void SetGesturesEnabled(unsigned int flags)
253{
254 GESTURES.enabledFlags = flags;
255}
256
257// Check if a gesture have been detected
258bool IsGestureDetected(unsigned int gesture)
259{
260 if ((GESTURES.enabledFlags & GESTURES.current) == gesture) return true;
261 else return false;
262}
263
264// Process gesture event and translate it into gestures
265void ProcessGestureEvent(GestureEvent event)
266{
267 // Reset required variables
268 GESTURES.Touch.pointCount = event.pointCount; // Required on UpdateGestures()
269
270 if (GESTURES.Touch.pointCount == 1) // One touch point
271 {
272 if (event.touchAction == TOUCH_ACTION_DOWN)
273 {
274 GESTURES.Touch.tapCounter++; // Tap counter
275
276 // Detect GESTURE_DOUBLE_TAP
277 if ((GESTURES.current == GESTURE_NONE) && (GESTURES.Touch.tapCounter >= 2) && ((rgGetCurrentTime() - GESTURES.Touch.eventTime) < TAP_TIMEOUT) && (rgVector2Distance(GESTURES.Touch.downPositionA, event.position[0]) < DOUBLETAP_RANGE))
278 {
279 GESTURES.current = GESTURE_DOUBLETAP;
280 GESTURES.Touch.tapCounter = 0;
281 }
282 else // Detect GESTURE_TAP
283 {
284 GESTURES.Touch.tapCounter = 1;
285 GESTURES.current = GESTURE_TAP;
286 }
287
288 GESTURES.Touch.downPositionA = event.position[0];
289 GESTURES.Touch.downDragPosition = event.position[0];
290
291 GESTURES.Touch.upPosition = GESTURES.Touch.downPositionA;
292 GESTURES.Touch.eventTime = rgGetCurrentTime();
293
294 GESTURES.Swipe.startTime = rgGetCurrentTime();
295
296 GESTURES.Drag.vector = (Vector2){ 0.0f, 0.0f };
297 }
298 else if (event.touchAction == TOUCH_ACTION_UP)
299 {
300 // A swipe can happen while the current gesture is drag, but (specially for web) also hold, so set upPosition for both cases
301 if (GESTURES.current == GESTURE_DRAG || GESTURES.current == GESTURE_HOLD) GESTURES.Touch.upPosition = event.position[0];
302
303 // NOTE: GESTURES.Drag.intensity dependent on the resolution of the screen
304 GESTURES.Drag.distance = rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition);
305 GESTURES.Drag.intensity = GESTURES.Drag.distance/(float)((rgGetCurrentTime() - GESTURES.Swipe.startTime));
306
307 // Detect GESTURE_SWIPE
308 if ((GESTURES.Drag.intensity > FORCE_TO_SWIPE) && (GESTURES.current != GESTURE_DRAG))
309 {
310 // NOTE: Angle should be inverted in Y
311 GESTURES.Drag.angle = 360.0f - rgVector2Angle(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition);
312
313 if ((GESTURES.Drag.angle < 30) || (GESTURES.Drag.angle > 330)) GESTURES.current = GESTURE_SWIPE_RIGHT; // Right
314 else if ((GESTURES.Drag.angle >= 30) && (GESTURES.Drag.angle <= 150)) GESTURES.current = GESTURE_SWIPE_UP; // Up
315 else if ((GESTURES.Drag.angle > 150) && (GESTURES.Drag.angle < 210)) GESTURES.current = GESTURE_SWIPE_LEFT; // Left
316 else if ((GESTURES.Drag.angle >= 210) && (GESTURES.Drag.angle <= 330)) GESTURES.current = GESTURE_SWIPE_DOWN; // Down
317 else GESTURES.current = GESTURE_NONE;
318 }
319 else
320 {
321 GESTURES.Drag.distance = 0.0f;
322 GESTURES.Drag.intensity = 0.0f;
323 GESTURES.Drag.angle = 0.0f;
324
325 GESTURES.current = GESTURE_NONE;
326 }
327
328 GESTURES.Touch.downDragPosition = (Vector2){ 0.0f, 0.0f };
329 GESTURES.Touch.pointCount = 0;
330 }
331 else if (event.touchAction == TOUCH_ACTION_MOVE)
332 {
333 GESTURES.Touch.moveDownPositionA = event.position[0];
334
335 if (GESTURES.current == GESTURE_HOLD)
336 {
337 if (GESTURES.Hold.resetRequired) GESTURES.Touch.downPositionA = event.position[0];
338
339 GESTURES.Hold.resetRequired = false;
340
341 // Detect GESTURE_DRAG
342 if ((rgGetCurrentTime() - GESTURES.Touch.eventTime) > DRAG_TIMEOUT)
343 {
344 GESTURES.Touch.eventTime = rgGetCurrentTime();
345 GESTURES.current = GESTURE_DRAG;
346 }
347 }
348
349 GESTURES.Drag.vector.x = GESTURES.Touch.moveDownPositionA.x - GESTURES.Touch.downDragPosition.x;
350 GESTURES.Drag.vector.y = GESTURES.Touch.moveDownPositionA.y - GESTURES.Touch.downDragPosition.y;
351 }
352 }
353 else if (GESTURES.Touch.pointCount == 2) // Two touch points
354 {
355 if (event.touchAction == TOUCH_ACTION_DOWN)
356 {
357 GESTURES.Touch.downPositionA = event.position[0];
358 GESTURES.Touch.downPositionB = event.position[1];
359
360 GESTURES.Touch.previousPositionA = GESTURES.Touch.downPositionA;
361 GESTURES.Touch.previousPositionB = GESTURES.Touch.downPositionB;
362
363 //GESTURES.Pinch.distance = rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.downPositionB);
364
365 GESTURES.Pinch.vector.x = GESTURES.Touch.downPositionB.x - GESTURES.Touch.downPositionA.x;
366 GESTURES.Pinch.vector.y = GESTURES.Touch.downPositionB.y - GESTURES.Touch.downPositionA.y;
367
368 GESTURES.current = GESTURE_HOLD;
369 GESTURES.Hold.timeDuration = rgGetCurrentTime();
370 }
371 else if (event.touchAction == TOUCH_ACTION_MOVE)
372 {
373 GESTURES.Pinch.distance = rgVector2Distance(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB);
374
375 GESTURES.Touch.moveDownPositionA = event.position[0];
376 GESTURES.Touch.moveDownPositionB = event.position[1];
377
378 GESTURES.Pinch.vector.x = GESTURES.Touch.moveDownPositionB.x - GESTURES.Touch.moveDownPositionA.x;
379 GESTURES.Pinch.vector.y = GESTURES.Touch.moveDownPositionB.y - GESTURES.Touch.moveDownPositionA.y;
380
381 if ((rgVector2Distance(GESTURES.Touch.previousPositionA, GESTURES.Touch.moveDownPositionA) >= MINIMUM_PINCH) || (rgVector2Distance(GESTURES.Touch.previousPositionB, GESTURES.Touch.moveDownPositionB) >= MINIMUM_PINCH))
382 {
383 if ( rgVector2Distance(GESTURES.Touch.previousPositionA, GESTURES.Touch.previousPositionB) > rgVector2Distance(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB) ) GESTURES.current = GESTURE_PINCH_IN;
384 else GESTURES.current = GESTURE_PINCH_OUT;
385 }
386 else
387 {
388 GESTURES.current = GESTURE_HOLD;
389 GESTURES.Hold.timeDuration = rgGetCurrentTime();
390 }
391
392 // NOTE: Angle should be inverted in Y
393 GESTURES.Pinch.angle = 360.0f - rgVector2Angle(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB);
394 }
395 else if (event.touchAction == TOUCH_ACTION_UP)
396 {
397 GESTURES.Pinch.distance = 0.0f;
398 GESTURES.Pinch.angle = 0.0f;
399 GESTURES.Pinch.vector = (Vector2){ 0.0f, 0.0f };
400 GESTURES.Touch.pointCount = 0;
401
402 GESTURES.current = GESTURE_NONE;
403 }
404 }
405 else if (GESTURES.Touch.pointCount > 2) // More than two touch points
406 {
407 // TODO: Process gesture events for more than two points
408 }
409}
410
411// Update gestures detected (must be called every frame)
412void UpdateGestures(void)
413{
414 // NOTE: Gestures are processed through system callbacks on touch events
415
416 // Detect GESTURE_HOLD
417 if (((GESTURES.current == GESTURE_TAP) || (GESTURES.current == GESTURE_DOUBLETAP)) && (GESTURES.Touch.pointCount < 2))
418 {
419 GESTURES.current = GESTURE_HOLD;
420 GESTURES.Hold.timeDuration = rgGetCurrentTime();
421 }
422
423 // Detect GESTURE_NONE
424 if ((GESTURES.current == GESTURE_SWIPE_RIGHT) || (GESTURES.current == GESTURE_SWIPE_UP) || (GESTURES.current == GESTURE_SWIPE_LEFT) || (GESTURES.current == GESTURE_SWIPE_DOWN))
425 {
426 GESTURES.current = GESTURE_NONE;
427 }
428}
429
430// Get latest detected gesture
431int GetGestureDetected(void)
432{
433 // Get current gesture only if enabled
434 return (GESTURES.enabledFlags & GESTURES.current);
435}
436
437// Hold time measured in seconds
438float GetGestureHoldDuration(void)
439{
440 // NOTE: time is calculated on current gesture HOLD
441
442 double time = 0.0;
443
444 if (GESTURES.current == GESTURE_HOLD) time = rgGetCurrentTime() - GESTURES.Hold.timeDuration;
445
446 return (float)time;
447}
448
449// Get drag vector (between initial touch point to current)
450Vector2 GetGestureDragVector(void)
451{
452 // NOTE: drag vector is calculated on one touch points TOUCH_ACTION_MOVE
453
454 return GESTURES.Drag.vector;
455}
456
457// Get drag angle
458// NOTE: Angle in degrees, horizontal-right is 0, counterclockwise
459float GetGestureDragAngle(void)
460{
461 // NOTE: drag angle is calculated on one touch points TOUCH_ACTION_UP
462
463 return GESTURES.Drag.angle;
464}
465
466// Get distance between two pinch points
467Vector2 GetGesturePinchVector(void)
468{
469 // NOTE: Pinch distance is calculated on two touch points TOUCH_ACTION_MOVE
470
471 return GESTURES.Pinch.vector;
472}
473
474// Get angle between two pinch points
475// NOTE: Angle in degrees, horizontal-right is 0, counterclockwise
476float GetGesturePinchAngle(void)
477{
478 // NOTE: pinch angle is calculated on two touch points TOUCH_ACTION_MOVE
479
480 return GESTURES.Pinch.angle;
481}
482
483//----------------------------------------------------------------------------------
484// Module specific Functions Definition
485//----------------------------------------------------------------------------------
486// Get angle from two-points vector with X-axis
487static float rgVector2Angle(Vector2 v1, Vector2 v2)
488{
489 float angle = atan2f(v2.y - v1.y, v2.x - v1.x)*(180.0f/PI);
490
491 if (angle < 0) angle += 360.0f;
492
493 return angle;
494}
495
496// Calculate distance between two Vector2
497static float rgVector2Distance(Vector2 v1, Vector2 v2)
498{
499 float result;
500
501 float dx = v2.x - v1.x;
502 float dy = v2.y - v1.y;
503
504 result = (float)sqrt(dx*dx + dy*dy);
505
506 return result;
507}
508
509// Time measure returned are seconds
510static double rgGetCurrentTime(void)
511{
512 double time = 0;
513
514#if !defined(RGESTURES_STANDALONE)
515 time = GetTime();
516#else
517#if defined(_WIN32)
518 unsigned long long int clockFrequency, currentTime;
519
520 QueryPerformanceFrequency(&clockFrequency); // BE CAREFUL: Costly operation!
521 QueryPerformanceCounter(&currentTime);
522
523 time = (double)currentTime/clockFrequency; // Time in seconds
524#endif
525
526#if defined(__linux__)
527 // NOTE: Only for Linux-based systems
528 struct timespec now;
529 clock_gettime(CLOCK_MONOTONIC, &now);
530 unsigned long long int nowTime = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec; // Time in nanoseconds
531
532 time = ((double)nowTime*1e-9); // Time in seconds
533#endif
534
535#if defined(__APPLE__)
536 //#define CLOCK_REALTIME CALENDAR_CLOCK // returns UTC time since 1970-01-01
537 //#define CLOCK_MONOTONIC SYSTEM_CLOCK // returns the time since boot time
538
539 clock_serv_t cclock;
540 mach_timespec_t now;
541 host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
542
543 // NOTE: OS X does not have clock_gettime(), using clock_get_time()
544 clock_get_time(cclock, &now);
545 mach_port_deallocate(mach_task_self(), cclock);
546 unsigned long long int nowTime = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec; // Time in nanoseconds
547
548 time = ((double)nowTime*1e-9); // Time in seconds
549#endif
550#endif
551
552 return time;
553}
554
555#endif // RGESTURES_IMPLEMENTATION

Generated with cgit - Back to sebastiano.tronto.net