File size: 5,983 Bytes
dec7e7d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */

/*
 * Copyright 2017-2020 Mark Callow.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @internal
 * @class SwipeDetector
 * @~English
 *
 * @brief Definition of a class for detecting swipes.
 *
 * @author Mark Callow, github.com/MarkCallow.
 */

#include <assert.h>
#include "SwipeDetector.h"
#include <SDL3/SDL_log.h>

#if !defined(SWIPEDETECTOR_LOG_GESTURE_EVENTS)
  #define SWIPEDETECTOR_LOG_GESTURE_EVENTS 0
#endif
#if !defined(SWIPEDETECTOR_LOG_GESTURE_DETECTION)
  #define SWIPEDETECTOR_LOG_GESTURE_DETECTION 0
#endif

bool
SwipeDetector::doEvent(SDL_Event* event)
{
    bool result = false;

    switch (event->type) {
      case SDL_EVENT_FINGER_UP: {
        int numFingers;
        SDL_Finger** fingers = SDL_GetTouchFingers(event->tfinger.touchID, &numFingers);
        if (SWIPEDETECTOR_LOG_GESTURE_EVENTS) {
            SDL_Log("SD: Finger: %" SDL_PRIx64 " UP - fingers: %i, x: %f, y: %f",
                    event->tfinger.fingerID, numFingers, event->tfinger.x, event->tfinger.y);
        }
        // SDL_GetTouchFingers appears to return the number of fingers
        // down *before* the event was generated, so 1 means the last finger
        // just lifted.
        if (numFingers == 1 && gestureStart.time != 0) {
            gestureStart.time = 0;
            gestureSwipe = false;
            if (SWIPEDETECTOR_LOG_GESTURE_DETECTION) {
                SDL_Log("***************** SD: FINGER_UP, %smultigesture done *****************",
                        gestureSwipe ? "Swipe complete & " : "");
            }
        } else {
            result = true;
        }
        SDL_free(fingers);
        break;
      }
      case GESTURE_MULTIGESTURE: {
        Gesture_MultiGestureEvent& mgesture = *(Gesture_MultiGestureEvent *)event;
        if (SWIPEDETECTOR_LOG_GESTURE_EVENTS) {
            SDL_Log("SD: MG Event: x = %f, y = %f, dAng = %f (%f), dR = %f, numFingers = %i, time = %" SDL_PRIu64,
               mgesture.x,
               mgesture.y,
               mgesture.dTheta * 180.0 / M_PI,
               mgesture.dTheta,
               mgesture.dDist,
               mgesture.numFingers,
               mgesture.timestamp);
        }
        if (SWIPEDETECTOR_LOG_GESTURE_DETECTION) {
            SDL_Log("SD: mgestureSwipe = %i, time = %" SDL_PRIu64,
                     gestureSwipe,
                     (mgesture.timestamp - gestureStart.time) / 1000000);
        }
        if (gestureStart.time == 0) {
            if (SWIPEDETECTOR_LOG_GESTURE_DETECTION) {
                SDL_Log("************ SD: Multigesture detection start **************");
            }
            gestureStart.time = mgesture.timestamp;
            gestureStart.point.x = mgesture.x;
            gestureStart.point.y = mgesture.y;
            lastVector.reset();
            gestureSwipe = false;
        } else {
            if (!gestureSwipe) {
                vector sv; // Vector from start point to current position
                float velocity;
                float theta; // Angle between current vector and previous vector.
                float duration;
                sv.w = mgesture.x - gestureStart.point.x;
                sv.h = mgesture.y - gestureStart.point.y;
                float distance = sv.length();
                if (lastVector.has_value()) {
                    // SDL2 timestamps were in milliseconds, SDL3 are nanoseconds. Given the
                    // normalized distances reported, using nanoseconds leads to 0 velocitySq.
                    duration = static_cast<float>(
                        (mgesture.timestamp - gestureStart.time) / 1000000.0);

                    velocity = distance / duration;
                    assert(!std::isinf(velocity));
                    theta = static_cast<float>(lastVector->getAngle(sv));
                    if (SWIPEDETECTOR_LOG_GESTURE_DETECTION) {
                        SDL_Log("SD: Detection: distance = %f, velocity = %f, theta = %f, sv angle = %f, sv angle normalized = %f, lastv angle = %f",
                                distance, velocity, theta,
                                sv.getAngle(),
                                sv.getAngleNormalized(),
                                lastVector->getAngle());
                    }
                    lastVector = sv;
                    // Multiple events with the same timestamp is a possibility
                    // hence the isinf() check.
                    if (std::abs(theta) < 3.0 && std::abs(mgesture.dDist) > 0.01 && !std::isinf(velocity) && velocity > 0.0007) {
                        if (SWIPEDETECTOR_LOG_GESTURE_DETECTION)
                            SDL_Log("----------------- SD: Swipe %s detected -----------------",
                                    toString(sv.getDirection()).c_str());
                        gestureSwipe = true;
                        if (SDL_EventEnabled(SDL_EVENT_USER)) {
                            SDL_Event user_event;
                            // SDL will copy this entire struct! Initialize to keep memory
                            // checkers happy.
                            SDL_zero(user_event);
                            user_event.type = SDL_EVENT_USER;
                            user_event.user.code = swipeGesture;
                            user_event.user.data1 = SwipeDetector::directionToPointer(sv.getDirection());
                            user_event.user.data2 = NULL;
                            SDL_PushEvent(&user_event);
                        }
                    } else {
                        if (SWIPEDETECTOR_LOG_GESTURE_DETECTION) SDL_Log("SD: No swipe detected.");
                        result = true;
                    }
                } else {
                     lastVector = sv;
                     result = true;
                }
            }
        }
        break;
      }
      default:
          result = true;
    }

    return result;
}