From 02a32167df0140a090bd78abcb670587c1cad80a Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Fri, 16 Dec 2022 21:46:54 -0500
Subject: [PATCH] Initial add.
---
LICENSE.txt | 18 ++
README.md | 44 +++
SDL_gesture.h | 854 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 916 insertions(+)
create mode 100644 LICENSE.txt
create mode 100644 README.md
create mode 100644 SDL_gesture.h
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..728a3d7
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,18 @@
+Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0500ece
--- /dev/null
+++ b/README.md
@@ -0,0 +1,44 @@
+## SDL_gesture
+
+SDL3 is removes the Gesture API that was available in SDL2, so as a migration
+path, we are providing an equivalent as a single-header library that you
+can drop into your SDL3-based project.
+
+We do not make formal releases of this code; just grab the latest and drop
+it into your project!
+
+To use:
+
+- Copy SDL_gesture.h into your project.
+- Wherever you need access to this functionality, `#include` the header,
+ _after_ including SDL.h.
+- In **ONLY ONE PLACE** in your project, make sure you've `#defined`
+ SDL_GESTURE_IMPLEMENTATION before including the header:
+
+
+ ```c
+ #define SDL_GESTURE_IMPLEMENTATION 1
+ #include "SDL_gesture.h"
+ ```
+
+ This will make the header include not just function declarations and such
+ but also its implementation code.
+- To use the API, call this somewhere near startup, after SDL_Init:
+
+ ```c
+ Gesture_Init();
+ ```
+
+ and then, before you call SDL_Quit at the end of your program:
+
+ ```c
+ Gesture_Quit();
+ ```
+
+ Now you will get Gesture events from the SDL event queue.
+- SDL_RecordGesture, SDL_SaveAllDollarTemplates, SDL_SaveDollarTemplate, and
+ SDL_LoadDollarTemplates work as before, they are just prefixed with
+ "Gesture_" instead of "SDL_". Same with event types.
+- It is safe to add this to SDL2-based projects, as it will just use SDL2's
+ existing API under the hood.
+
diff --git a/SDL_gesture.h b/SDL_gesture.h
new file mode 100644
index 0000000..0c6b5b8
--- /dev/null
+++ b/SDL_gesture.h
@@ -0,0 +1,854 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+
+/* Touch gestures were removed from SDL3, so this is the SDL2 implementation copied in here, and tweaked a little. */
+
+#ifndef INCL_SDL_GESTURE_H
+#define INCL_SDL_GESTURE_H
+
+#if !defined(SDL_VERSION_MAJOR)
+#error Please include SDL.h before including this header.
+#elif SDL_VERSION_MAJOR < 2
+#error This header requires SDL2 or later.
+#elif SDL_VERSION_MAJOR == 2
+/* building against SDL2? Just use the built-in SDL2 implementation. */
+#define Gesture_Init() (0)
+#define Gesture_ID SDL_GestureID
+#define Gesture_RecordGesture SDL_RecordGesture
+#define Gesture_SaveAllDollarTemplates SDL_SaveAllDollarTemplates
+#define Gesture_SaveDollarTemplate SDL_SaveDollarTemplate
+#define GESTURE_DOLLARGESTURE SDL_DOLLARGESTURE
+#define GESTURE_DOLLARRECORD SDL_DOLLARRECORD
+#define GESTURE_MULTIGESTURE SDL_MULTIGESTURE
+#define Gesture_MultiGestureEvent SDL_MultiGestureEvent
+#define Gesture_DollarGestureEvent SDL_MultiGestureEvent
+#else
+
+/* Set up for C function definitions, even when using C++ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef Sint64 Gesture_ID;
+
+/* events... */
+
+/* generally you shouldn't hardcode event type numbers--and doubly so in
+ the reserved range!--but these match SDL2 and SDL3 promises to preserve
+ these values to help sdl2-compat. */
+#define GESTURE_DOLLARGESTURE 0x800
+#define GESTURE_DOLLARRECORD 0x801
+#define GESTURE_MULTIGESTURE 0x802
+
+typedef struct Gesture_MultiGestureEvent
+{
+ Uint32 type;
+ Uint32 timestamp;
+ SDL_TouchID touchId;
+ float dTheta;
+ float dDist;
+ float x;
+ float y;
+ Uint16 numFingers;
+ Uint16 padding;
+} Gesture_MultiGestureEvent;
+
+typedef struct Gesture_DollarGestureEvent
+{
+ Uint32 type;
+ Uint32 timestamp;
+ SDL_TouchID touchId;
+ SDL_GestureID gestureId;
+ Uint32 numFingers;
+ float error;
+ float x;
+ float y;
+} Gesture_DollarGestureEvent;
+
+
+/* Function prototypes */
+
+/**
+ * Call this once, AFTER SDL_Init, to set up the Gesture API.
+ *
+ * \returns 0 on success, -1 on error. Call SDL_GetError() for specifics.
+ */
+extern int SDLCALL Gesture_Init(void);
+
+/**
+ * Call this once, BEFORE SDL_Quit, to clean up the Gesture API.
+ */
+extern void SDLCALL Gesture_Quit(void);
+
+/**
+ * Begin recording a gesture on a specified touch device or all touch devices.
+ *
+ * If the parameter `touchId` is -1 (i.e., all devices), this function will
+ * always return 1, regardless of whether there actually are any devices.
+ *
+ * \param touchId the touch device id, or -1 for all touch devices
+ * \returns 1 on success or 0 if the specified device could not be found.
+ */
+extern int SDLCALL Gesture_RecordGesture(SDL_TouchID touchId);
+
+/**
+ * Save all currently loaded Dollar Gesture templates.
+ *
+ * \param dst a SDL_RWops to save to
+ * \returns the number of saved templates on success or 0 on failure; call
+ * SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 2.0.0.
+ *
+ * \sa Gesture_LoadDollarTemplates
+ * \sa Gesture_SaveDollarTemplate
+ */
+extern int SDLCALL Gesture_SaveAllDollarTemplates(SDL_RWops *dst);
+
+/**
+ * Save a currently loaded Dollar Gesture template.
+ *
+ * \param gestureId a gesture id
+ * \param dst a SDL_RWops to save to
+ * \returns 1 on success or 0 on failure; call SDL_GetError() for more
+ * information.
+ *
+ * \since This function is available since SDL 2.0.0.
+ *
+ * \sa SDL_LoadDollarTemplates
+ * \sa SDL_SaveAllDollarTemplates
+ */
+extern int SDLCALL Gesture_SaveDollarTemplate(SDL_GestureID gestureId,SDL_RWops *dst);
+
+/**
+ * Load Dollar Gesture templates from a file.
+ *
+ * \param touchId a touch id
+ * \param src a SDL_RWops to load from
+ * \returns the number of loaded templates on success or a negative error code
+ * (or 0) on failure; call SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 2.0.0.
+ *
+ * \sa SDL_SaveAllDollarTemplates
+ * \sa SDL_SaveDollarTemplate
+ */
+extern int SDLCALL Gesture_LoadDollarTemplates(SDL_TouchID touchId, SDL_RWops *src);
+
+/* Ends C function definitions when using C++ */
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !defined INCL_SDL_GESTURE_H */
+
+
+#if defined(SDL_GESTURE_IMPLEMENTATION)
+
+#define GESTURE_MAX_DOLLAR_PATH_SIZE 1024
+#define GESTURE_DOLLARNPOINTS 64
+#define GESTURE_DOLLARSIZE 256
+#define GESTURE_PHI 0.618033989
+
+typedef struct
+{
+ float length;
+ int numPoints;
+ SDL_FPoint p[GESTURE_MAX_DOLLAR_PATH_SIZE];
+} GestureDollarPath;
+
+typedef struct
+{
+ SDL_FPoint path[GESTURE_DOLLARNPOINTS];
+ unsigned long hash;
+} GestureDollarTemplate;
+
+typedef struct
+{
+ SDL_TouchID touchId;
+ SDL_FPoint centroid;
+ GestureDollarPath dollarPath;
+ Uint16 numDownFingers;
+ int numDollarTemplates;
+ GestureDollarTemplate *dollarTemplate;
+ SDL_bool recording;
+} GestureTouch;
+
+static GestureTouch *GestureTouches = NULL;
+static int GestureNumTouches = 0;
+static SDL_bool GestureRecordAll = SDL_FALSE;
+
+
+static void GestureProcessEvent(const SDL_Event *event);
+
+static int SDLCALL *GestureEventWatch(void *userdata, SDL_Event *event)
+{
+ GestureProcessEvent(event);
+ return 1;
+}
+
+int Gesture_Init(void)
+{
+ Gesture_Quit();
+ SDL_AddEventWatch(GestureEventWatch, NULL);
+ return 0;
+}
+
+
+static GestureTouch *GestureAddTouch(const SDL_TouchID touchId)
+{
+ GestureTouch *gestureTouch = (GestureTouch *)SDL_realloc(GestureTouches, (GestureNumTouches + 1) * sizeof(GestureTouch));
+ if (gestureTouch == NULL) {
+ SDL_OutOfMemory();
+ return NULL;
+ }
+
+ GestureTouches = gestureTouch;
+ SDL_zero(GestureTouches[GestureNumTouches]);
+ GestureTouches[GestureNumTouches].touchId = touchId;
+ return &GestureTouches[GestureNumTouches++];
+}
+
+static int GestureDelTouch(const SDL_TouchID touchId)
+{
+ int i;
+ for (i = 0; i < GestureNumTouches; i++) {
+ if (GestureTouches[i].touchId == touchId) {
+ break;
+ }
+ }
+
+ if (i == GestureNumTouches) {
+ /* not found */
+ return -1;
+ }
+
+ SDL_free(GestureTouches[i].dollarTemplate);
+ SDL_zero(GestureTouches[i]);
+
+ GestureNumTouches--;
+ if (i != GestureNumTouches) {
+ SDL_copyp(&GestureTouches[i], &GestureTouches[GestureNumTouches]);
+ }
+ return 0;
+}
+
+static GestureTouch *GestureGetTouch(const SDL_TouchID touchId)
+{
+ int i;
+ for (i = 0; i < GestureNumTouches; i++) {
+ /* printf("%i ?= %i\n",GestureTouches[i].touchId,touchId); */
+ if (GestureTouches[i].touchId == touchId) {
+ return &GestureTouches[i];
+ }
+ }
+ return NULL;
+}
+
+int SDL_RecordGesture(SDL_TouchID touchId)
+{
+ const int numtouchdevs = SDL_GetNumTouchDevices();
+ int i;
+
+ /* make sure we know about all the devices SDL3 knows about, since we aren't connected as tightly as we were in SDL2. */
+ for (i = 0; i < numtouchdevs; i++) {
+ const SDL_TouchID thistouch = SDL_GetTouchDevice(i);
+ if (!GestureGetTouch(thistouch)) {
+ if (!GestureAddTouch(thistouch)) {
+ return 0; /* uhoh, out of memory */
+ }
+ }
+ }
+
+ if (touchId < 0) {
+ GestureRecordAll = SDL_TRUE; /* !!! FIXME: this is never set back to SDL_FALSE anywhere, that's probably a bug. */
+ for (i = 0; i < GestureNumTouches; i++) {
+ GestureTouches[i].recording = SDL_TRUE;
+ }
+ } else {
+ GestureTouch *touch = GestureGetTouch(touchId);
+ if (!touch) {
+ return 0; /* bogus touchid */
+ }
+ touch->recording = SDL_TRUE;
+ }
+
+ return 1;
+}
+
+void Gesture_Quit(void)
+{
+ SDL_DelEventWatch(GestureEventWatch, NULL);
+ SDL_free(GestureTouches);
+ GestureTouches = NULL;
+ GestureNumTouches = 0;
+ GestureRecordAll = SDL_FALSE;
+}
+
+static unsigned long GestureHashDollar(SDL_FPoint *points)
+{
+ unsigned long hash = 5381;
+ int i;
+ for (i = 0; i < GESTURE_DOLLARNPOINTS; i++) {
+ hash = ((hash << 5) + hash) + (unsigned long)points[i].x;
+ hash = ((hash << 5) + hash) + (unsigned long)points[i].y;
+ }
+ return hash;
+}
+
+static int GestureSaveTemplate(GestureDollarTemplate *templ, SDL_RWops *dst)
+{
+ const Sint64 bytes = sizeof(templ->path[0]) * GESTURE_DOLLARNPOINTS;
+
+ if (dst == NULL) {
+ return 0;
+ }
+
+ /* No Longer storing the Hash, rehash on load */
+ /* if (SDL_RWops.write(dst, &(templ->hash), sizeof(templ->hash)) != sizeof(templ->hash)) return 0; */
+
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+ if (SDL_RWwrite(dst, templ->path, bytes) != bytes) {
+ return 0;
+ }
+#else
+ {
+ GestureDollarTemplate copy = *templ;
+ SDL_FPoint *p = copy.path;
+ int i;
+ for (i = 0; i < GESTURE_DOLLARNPOINTS; i++, p++) {
+ p->x = SDL_SwapFloatLE(p->x);
+ p->y = SDL_SwapFloatLE(p->y);
+ }
+
+ if (SDL_RWwrite(dst, copy.path, bytes) != bytes) {
+ return 0;
+ }
+ }
+#endif
+
+ return 1;
+}
+
+DECLSPEC int SDLCALL
+SDL_SaveAllDollarTemplates(SDL_RWops *dst)
+{
+ int i, j, rtrn = 0;
+ for (i = 0; i < GestureNumTouches; i++) {
+ GestureTouch *touch = &GestureTouches[i];
+ for (j = 0; j < touch->numDollarTemplates; j++) {
+ rtrn += GestureSaveTemplate(&touch->dollarTemplate[j], dst);
+ }
+ }
+ return rtrn;
+}
+
+DECLSPEC int SDLCALL
+SDL_SaveDollarTemplate(SDL_GestureID gestureId, SDL_RWops *dst)
+{
+ int i, j;
+ for (i = 0; i < GestureNumTouches; i++) {
+ GestureTouch *touch = &GestureTouches[i];
+ for (j = 0; j < touch->numDollarTemplates; j++) {
+ if (touch->dollarTemplate[j].hash == gestureId) {
+ return GestureSaveTemplate(&touch->dollarTemplate[j], dst);
+ }
+ }
+ }
+ return SDL_SetError("Unknown gestureId");
+}
+
+/* path is an already sampled set of points
+Returns the index of the gesture on success, or -1 */
+static int GestureAddDollar_one(GestureTouch *inTouch, SDL_FPoint *path)
+{
+ GestureDollarTemplate *dollarTemplate;
+ GestureDollarTemplate *templ;
+ int index;
+
+ index = inTouch->numDollarTemplates;
+ dollarTemplate = (GestureDollarTemplate *)SDL_realloc(inTouch->dollarTemplate, (index + 1) * sizeof(GestureDollarTemplate));
+ if (dollarTemplate == NULL) {
+ return SDL_OutOfMemory();
+ }
+ inTouch->dollarTemplate = dollarTemplate;
+
+ templ = &inTouch->dollarTemplate[index];
+ SDL_memcpy(templ->path, path, GESTURE_DOLLARNPOINTS * sizeof(SDL_FPoint));
+ templ->hash = GestureHashDollar(templ->path);
+ inTouch->numDollarTemplates++;
+
+ return index;
+}
+
+static int GestureAddDollar(GestureTouch *inTouch, SDL_FPoint *path)
+{
+ int index = -1;
+ int i = 0;
+ if (inTouch == NULL) {
+ if (GestureNumTouches == 0) {
+ return SDL_SetError("no gesture touch devices registered");
+ }
+ for (i = 0; i < GestureNumTouches; i++) {
+ inTouch = &GestureTouches[i];
+ index = GestureAddDollar_one(inTouch, path);
+ if (index < 0) {
+ return -1;
+ }
+ }
+ /* Use the index of the last one added. */
+ return index;
+ }
+ return GestureAddDollar_one(inTouch, path);
+}
+
+DECLSPEC int SDLCALL
+SDL_LoadDollarTemplates(SDL_TouchID touchId, SDL_RWops *src)
+{
+ int i, loaded = 0;
+ GestureTouch *touch = NULL;
+ if (src == NULL) {
+ return 0;
+ }
+ if (touchId >= 0) {
+ for (i = 0; i < GestureNumTouches; i++) {
+ if (GestureTouches[i].touchId == touchId) {
+ touch = &GestureTouches[i];
+ }
+ }
+ if (touch == NULL) {
+ return SDL_SetError("given touch id not found");
+ }
+ }
+
+ while (1) {
+ GestureDollarTemplate templ;
+ const Sint64 bytes = sizeof(templ->path[0]) * GESTURE_DOLLARNPOINTS;
+
+ if (SDL_RWread(src, templ.path, bytes) < bytes) {
+ if (loaded == 0) {
+ return SDL_SetError("could not read any dollar gesture from rwops");
+ }
+ break;
+ }
+
+#if SDL_BYTEORDER != SDL_LIL_ENDIAN
+ for (i = 0; i < GESTURE_DOLLARNPOINTS; i++) {
+ SDL_FPoint *p = &templ.path[i];
+ p->x = SDL_SwapFloatLE(p->x);
+ p->y = SDL_SwapFloatLE(p->y);
+ }
+#endif
+
+ if (touchId >= 0) {
+ /* printf("Adding loaded gesture to 1 touch\n"); */
+ if (GestureAddDollar(touch, templ.path) >= 0) {
+ loaded++;
+ }
+ } else {
+ /* printf("Adding to: %i touches\n",GestureNumTouches); */
+ for (i = 0; i < GestureNumTouches; i++) {
+ touch = &GestureTouches[i];
+ /* printf("Adding loaded gesture to + touches\n"); */
+ /* TODO: What if this fails? */
+ GestureAddDollar(touch, templ.path);
+ }
+ loaded++;
+ }
+ }
+
+ return loaded;
+}
+
+static float GestureDollarDifference(SDL_FPoint *points, SDL_FPoint *templ, float ang)
+{
+ /* SDL_FPoint p[GESTURE_DOLLARNPOINTS]; */
+ float dist = 0;
+ SDL_FPoint p;
+ int i;
+ for (i = 0; i < GESTURE_DOLLARNPOINTS; i++) {
+ p.x = points[i].x * SDL_cosf(ang) - points[i].y * SDL_sinf(ang);
+ p.y = points[i].x * SDL_sinf(ang) + points[i].y * SDL_cosf(ang);
+ dist += SDL_sqrtf((p.x - templ[i].x) * (p.x - templ[i].x) + (p.y - templ[i].y) * (p.y - templ[i].y));
+ }
+ return dist / GESTURE_DOLLARNPOINTS;
+}
+
+static float GestureBestDollarDifference(SDL_FPoint *points, SDL_FPoint *templ)
+{
+ /*------------BEGIN DOLLAR BLACKBOX------------------
+ -TRANSLATED DIRECTLY FROM PSUDEO-CODE AVAILABLE AT-
+ -"http://depts.washington.edu/aimgroup/proj/dollar/"
+ */
+ double ta = -SDL_PI_D / 4;
+ double tb = SDL_PI_D / 4;
+ double dt = SDL_PI_D / 90;
+ float x1 = (float)(GESTURE_PHI * ta + (1 - GESTURE_PHI) * tb);
+ float f1 = GestureDollarDifference(points, templ, x1);
+ float x2 = (float)((1 - GESTURE_PHI) * ta + GESTURE_PHI * tb);
+ float f2 = GestureDollarDifference(points, templ, x2);
+ while (SDL_fabs(ta - tb) > dt) {
+ if (f1 < f2) {
+ tb = x2;
+ x2 = x1;
+ f2 = f1;
+ x1 = (float)(GESTURE_PHI * ta + (1 - GESTURE_PHI) * tb);
+ f1 = GestureDollarDifference(points, templ, x1);
+ } else {
+ ta = x1;
+ x1 = x2;
+ f1 = f2;
+ x2 = (float)((1 - GESTURE_PHI) * ta + GESTURE_PHI * tb);
+ f2 = GestureDollarDifference(points, templ, x2);
+ }
+ }
+ /*
+ if (f1 <= f2)
+ printf("Min angle (x1): %f\n",x1);
+ else if (f1 > f2)
+ printf("Min angle (x2): %f\n",x2);
+ */
+ return SDL_min(f1, f2);
+}
+
+/* `path` contains raw points, plus (possibly) the calculated length */
+static int GestureDollarNormalize(const GestureDollarPath *path, SDL_FPoint *points, SDL_bool is_recording)
+{
+ int i;
+ float interval;
+ float dist;
+ int numPoints = 0;
+ SDL_FPoint centroid;
+ float xmin, xmax, ymin, ymax;
+ float ang;
+ float w, h;
+ float length = path->length;
+
+ /* Calculate length if it hasn't already been done */
+ if (length <= 0) {
+ for (i = 1; i < path->numPoints; i++) {
+ const float dx = path->p[i].x - path->p[i - 1].x;
+ const float dy = path->p[i].y - path->p[i - 1].y;
+ length += SDL_sqrtf(dx * dx + dy * dy);
+ }
+ }
+
+ /* Resample */
+ interval = length / (GESTURE_DOLLARNPOINTS - 1);
+ dist = interval;
+
+ centroid.x = 0;
+ centroid.y = 0;
+
+ /* printf("(%f,%f)\n",path->p[path->numPoints-1].x,path->p[path->numPoints-1].y); */
+ for (i = 1; i < path->numPoints; i++) {
+ const float d = SDL_sqrtf((path->p[i - 1].x - path->p[i].x) * (path->p[i - 1].x - path->p[i].x) + (path->p[i - 1].y - path->p[i].y) * (path->p[i - 1].y - path->p[i].y));
+ /* printf("d = %f dist = %f/%f\n",d,dist,interval); */
+ while (dist + d > interval) {
+ points[numPoints].x = path->p[i - 1].x +
+ ((interval - dist) / d) * (path->p[i].x - path->p[i - 1].x);
+ points[numPoints].y = path->p[i - 1].y +
+ ((interval - dist) / d) * (path->p[i].y - path->p[i - 1].y);
+ centroid.x += points[numPoints].x;
+ centroid.y += points[numPoints].y;
+ numPoints++;
+
+ dist -= interval;
+ }
+ dist += d;
+ }
+ if (numPoints < GESTURE_DOLLARNPOINTS - 1) {
+ if (is_recording) {
+ SDL_SetError("ERROR: NumPoints = %i", numPoints);
+ }
+ return 0;
+ }
+ /* copy the last point */
+ points[GESTURE_DOLLARNPOINTS - 1] = path->p[path->numPoints - 1];
+ numPoints = GESTURE_DOLLARNPOINTS;
+
+ centroid.x /= numPoints;
+ centroid.y /= numPoints;
+
+ /* printf("Centroid (%f,%f)",centroid.x,centroid.y); */
+ /* Rotate Points so point 0 is left of centroid and solve for the bounding box */
+ xmin = centroid.x;
+ xmax = centroid.x;
+ ymin = centroid.y;
+ ymax = centroid.y;
+
+ ang = SDL_atan2f(centroid.y - points[0].y, centroid.x - points[0].x);
+
+ for (i = 0; i < numPoints; i++) {
+ const float px = points[i].x;
+ const float py = points[i].y;
+ points[i].x = (px - centroid.x) * SDL_cosf(ang) - (py - centroid.y) * SDL_sinf(ang) + centroid.x;
+ points[i].y = (px - centroid.x) * SDL_sinf(ang) + (py - centroid.y) * SDL_cosf(ang) + centroid.y;
+
+ if (points[i].x < xmin) {
+ xmin = points[i].x;
+ }
+ if (points[i].x > xmax) {
+ xmax = points[i].x;
+ }
+ if (points[i].y < ymin) {
+ ymin = points[i].y;
+ }
+ if (points[i].y > ymax) {
+ ymax = points[i].y;
+ }
+ }
+
+ /* Scale points to GESTURE_DOLLARSIZE, and translate to the origin */
+ w = xmax - xmin;
+ h = ymax - ymin;
+
+ for (i = 0; i < numPoints; i++) {
+ points[i].x = (points[i].x - centroid.x) * GESTURE_DOLLARSIZE / w;
+ points[i].y = (points[i].y - centroid.y) * GESTURE_DOLLARSIZE / h;
+ }
+ return numPoints;
+}
+
+static float GestureDollarRecognize(const GestureDollarPath *path, int *bestTempl, GestureTouch *touch)
+{
+ SDL_FPoint points[GESTURE_DOLLARNPOINTS];
+ int i;
+ float bestDiff = 10000;
+
+ SDL_memset(points, 0, sizeof(points));
+
+ GestureDollarNormalize(path, points, SDL_FALSE);
+
+ /* PrintPath(points); */
+ *bestTempl = -1;
+ for (i = 0; i < touch->numDollarTemplates; i++) {
+ const float diff = GestureBestDollarDifference(points, touch->dollarTemplate[i].path);
+ if (diff < bestDiff) {
+ bestDiff = diff;
+ *bestTempl = i;
+ }
+ }
+ return bestDiff;
+}
+
+static void GestureSendMulti(GestureTouch *touch, float dTheta, float dDist)
+{
+ if (SDL_GetEventState(SDL_MULTIGESTURE) == SDL_ENABLE) {
+ SDL_Event event;
+ event.type = GESTURE_MULTIGESTURE;
+ event.common.timestamp = 0;
+ event.mgesture.touchId = touch->touchId;
+ event.mgesture.x = touch->centroid.x;
+ event.mgesture.y = touch->centroid.y;
+ event.mgesture.dTheta = dTheta;
+ event.mgesture.dDist = dDist;
+ event.mgesture.numFingers = touch->numDownFingers;
+ SDL_PushEvent(&event);
+ }
+}
+
+static void GestureSendDollar(GestureTouch *touch, SDL_GestureID gestureId, float error)
+{
+ if (SDL_GetEventState(SDL_DOLLARGESTURE) == SDL_ENABLE) {
+ SDL_Event event;
+ event.type = GESTURE_DOLLARGESTURE;
+ event.common.timestamp = 0;
+ event.dgesture.touchId = touch->touchId;
+ event.dgesture.x = touch->centroid.x;
+ event.dgesture.y = touch->centroid.y;
+ event.dgesture.gestureId = gestureId;
+ event.dgesture.error = error;
+ /* A finger came up to trigger this event. */
+ event.dgesture.numFingers = touch->numDownFingers + 1;
+ SDL_PushEvent(&event);
+ }
+}
+
+static void GestureSendDollarRecord(GestureTouch *touch, SDL_GestureID gestureId)
+{
+ if (SDL_GetEventState(SDL_DOLLARRECORD) == SDL_ENABLE) {
+ SDL_Event event;
+ event.type = GESTURE_DOLLARRECORD;
+ event.common.timestamp = 0;
+ event.dgesture.touchId = touch->touchId;
+ event.dgesture.gestureId = gestureId;
+ SDL_PushEvent(&event);
+ }
+}
+
+static void GestureProcessEvent(const SDL_Event *event)
+{
+ float x, y;
+ int index;
+ int i;
+ float pathDx, pathDy;
+ SDL_FPoint lastP;
+ SDL_FPoint lastCentroid;
+ float lDist;
+ float Dist;
+ float dtheta;
+ float dDist;
+
+ if (event->type == SDL_FINGERMOTION || event->type == SDL_FINGERDOWN || event->type == SDL_FINGERUP) {
+ GestureTouch *inTouch = GestureGetTouch(event->tfinger.touchId);
+
+ if (inTouch == NULL) { /* we maybe didn't see this one before. */
+ inTouch = GestureAddTouch(event->tfinger.touchId);
+ if (!inTouch) {
+ return; /* oh well. */
+ }
+ }
+
+ x = event->tfinger.x;
+ y = event->tfinger.y;
+
+ /* Finger Up */
+ if (event->type == SDL_FINGERUP) {
+ SDL_FPoint path[GESTURE_DOLLARNPOINTS];
+ inTouch->numDownFingers--;
+
+ if (inTouch->recording) {
+ inTouch->recording = SDL_FALSE;
+ GestureDollarNormalize(&inTouch->dollarPath, path, SDL_TRUE);
+ /* PrintPath(path); */
+ if (GestureRecordAll) {
+ index = GestureAddDollar(NULL, path);
+ for (i = 0; i < GestureNumTouches; i++) {
+ GestureTouches[i].recording = SDL_FALSE;
+ }
+ } else {
+ index = GestureAddDollar(inTouch, path);
+ }
+
+ if (index >= 0) {
+ GestureSendDollarRecord(inTouch, inTouch->dollarTemplate[index].hash);
+ } else {
+ GestureSendDollarRecord(inTouch, -1);
+ }
+ } else {
+ int bestTempl = -1;
+ const float error = GestureDollarRecognize(&inTouch->dollarPath, &bestTempl, inTouch);
+ if (bestTempl >= 0) {
+ /* Send Event */
+ const unsigned long gestureId = inTouch->dollarTemplate[bestTempl].hash;
+ GestureSendDollar(inTouch, gestureId, error);
+ /* printf ("%s\n",);("Dollar error: %f\n",error); */
+ }
+ }
+
+ /* inTouch->gestureLast[j] = inTouch->gestureLast[inTouch->numDownFingers]; */
+ if (inTouch->numDownFingers > 0) {
+ inTouch->centroid.x = (inTouch->centroid.x * (inTouch->numDownFingers + 1) - x) / inTouch->numDownFingers;
+ inTouch->centroid.y = (inTouch->centroid.y * (inTouch->numDownFingers + 1) - y) / inTouch->numDownFingers;
+ }
+ } else if (event->type == SDL_FINGERMOTION) {
+ const float dx = event->tfinger.dx;
+ const float dy = event->tfinger.dy;
+ GestureDollarPath *path = &inTouch->dollarPath;
+ if (path->numPoints < GESTURE_MAX_DOLLAR_PATH_SIZE) {
+ path->p[path->numPoints].x = inTouch->centroid.x;
+ path->p[path->numPoints].y = inTouch->centroid.y;
+ pathDx = (path->p[path->numPoints].x - path->p[path->numPoints - 1].x);
+ pathDy = (path->p[path->numPoints].y - path->p[path->numPoints - 1].y);
+ path->length += (float)SDL_sqrt(pathDx * pathDx + pathDy * pathDy);
+ path->numPoints++;
+ }
+
+ lastP.x = x - dx;
+ lastP.y = y - dy;
+ lastCentroid = inTouch->centroid;
+
+ inTouch->centroid.x += dx / inTouch->numDownFingers;
+ inTouch->centroid.y += dy / inTouch->numDownFingers;
+ /* printf("Centrid : (%f,%f)\n",inTouch->centroid.x,inTouch->centroid.y); */
+ if (inTouch->numDownFingers > 1) {
+ SDL_FPoint lv; /* Vector from centroid to last x,y position */
+ SDL_FPoint v; /* Vector from centroid to current x,y position */
+ /* lv = inTouch->gestureLast[j].cv; */
+ lv.x = lastP.x - lastCentroid.x;
+ lv.y = lastP.y - lastCentroid.y;
+ lDist = SDL_sqrtf(lv.x * lv.x + lv.y * lv.y);
+ /* printf("lDist = %f\n",lDist); */
+ v.x = x - inTouch->centroid.x;
+ v.y = y - inTouch->centroid.y;
+ /* inTouch->gestureLast[j].cv = v; */
+ Dist = SDL_sqrtf(v.x * v.x + v.y * v.y);
+ /* SDL_cosf(dTheta) = (v . lv)/(|v| * |lv|) */
+
+ /* Normalize Vectors to simplify angle calculation */
+ lv.x /= lDist;
+ lv.y /= lDist;
+ v.x /= Dist;
+ v.y /= Dist;
+ dtheta = SDL_atan2f(lv.x * v.y - lv.y * v.x, lv.x * v.x + lv.y * v.y);
+
+ dDist = (Dist - lDist);
+ if (lDist == 0) {
+ /* To avoid impossible values */
+ dDist = 0;
+ dtheta = 0;
+ }
+
+ /* inTouch->gestureLast[j].dDist = dDist;
+ inTouch->gestureLast[j].dtheta = dtheta;
+
+ printf("dDist = %f, dTheta = %f\n",dDist,dtheta);
+ gdtheta = gdtheta*.9 + dtheta*.1;
+ gdDist = gdDist*.9 + dDist*.1
+ knob.r += dDist/numDownFingers;
+ knob.ang += dtheta;
+ printf("thetaSum = %f, distSum = %f\n",gdtheta,gdDist);
+ printf("id: %i dTheta = %f, dDist = %f\n",j,dtheta,dDist); */
+ GestureSendMulti(inTouch, dtheta, dDist);
+ } else {
+ /* inTouch->gestureLast[j].dDist = 0;
+ inTouch->gestureLast[j].dtheta = 0;
+ inTouch->gestureLast[j].cv.x = 0;
+ inTouch->gestureLast[j].cv.y = 0; */
+ }
+ /* inTouch->gestureLast[j].f.p.x = x;
+ inTouch->gestureLast[j].f.p.y = y;
+ break;
+ pressure? */
+ } else if (event->type == SDL_FINGERDOWN) {
+ inTouch->numDownFingers++;
+ inTouch->centroid.x = (inTouch->centroid.x * (inTouch->numDownFingers - 1) +
+ x) /
+ inTouch->numDownFingers;
+ inTouch->centroid.y = (inTouch->centroid.y * (inTouch->numDownFingers - 1) +
+ y) /
+ inTouch->numDownFingers;
+ /* printf("Finger Down: (%f,%f). Centroid: (%f,%f\n",x,y,
+ inTouch->centroid.x,inTouch->centroid.y); */
+
+ inTouch->dollarPath.length = 0;
+ inTouch->dollarPath.p[0].x = x;
+ inTouch->dollarPath.p[0].y = y;
+ inTouch->dollarPath.numPoints = 1;
+ }
+ }
+}
+
+#endif /* defined(SDL_GESTURE_IMPLEMENTATION) */
+#endif /* SDL vesion > 2
(Patch may be truncated, please check the link at the top of this post.)