Files
2026-06-14 19:09:18 +01:00

201 lines
6.0 KiB
C++

/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2017-2020 Mark Callow, <khronos at callow dot im>.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _SWIPE_DETECTOR_H
#define _SWIPE_DETECTOR_H
#if defined(_WIN32)
#define _USE_MATH_DEFINES
#endif
#include <optional>
#include <string>
#include <math.h>
#include <SDL3/SDL.h>
#include "SDL_gesture.h"
class SwipeDetector {
public:
enum class Direction { up, down, left, right };
SwipeDetector() : gestureSwipe(false) {}
bool doEvent(SDL_Event* event);
#if defined(_MSC_VER) && !defined(__clang__)
// Not clangcl
#pragma warning(push)
#pragma warning(disable : 4311)
#pragma warning(disable : 4302)
#pragma warning(disable : 4312)
#endif
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wvoid-pointer-to-int-cast"
#endif
// These conversions allow storing a Direction in a pointer.
// Ugly. Horrible. But preferable to allocating and freeing memory
// when passing the information in user events.
static inline Direction pointerToDirection(void* p) {
return static_cast<SwipeDetector::Direction>(reinterpret_cast<long>(p));
}
// Only preserves the low 32-bits of the pointer; perfect for this use.
static inline void* directionToPointer(SwipeDetector::Direction d) {
return reinterpret_cast<void*>(static_cast<long>(d));
}
#if defined(_MSC_VER) && !defined(__clang__)
#pragma warning(pop)
#endif
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
static const Uint32 swipeGesture = 0x01;
class vector {
public:
float w;
float h;
vector() : w(0.0), h(0.0) { }
vector(float _w, float _h) : w(_w), h(_h) { }
/**
* @~English
* @internal
* @brief Find the angle between the vector and the X-axis
*
* Positive angles increase counter-clockwise from the X-axis
* which has +x to the right.
*
* @return the angle between the vector and the x axis in degrees.
*/
double getAngle() {
double rad = atan2(h, w);
return rad * 180/M_PI;
}
/**
* @~English
* @internal
* @brief Find the angle between this vector and another.
*
* Positive angles increase counter-clockwise.
*
* @return the angle between the 2 vectors in degrees.
*/
double getAngle(const vector& v2) {
// Reputed to be more accurate but in our use so far both approaches give same answer.
//double rad = atan2f(v2.h, v2.w) - SDL_atan2(h, w);
double rad = atan2f(w * v2.h - h * v2.w, w * v2.w + h * v2.h);
return rad * 180/M_PI;
}
/**
* @~English
* @internal
* @brief Find the angle between the vector and the X-axis
*
* Positive angles increase counter-clockwise from the X-axis
* which has +x to the right. Value is normalized to the range 0 to 360.
*
* @return the angle between the vector and the x axis in degrees.
*/
double getAngleNormalized() {
double rad = atan2(h, w) + M_PI;
return fmod(rad*180/M_PI + 180, 360);
}
/**
* @~English
* @internal
* @brief Return the length of the vector.
*
* @return the length of the vector.
*/
float length() { return sqrt(w * w + h * h); }
/**
* @~English
* @internal
* @brief Return the direction of the vector.
*
* @return the direction
*/
Direction getDirection() {
double angle = getAngleNormalized();
return getDirection(angle);
}
/**
* @~English
* @internal
* @brief Return a direction given an angle.
*
* Directions are defined as follows:
*
* Up: [45, 135]
* Right: [0,45] and [315, 360]
* Down: [225, 315]
* Left: [135, 225]
*
* @param angle an angle from 0 to 360°
* @return the direction of an angle
*/
static Direction getDirection(double angle){
if (inRange(angle, 45, 135)) {
return Direction::down;
} else if (inRange(angle, 0, 45) || inRange(angle, 315, 360)) {
return Direction::right;
} else if (inRange(angle, 225, 315)) {
return Direction::up;
} else {
return Direction::left;
}
}
protected:
/**
* @~English
* @internal
* @brief Check if angle falls within an interval.
*
* @param angle an angle
* @param init the initial bound
* @param end the final bound
*
* @return true if the given angle is in the interval [init, end), false
* otherwise.
*/
static bool inRange(double angle, float init, float end){
return (angle >= init) && (angle < end);
}
};
protected:
struct gestureStart {
Uint64 time;
SDL_FPoint point;
gestureStart() { time = 0; point.x = point.y = 0.0; }
} gestureStart;
std::optional<vector> lastVector;
bool gestureSwipe;
};
[[nodiscard]] inline std::string toString(SwipeDetector::Direction dir) {
switch (dir) {
case SwipeDetector::Direction::up: return "up";
case SwipeDetector::Direction::down: return "down";
case SwipeDetector::Direction::left: return "left";
case SwipeDetector::Direction::right: return "right";
// This is to hide a warning from MSVC. According to the solution given
// in https://developercommunity.visualstudio.com/t/Visual-Studio-warning-on-Strongly-typed-/96302
// it is possible to construct an enum class with any value. Thus warning.
default: return "unknown";
}
}
#endif /* _SWIPE_DETECTOR_H */