Wise&mystical  1.0
Project about Europe
Loading...
Searching...
No Matches
rgestures.h
Go to the documentation of this file.
1
46#ifndef RGESTURES_H
47#define RGESTURES_H
48
49#ifndef PI
50 #define PI 3.14159265358979323846
51#endif
52
53//----------------------------------------------------------------------------------
54// Defines and Macros
55//----------------------------------------------------------------------------------
56#ifndef MAX_TOUCH_POINTS
57 #define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported
58#endif
59
60//----------------------------------------------------------------------------------
61// Types and Structures Definition
62// NOTE: Below types are required for GESTURES_STANDALONE usage
63//----------------------------------------------------------------------------------
64// Boolean type
65#if defined(__STDC__) && __STDC_VERSION__ >= 199901L
66 #include <stdbool.h>
67#elif !defined(__cplusplus) && !defined(bool) && !defined(RL_BOOL_TYPE)
68 typedef enum bool { false, true } bool;
69#endif
70
71#if !defined(RL_VECTOR2_TYPE)
72// Vector2 type
73typedef struct Vector2 {
74 float x;
75 float y;
77#endif
78
79#if defined(GESTURES_STANDALONE)
80// Gestures type
81// NOTE: It could be used as flags to enable only some gestures
82typedef enum {
83 GESTURE_NONE = 0,
84 GESTURE_TAP = 1,
86 GESTURE_HOLD = 4,
87 GESTURE_DRAG = 8,
92 GESTURE_PINCH_IN = 256,
94} Gesture;
95#endif
96
97typedef enum {
103
104// Gesture event
105typedef struct {
108 int pointId[MAX_TOUCH_POINTS];
111
112//----------------------------------------------------------------------------------
113// Global Variables Definition
114//----------------------------------------------------------------------------------
115//...
116
117//----------------------------------------------------------------------------------
118// Module Functions Declaration
119//----------------------------------------------------------------------------------
120
121#ifdef __cplusplus
122extern "C" { // Prevents name mangling of functions
123#endif
124
125void ProcessGestureEvent(GestureEvent event); // Process gesture event and translate it into gestures
126void UpdateGestures(void); // Update gestures detected (must be called every frame)
127
128#if defined(GESTURES_STANDALONE)
129void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags
130bool IsGestureDetected(int gesture); // Check if a gesture have been detected
131int GetGestureDetected(void); // Get latest detected gesture
132
133float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds
134Vector2 GetGestureDragVector(void); // Get gesture drag vector
135float GetGestureDragAngle(void); // Get gesture drag angle
136Vector2 GetGesturePinchVector(void); // Get gesture pinch delta
137float GetGesturePinchAngle(void); // Get gesture pinch angle
138#endif
139
140#ifdef __cplusplus
141}
142#endif
143
144#endif // GESTURES_H
145
146
152#if defined(GESTURES_IMPLEMENTATION)
153
154#if defined(_WIN32)
155 #if defined(__cplusplus)
156 extern "C" { // Prevents name mangling of functions
157 #endif
158 // Functions required to query time on Windows
159 int __stdcall QueryPerformanceCounter(unsigned long long int *lpPerformanceCount);
160 int __stdcall QueryPerformanceFrequency(unsigned long long int *lpFrequency);
161 #if defined(__cplusplus)
162 }
163 #endif
164#elif defined(__linux__)
165 #if _POSIX_C_SOURCE < 199309L
166 #undef _POSIX_C_SOURCE
167 #define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext.
168 #endif
169 #include <sys/time.h> // Required for: timespec
170 #include <time.h> // Required for: clock_gettime()
171
172 #include <math.h> // Required for: sqrtf(), atan2f()
173#endif
174#if defined(__APPLE__) // macOS also defines __MACH__
175 #include <mach/clock.h> // Required for: clock_get_time()
176 #include <mach/mach.h> // Required for: mach_timespec_t
177#endif
178
179//----------------------------------------------------------------------------------
180// Defines and Macros
181//----------------------------------------------------------------------------------
182#define FORCE_TO_SWIPE 0.0005f // Swipe force, measured in normalized screen units/time
183#define MINIMUM_DRAG 0.015f // Drag minimum force, measured in normalized screen units (0.0f to 1.0f)
184#define MINIMUM_PINCH 0.005f // Pinch minimum force, measured in normalized screen units (0.0f to 1.0f)
185#define TAP_TIMEOUT 300 // Tap minimum time, measured in milliseconds
186#define PINCH_TIMEOUT 300 // Pinch minimum time, measured in milliseconds
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 int tapCounter; // TAP counter (one tap implies TOUCH_ACTION_DOWN and TOUCH_ACTION_UP actions)
208 } Touch;
209 struct {
210 bool resetRequired; // HOLD reset to get first touch point again
211 double timeDuration; // HOLD duration in milliseconds
212 } Hold;
213 struct {
214 Vector2 vector; // DRAG vector (between initial and current position)
215 float angle; // DRAG angle (relative to x-axis)
216 float distance; // DRAG distance (from initial touch point to final) (normalized [0..1])
217 float intensity; // DRAG intensity, how far why did the DRAG (pixels per frame)
218 } Drag;
219 struct {
220 bool start; // SWIPE used to define when start measuring GESTURES.Swipe.timeDuration
221 double timeDuration; // SWIPE time to calculate drag intensity
222 } Swipe;
223 struct {
224 Vector2 vector; // PINCH vector (between first and second touch points)
225 float angle; // PINCH angle (relative to x-axis)
226 float distance; // PINCH displacement distance (normalized [0..1])
227 } Pinch;
228} GesturesData;
229
230//----------------------------------------------------------------------------------
231// Global Variables Definition
232//----------------------------------------------------------------------------------
233static GesturesData GESTURES = {
234 .Touch.firstId = -1,
235 .current = GESTURE_NONE, // No current gesture detected
236 .enabledFlags = 0b0000001111111111 // All gestures supported by default
237};
238
239//----------------------------------------------------------------------------------
240// Module specific Functions Declaration
241//----------------------------------------------------------------------------------
242static float rgVector2Angle(Vector2 initialPosition, Vector2 finalPosition);
243static float rgVector2Distance(Vector2 v1, Vector2 v2);
244static double rgGetCurrentTime(void);
245
246//----------------------------------------------------------------------------------
247// Module Functions Definition
248//----------------------------------------------------------------------------------
249
250// Enable only desired getures to be detected
251void SetGesturesEnabled(unsigned int flags)
252{
253 GESTURES.enabledFlags = flags;
254}
255
256// Check if a gesture have been detected
257bool IsGestureDetected(int gesture)
258{
259 if ((GESTURES.enabledFlags & GESTURES.current) == gesture) return true;
260 else return false;
261}
262
263// Process gesture event and translate it into gestures
265{
266 // Reset required variables
267 GESTURES.Touch.pointCount = event.pointCount; // Required on UpdateGestures()
268
269 if (GESTURES.Touch.pointCount == 1) // One touch point
270 {
271 if (event.touchAction == TOUCH_ACTION_DOWN)
272 {
273 GESTURES.Touch.tapCounter++; // Tap counter
274
275 // Detect GESTURE_DOUBLE_TAP
276 if ((GESTURES.current == GESTURE_NONE) && (GESTURES.Touch.tapCounter >= 2) && ((rgGetCurrentTime() - GESTURES.Touch.eventTime) < TAP_TIMEOUT) && (rgVector2Distance(GESTURES.Touch.downPositionA, event.position[0]) < DOUBLETAP_RANGE))
277 {
278 GESTURES.current = GESTURE_DOUBLETAP;
279 GESTURES.Touch.tapCounter = 0;
280 }
281 else // Detect GESTURE_TAP
282 {
283 GESTURES.Touch.tapCounter = 1;
284 GESTURES.current = GESTURE_TAP;
285 }
286
287 GESTURES.Touch.downPositionA = event.position[0];
288 GESTURES.Touch.downDragPosition = event.position[0];
289
290 GESTURES.Touch.upPosition = GESTURES.Touch.downPositionA;
291 GESTURES.Touch.eventTime = rgGetCurrentTime();
292
293 GESTURES.Touch.firstId = event.pointId[0];
294
295 GESTURES.Drag.vector = (Vector2){ 0.0f, 0.0f };
296 }
297 else if (event.touchAction == TOUCH_ACTION_UP)
298 {
299 if (GESTURES.current == GESTURE_DRAG) GESTURES.Touch.upPosition = event.position[0];
300
301 // NOTE: GESTURES.Drag.intensity dependend on the resolution of the screen
302 GESTURES.Drag.distance = rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition);
303 GESTURES.Drag.intensity = GESTURES.Drag.distance/(float)((rgGetCurrentTime() - GESTURES.Swipe.timeDuration));
304
305 GESTURES.Swipe.start = false;
306
307 // Detect GESTURE_SWIPE
308 if ((GESTURES.Drag.intensity > FORCE_TO_SWIPE) && (GESTURES.Touch.firstId == event.pointId[0]))
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 < 120)) GESTURES.current = GESTURE_SWIPE_UP; // Up
315 else if ((GESTURES.Drag.angle > 120) && (GESTURES.Drag.angle < 210)) GESTURES.current = GESTURE_SWIPE_LEFT; // Left
316 else if ((GESTURES.Drag.angle > 210) && (GESTURES.Drag.angle < 300)) 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 if (GESTURES.current == GESTURE_DRAG) GESTURES.Touch.eventTime = rgGetCurrentTime();
334
335 if (!GESTURES.Swipe.start)
336 {
337 GESTURES.Swipe.timeDuration = rgGetCurrentTime();
338 GESTURES.Swipe.start = true;
339 }
340
341 GESTURES.Touch.moveDownPositionA = event.position[0];
342
343 if (GESTURES.current == GESTURE_HOLD)
344 {
345 if (GESTURES.Hold.resetRequired) GESTURES.Touch.downPositionA = event.position[0];
346
347 GESTURES.Hold.resetRequired = false;
348
349 // Detect GESTURE_DRAG
350 if (rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.moveDownPositionA) >= MINIMUM_DRAG)
351 {
352 GESTURES.Touch.eventTime = rgGetCurrentTime();
353 GESTURES.current = GESTURE_DRAG;
354 }
355 }
356
357 GESTURES.Drag.vector.x = GESTURES.Touch.moveDownPositionA.x - GESTURES.Touch.downDragPosition.x;
358 GESTURES.Drag.vector.y = GESTURES.Touch.moveDownPositionA.y - GESTURES.Touch.downDragPosition.y;
359 }
360 }
361 else if (GESTURES.Touch.pointCount == 2) // Two touch points
362 {
363 if (event.touchAction == TOUCH_ACTION_DOWN)
364 {
365 GESTURES.Touch.downPositionA = event.position[0];
366 GESTURES.Touch.downPositionB = event.position[1];
367
368 //GESTURES.Pinch.distance = rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.downPositionB);
369
370 GESTURES.Pinch.vector.x = GESTURES.Touch.downPositionB.x - GESTURES.Touch.downPositionA.x;
371 GESTURES.Pinch.vector.y = GESTURES.Touch.downPositionB.y - GESTURES.Touch.downPositionA.y;
372
373 GESTURES.current = GESTURE_HOLD;
374 GESTURES.Hold.timeDuration = rgGetCurrentTime();
375 }
376 else if (event.touchAction == TOUCH_ACTION_MOVE)
377 {
378 GESTURES.Pinch.distance = rgVector2Distance(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB);
379
380 GESTURES.Touch.downPositionA = GESTURES.Touch.moveDownPositionA;
381 GESTURES.Touch.downPositionB = GESTURES.Touch.moveDownPositionB;
382
383 GESTURES.Touch.moveDownPositionA = event.position[0];
384 GESTURES.Touch.moveDownPositionB = event.position[1];
385
386 GESTURES.Pinch.vector.x = GESTURES.Touch.moveDownPositionB.x - GESTURES.Touch.moveDownPositionA.x;
387 GESTURES.Pinch.vector.y = GESTURES.Touch.moveDownPositionB.y - GESTURES.Touch.moveDownPositionA.y;
388
389 if ((rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.moveDownPositionA) >= MINIMUM_PINCH) || (rgVector2Distance(GESTURES.Touch.downPositionB, GESTURES.Touch.moveDownPositionB) >= MINIMUM_PINCH))
390 {
391 if ((rgVector2Distance(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB) - GESTURES.Pinch.distance) < 0) GESTURES.current = GESTURE_PINCH_IN;
392 else GESTURES.current = GESTURE_PINCH_OUT;
393 }
394 else
395 {
396 GESTURES.current = GESTURE_HOLD;
397 GESTURES.Hold.timeDuration = rgGetCurrentTime();
398 }
399
400 // NOTE: Angle should be inverted in Y
401 GESTURES.Pinch.angle = 360.0f - rgVector2Angle(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB);
402 }
403 else if (event.touchAction == TOUCH_ACTION_UP)
404 {
405 GESTURES.Pinch.distance = 0.0f;
406 GESTURES.Pinch.angle = 0.0f;
407 GESTURES.Pinch.vector = (Vector2){ 0.0f, 0.0f };
408 GESTURES.Touch.pointCount = 0;
409
410 GESTURES.current = GESTURE_NONE;
411 }
412 }
413 else if (GESTURES.Touch.pointCount > 2) // More than two touch points
414 {
415 // TODO: Process gesture events for more than two points
416 }
417}
418
419// Update gestures detected (must be called every frame)
420void UpdateGestures(void)
421{
422 // NOTE: Gestures are processed through system callbacks on touch events
423
424 // Detect GESTURE_HOLD
425 if (((GESTURES.current == GESTURE_TAP) || (GESTURES.current == GESTURE_DOUBLETAP)) && (GESTURES.Touch.pointCount < 2))
426 {
427 GESTURES.current = GESTURE_HOLD;
428 GESTURES.Hold.timeDuration = rgGetCurrentTime();
429 }
430
431 if (((rgGetCurrentTime() - GESTURES.Touch.eventTime) > TAP_TIMEOUT) && (GESTURES.current == GESTURE_DRAG) && (GESTURES.Touch.pointCount < 2))
432 {
433 GESTURES.current = GESTURE_HOLD;
434 GESTURES.Hold.timeDuration = rgGetCurrentTime();
435 GESTURES.Hold.resetRequired = true;
436 }
437
438 // Detect GESTURE_NONE
439 if ((GESTURES.current == GESTURE_SWIPE_RIGHT) || (GESTURES.current == GESTURE_SWIPE_UP) || (GESTURES.current == GESTURE_SWIPE_LEFT) || (GESTURES.current == GESTURE_SWIPE_DOWN))
440 {
441 GESTURES.current = GESTURE_NONE;
442 }
443}
444
445// Get latest detected gesture
446int GetGestureDetected(void)
447{
448 // Get current gesture only if enabled
449 return (GESTURES.enabledFlags & GESTURES.current);
450}
451
452// Hold time measured in ms
453float GetGestureHoldDuration(void)
454{
455 // NOTE: time is calculated on current gesture HOLD
456
457 double time = 0.0;
458
459 if (GESTURES.current == GESTURE_HOLD) time = rgGetCurrentTime() - GESTURES.Hold.timeDuration;
460
461 return (float)time;
462}
463
464// Get drag vector (between initial touch point to current)
466{
467 // NOTE: drag vector is calculated on one touch points TOUCH_ACTION_MOVE
468
469 return GESTURES.Drag.vector;
470}
471
472// Get drag angle
473// NOTE: Angle in degrees, horizontal-right is 0, counterclock-wise
474float GetGestureDragAngle(void)
475{
476 // NOTE: drag angle is calculated on one touch points TOUCH_ACTION_UP
477
478 return GESTURES.Drag.angle;
479}
480
481// Get distance between two pinch points
483{
484 // NOTE: Pinch distance is calculated on two touch points TOUCH_ACTION_MOVE
485
486 return GESTURES.Pinch.vector;
487}
488
489// Get angle beween two pinch points
490// NOTE: Angle in degrees, horizontal-right is 0, counterclock-wise
491float GetGesturePinchAngle(void)
492{
493 // NOTE: pinch angle is calculated on two touch points TOUCH_ACTION_MOVE
494
495 return GESTURES.Pinch.angle;
496}
497
498//----------------------------------------------------------------------------------
499// Module specific Functions Definition
500//----------------------------------------------------------------------------------
501// Get angle from two-points vector with X-axis
502static float rgVector2Angle(Vector2 v1, Vector2 v2)
503{
504 float angle = atan2f(v2.y - v1.y, v2.x - v1.x)*(180.0f/PI);
505
506 if (angle < 0) angle += 360.0f;
507
508 return angle;
509}
510
511// Calculate distance between two Vector2
512static float rgVector2Distance(Vector2 v1, Vector2 v2)
513{
514 float result;
515
516 float dx = v2.x - v1.x;
517 float dy = v2.y - v1.y;
518
519 result = (float)sqrt(dx*dx + dy*dy);
520
521 return result;
522}
523
524// Time measure returned are milliseconds
525static double rgGetCurrentTime(void)
526{
527 double time = 0;
528
529#if defined(_WIN32)
530 unsigned long long int clockFrequency, currentTime;
531
532 QueryPerformanceFrequency(&clockFrequency); // BE CAREFUL: Costly operation!
533 QueryPerformanceCounter(&currentTime);
534
535 time = (double)currentTime/clockFrequency*1000.0f; // Time in miliseconds
536#endif
537
538#if defined(__linux__)
539 // NOTE: Only for Linux-based systems
540 struct timespec now;
541 clock_gettime(CLOCK_MONOTONIC, &now);
542 unsigned long long int nowTime = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec; // Time in nanoseconds
543
544 time = ((double)nowTime/1000000.0); // Time in miliseconds
545#endif
546
547#if defined(__APPLE__)
548 //#define CLOCK_REALTIME CALENDAR_CLOCK // returns UTC time since 1970-01-01
549 //#define CLOCK_MONOTONIC SYSTEM_CLOCK // returns the time since boot time
550
551 clock_serv_t cclock;
552 mach_timespec_t now;
553 host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
554
555 // NOTE: OS X does not have clock_gettime(), using clock_get_time()
556 clock_get_time(cclock, &now);
557 mach_port_deallocate(mach_task_self(), cclock);
558 unsigned long long int nowTime = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec; // Time in nanoseconds
559
560 time = ((double)nowTime/1000000.0); // Time in miliseconds
561#endif
562
563 return time;
564}
565
566#endif // GESTURES_IMPLEMENTATION
RLAPI int GetGestureDetected(void)
Gesture
Definition: raylib.h:852
@ GESTURE_DRAG
Definition: raylib.h:857
@ GESTURE_SWIPE_RIGHT
Definition: raylib.h:858
@ GESTURE_HOLD
Definition: raylib.h:856
@ GESTURE_PINCH_IN
Definition: raylib.h:862
@ GESTURE_SWIPE_UP
Definition: raylib.h:860
@ GESTURE_SWIPE_LEFT
Definition: raylib.h:859
@ GESTURE_DOUBLETAP
Definition: raylib.h:855
@ GESTURE_TAP
Definition: raylib.h:854
@ GESTURE_NONE
Definition: raylib.h:853
@ GESTURE_SWIPE_DOWN
Definition: raylib.h:861
@ GESTURE_PINCH_OUT
Definition: raylib.h:863
RLAPI float GetGestureHoldDuration(void)
RLAPI float GetGesturePinchAngle(void)
RLAPI Vector2 GetGesturePinchVector(void)
RLAPI Vector2 GetGestureDragVector(void)
RLAPI float GetGestureDragAngle(void)
RLAPI void SetGesturesEnabled(unsigned int flags)
RLAPI bool IsGestureDetected(int gesture)
#define PI
rgestures - Gestures system, gestures processing based on input events (touch/mouse)
Definition: rgestures.h:50
void ProcessGestureEvent(GestureEvent event)
void UpdateGestures(void)
#define MAX_TOUCH_POINTS
Definition: rgestures.h:57
TouchAction
Definition: rgestures.h:97
@ TOUCH_ACTION_CANCEL
Definition: rgestures.h:101
@ TOUCH_ACTION_DOWN
Definition: rgestures.h:99
@ TOUCH_ACTION_MOVE
Definition: rgestures.h:100
@ TOUCH_ACTION_UP
Definition: rgestures.h:98
bool
Definition: rgestures.h:68
int touchAction
Definition: rgestures.h:106
Vector2 position[MAX_TOUCH_POINTS]
Definition: rgestures.h:109
int pointCount
Definition: rgestures.h:107
float x
Definition: physac.h:130
float y
Definition: physac.h:131