/* -*- tab-width: 4; -*- */ /* vi: set sw=2 ts=4 expandtab: */ /* * Copyright 2017-2020 Mark Callow, . * SPDX-License-Identifier: Apache-2.0 */ #ifndef _SWIPE_DETECTOR_H #define _SWIPE_DETECTOR_H #if defined(_WIN32) #define _USE_MATH_DEFINES #endif #include #include #include #include #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(reinterpret_cast(p)); } // Only preserves the low 32-bits of the pointer; perfect for this use. static inline void* directionToPointer(SwipeDetector::Direction d) { return reinterpret_cast(static_cast(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 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 */