Add ktx
This commit is contained in:
+966
@@ -0,0 +1,966 @@
|
||||
/*
|
||||
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_MAJOR_VERSION)
|
||||
#error Please include SDL.h before including this header.
|
||||
#elif SDL_MAJOR_VERSION < 2
|
||||
#error This header requires SDL2 or later.
|
||||
#elif SDL_MAJOR_VERSION == 2
|
||||
/* building against SDL2? Just use the built-in SDL2 implementation. */
|
||||
#define Gesture_Init() (0)
|
||||
#define Gesture_Quit()
|
||||
#define Gesture_ID SDL_GestureID
|
||||
#define Gesture_LoadDollarTemplates SDL_LoadDollarTemplates
|
||||
#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_DollarGestureEvent
|
||||
#else
|
||||
|
||||
#include <cmath>
|
||||
|
||||
/* 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 reserved;
|
||||
Uint64 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 reserved;
|
||||
Uint64 timestamp;
|
||||
SDL_TouchID touchID;
|
||||
Gesture_ID 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_IOStream 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_IOStream *dst);
|
||||
|
||||
/**
|
||||
* Save a currently loaded Dollar Gesture template.
|
||||
*
|
||||
* \param gestureId a gesture id
|
||||
* \param dst a SDL_IOStream 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(Gesture_ID gestureId, SDL_IOStream *dst);
|
||||
|
||||
/**
|
||||
* Load Dollar Gesture templates from a file.
|
||||
*
|
||||
* \param touchID a touch id
|
||||
* \param src a SDL_IOStream 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_IOStream *src);
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#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];
|
||||
Sint64 hash;
|
||||
} GestureDollarTemplate;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SDL_TouchID touchID;
|
||||
SDL_FPoint centroid;
|
||||
GestureDollarPath dollarPath;
|
||||
int numDownFingers;
|
||||
int numDollarTemplates;
|
||||
GestureDollarTemplate *dollarTemplate;
|
||||
bool recording;
|
||||
} GestureTouch;
|
||||
|
||||
static GestureTouch *GestureTouches = NULL;
|
||||
static int GestureNumTouches = 0;
|
||||
static bool GestureRecordAll = false;
|
||||
|
||||
static void GestureProcessEvent(const SDL_Event *event);
|
||||
|
||||
static bool SDLCALL GestureEventWatch(void *, SDL_Event *event)
|
||||
{
|
||||
GestureProcessEvent(event);
|
||||
return true;
|
||||
}
|
||||
|
||||
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++];
|
||||
}
|
||||
|
||||
#if 0
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
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 Gesture_RecordGesture(SDL_TouchID touchID)
|
||||
{
|
||||
SDL_TouchID *devices;
|
||||
int i;
|
||||
|
||||
devices = SDL_GetTouchDevices(NULL);
|
||||
if (devices) {
|
||||
/* 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; devices[i]; i++) {
|
||||
if (!GestureGetTouch(devices[i])) {
|
||||
GestureAddTouch(devices[i]);
|
||||
}
|
||||
}
|
||||
SDL_free(devices);
|
||||
}
|
||||
|
||||
if (touchID != 0) {
|
||||
GestureRecordAll = true; /* !!! FIXME: this is never set back to false anywhere, that's probably a bug. */
|
||||
for (i = 0; i < GestureNumTouches; i++) {
|
||||
GestureTouches[i].recording = true;
|
||||
}
|
||||
} else {
|
||||
GestureTouch *touch = GestureGetTouch(touchID);
|
||||
if (!touch) {
|
||||
return 0; /* bogus touchid */
|
||||
}
|
||||
touch->recording = true;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Gesture_Quit(void)
|
||||
{
|
||||
SDL_RemoveEventWatch(GestureEventWatch, NULL);
|
||||
SDL_free(GestureTouches);
|
||||
GestureTouches = NULL;
|
||||
GestureNumTouches = 0;
|
||||
GestureRecordAll = 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_IOStream *dst)
|
||||
{
|
||||
const size_t bytes = sizeof(templ->path[0]) * GESTURE_DOLLARNPOINTS;
|
||||
|
||||
if (dst == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* No Longer storing the Hash, rehash on load */
|
||||
/* if (SDL_IOWrite(dst, &(templ->hash), sizeof(templ->hash)) != sizeof(templ->hash)) return 0; */
|
||||
|
||||
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
|
||||
if (SDL_WriteIO(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_WriteIO(dst, copy.path, bytes) != bytes) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
SDL_DECLSPEC int SDLCALL
|
||||
Gesture_SaveAllDollarTemplates(SDL_IOStream *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;
|
||||
}
|
||||
|
||||
SDL_DECLSPEC int SDLCALL
|
||||
Gesture_SaveDollarTemplate(Gesture_ID gestureId, SDL_IOStream *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);
|
||||
}
|
||||
|
||||
SDL_DECLSPEC int SDLCALL
|
||||
Gesture_LoadDollarTemplates(SDL_TouchID touchID, SDL_IOStream *src)
|
||||
{
|
||||
int i, loaded = 0;
|
||||
GestureTouch *touch = NULL;
|
||||
if (src == NULL) {
|
||||
return 0;
|
||||
}
|
||||
/* In SDL2 this test was `touchID >= 0` leading to warnings from gcc
|
||||
because SDL_TouchId is now Uint64. In SDL2 it was Sint64. The
|
||||
documentation does not say what < 0 means here but the only defined
|
||||
negative touchID was SDL_MOUSE_TOUCHID (-1). In SDL3 SDL_PEN_TOUCHID (-2)
|
||||
has been added hence this test. Given the lack of documentation
|
||||
it is impossible to say if this updated test is correct. */
|
||||
if (touchID < SDL_PEN_TOUCHID) {
|
||||
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 size_t bytes = sizeof(templ.path[0]) * GESTURE_DOLLARNPOINTS;
|
||||
|
||||
if (SDL_ReadIO(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
|
||||
|
||||
// See comment at line 436.
|
||||
if (touchID < SDL_PEN_TOUCHID) {
|
||||
/* 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, 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, 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_EventEnabled(GESTURE_MULTIGESTURE)) {
|
||||
Gesture_MultiGestureEvent mgesture;
|
||||
mgesture.type = GESTURE_MULTIGESTURE;
|
||||
mgesture.timestamp = 0;
|
||||
mgesture.touchID = touch->touchID;
|
||||
mgesture.x = touch->centroid.x;
|
||||
mgesture.y = touch->centroid.y;
|
||||
mgesture.dTheta = dTheta;
|
||||
mgesture.dDist = dDist;
|
||||
mgesture.numFingers = (Uint16)touch->numDownFingers;
|
||||
SDL_PushEvent((SDL_Event*)&mgesture);
|
||||
}
|
||||
}
|
||||
|
||||
static void GestureSendDollar(GestureTouch *touch, Gesture_ID gestureId, float error)
|
||||
{
|
||||
if (SDL_EventEnabled(GESTURE_DOLLARGESTURE)) {
|
||||
Gesture_DollarGestureEvent dgesture;
|
||||
dgesture.type = GESTURE_DOLLARGESTURE;
|
||||
dgesture.timestamp = 0;
|
||||
dgesture.touchID = touch->touchID;
|
||||
dgesture.x = touch->centroid.x;
|
||||
dgesture.y = touch->centroid.y;
|
||||
dgesture.gestureId = gestureId;
|
||||
dgesture.error = error;
|
||||
/* A finger came up to trigger this event. */
|
||||
dgesture.numFingers = touch->numDownFingers + 1;
|
||||
SDL_PushEvent((SDL_Event*)&dgesture);
|
||||
}
|
||||
}
|
||||
|
||||
static void GestureSendDollarRecord(GestureTouch *touch, Gesture_ID gestureId)
|
||||
{
|
||||
if (SDL_EventEnabled(GESTURE_DOLLARRECORD)) {
|
||||
Gesture_DollarGestureEvent dgesture;
|
||||
dgesture.type = GESTURE_DOLLARRECORD;
|
||||
dgesture.timestamp = 0;
|
||||
dgesture.touchID = touch->touchID;
|
||||
dgesture.gestureId = gestureId;
|
||||
SDL_PushEvent((SDL_Event*)&dgesture);
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(GESTURE_LOG_UP_DOWN_EVENTS)
|
||||
#define GESTURE_LOG_UP_DOWN_EVENTS 0
|
||||
#endif
|
||||
#if !defined(GESTURE_LOG_MOTION_EVENTS)
|
||||
#define GESTURE_LOG_MOTION_EVENTS 0
|
||||
#endif
|
||||
|
||||
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_EVENT_FINGER_MOTION || event->type == SDL_EVENT_FINGER_DOWN || event->type == SDL_EVENT_FINGER_UP) {
|
||||
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. */
|
||||
}
|
||||
}
|
||||
int numDownFingersReported;
|
||||
SDL_Finger** fingers = SDL_GetTouchFingers(event->tfinger.touchID, &numDownFingersReported);
|
||||
|
||||
x = event->tfinger.x;
|
||||
y = event->tfinger.y;
|
||||
|
||||
/* Finger Up */
|
||||
if (event->type == SDL_EVENT_FINGER_UP) {
|
||||
#if GESTURE_LOG_UP_DOWN_EVENTS
|
||||
SDL_Log("GPE: Finger: %#" SDL_PRIx64 " UP. Device: %#" SDL_PRIx64 ", fingers: %i, x: %f, y: %f, press: %f",
|
||||
event->tfinger.fingerID, event->tfinger.touchID, numDownFingersReported,
|
||||
event->tfinger.x, event->tfinger.y, event->tfinger.pressure);
|
||||
#endif
|
||||
SDL_FPoint path[GESTURE_DOLLARNPOINTS];
|
||||
|
||||
#if SDL_PLATFORM_MACOS
|
||||
/* Workaround issue https://github.com/libsdl-org/SDL/issues/13428,
|
||||
Extra SDL_EVENT_FINGER_{UP,DOWN} with mouse button press, by
|
||||
ignoring events with fingerID of SDL_BUTTON_LEFT.
|
||||
|
||||
N.B. If SDL_HINT_MOUSE_TOUCH_EVENTS is set to 0 no touch
|
||||
events are received from the trackpad. */
|
||||
if (event->tfinger.fingerID == SDL_BUTTON_LEFT) return;
|
||||
#endif
|
||||
/* Using the number of fingers returned by SDL_GetTouchFingers
|
||||
is much more robust than counting finger up and down events.
|
||||
With counting it is easy for the counted number to be higher
|
||||
than the actual number. Unfortunately it has not been possible
|
||||
to identify a sequence of actions that reliably reproduces
|
||||
this but asserts have shown it happens often. Perhaps
|
||||
sometimes a single UP or DOWN event is received for multiple
|
||||
fingers.
|
||||
|
||||
Using the reported number is independent of how many events
|
||||
are actually received. But, and this is a big one, in the
|
||||
case of FINGER_UP SDL_GetTouchFingers reports the number of
|
||||
fingers down *before* the up event.
|
||||
|
||||
N.B. In the case of a left button press on macOS,
|
||||
SDL_GetTouchFingers reports 1 for the event that is not
|
||||
ignored.
|
||||
*/
|
||||
inTouch->numDownFingers = numDownFingersReported - 1;
|
||||
assert(inTouch->numDownFingers >= 0);
|
||||
#if (GESTURE_LOG_UP_DOWN_EVENTS)
|
||||
SDL_Log("GPE FINGER_UP, numDownFingers now = %i", inTouch->numDownFingers);
|
||||
#endif
|
||||
|
||||
if (inTouch->recording) {
|
||||
inTouch->recording = false;
|
||||
GestureDollarNormalize(&inTouch->dollarPath, path, true);
|
||||
/* PrintPath(path); */
|
||||
if (GestureRecordAll) {
|
||||
index = GestureAddDollar(NULL, path);
|
||||
for (i = 0; i < GestureNumTouches; i++) {
|
||||
GestureTouches[i].recording = 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 Gesture_ID 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 {
|
||||
inTouch->centroid.x = inTouch->centroid.y = 0.0f;
|
||||
}
|
||||
} else if (event->type == SDL_EVENT_FINGER_MOTION) {
|
||||
/* There is one FINGER_MOTION event per down finger. x,y gives
|
||||
the position of the finger whose id is in the event. */
|
||||
const float dx = event->tfinger.dx;
|
||||
const float dy = event->tfinger.dy;
|
||||
GestureDollarPath *path = &inTouch->dollarPath;
|
||||
|
||||
#if GESTURE_LOG_MOTION_EVENTS
|
||||
SDL_Log("GPE: Finger: %#" SDL_PRIx64 " MOTION: device: %#" SDL_PRIx64 ", timestamp = %"
|
||||
SDL_PRIu64 ", fingers: %i, x: %f, y: %f, press: %f, numDownFingers: %i",
|
||||
event->tfinger.fingerID, event->tfinger.touchID, event->tfinger.timestamp,
|
||||
numDownFingersReported, event->tfinger.x, event->tfinger.y, event->tfinger.pressure,
|
||||
inTouch->numDownFingers);
|
||||
#endif
|
||||
assert(numDownFingersReported > 0);
|
||||
#if SDL_PLATFORM_MACOS
|
||||
/* Workaround issue https://github.com/libsdl-org/SDL/issues/13428.
|
||||
See comment at line 753 for more details. */
|
||||
if (event->tfinger.fingerID == SDL_BUTTON_LEFT) return;
|
||||
/* SDL_GetTouchFingers reports 2 fingers down in the motion event
|
||||
for the other finger during button press. Fix up the number of
|
||||
fingers. */
|
||||
uint32_t reportedNumFingers = numDownFingersReported;
|
||||
for (uint32_t i = 0; i < reportedNumFingers; i++) {
|
||||
if (fingers[i]->id == SDL_BUTTON_LEFT) {
|
||||
numDownFingersReported--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* See comment at line 762. One case where the count reliably
|
||||
differs from reported is on iOS. When touching, dragging and
|
||||
releasing 2 fingers, iOS sends a BUTTON_DOWN and BUTTON_UP
|
||||
for one of the fingers. When the finger corresponding to the
|
||||
button is raised, it sends the BUTTON_UP followed by the
|
||||
FINGER_UP but FINGER_MOTION events can come before the
|
||||
FINGER_UP and those events have only one finger down. */
|
||||
inTouch->numDownFingers = numDownFingersReported;
|
||||
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("Centroid : (%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_EVENT_FINGER_DOWN) {
|
||||
#if (GESTURE_LOG_UP_DOWN_EVENTS)
|
||||
SDL_Log("GPE: Finger: %#" SDL_PRIx64 " DOWN. Device: %#" SDL_PRIx64 ", fingers: %i, x: %f, y: %f, press: %f",
|
||||
event->tfinger.fingerID, event->tfinger.touchID, numDownFingersReported,
|
||||
event->tfinger.x, event->tfinger.y, event->tfinger.pressure);
|
||||
#endif
|
||||
#if SDL_PLATFORM_MACOS
|
||||
/* See comment starting at line 753. */
|
||||
if (event->tfinger.fingerID == SDL_BUTTON_LEFT) return;
|
||||
#endif
|
||||
/* Using the number of fingers returned by SDL_GetTouchFingers
|
||||
is much more robust than counting finger up and down events.
|
||||
With counting it is easy for the counted number to be higher
|
||||
than the actual number. Unfortunately it has not been possible
|
||||
to identify a sequence of actions that reliably reproduces
|
||||
this. Using the reported number is independent of how many
|
||||
events are actually received. */
|
||||
inTouch->numDownFingers = numDownFingersReported;
|
||||
inTouch->centroid.x = inTouch->centroid.y = 0.0;
|
||||
for (i = 0; i < numDownFingersReported; i++) {
|
||||
inTouch->centroid.x += fingers[i]->x;
|
||||
inTouch->centroid.y += fingers[i]->y;
|
||||
}
|
||||
inTouch->centroid.x /= numDownFingersReported;
|
||||
inTouch->centroid.y /= numDownFingersReported;
|
||||
//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;
|
||||
}
|
||||
SDL_free(fingers);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* defined(SDL_GESTURE_IMPLEMENTATION) */
|
||||
#endif /* SDL version > 2 */
|
||||
#endif /* INCL_SDL_GESTURE_H */
|
||||
|
||||
/* vi: set sts=4 ts=4 sw=4 expandtab: */
|
||||
Reference in New Issue
Block a user