mirror of https://github.com/mosra/magnum.git
11 changed files with 876 additions and 3 deletions
@ -0,0 +1,361 @@ |
|||||||
|
#ifndef Magnum_Platform_Gesture_h |
||||||
|
#define Magnum_Platform_Gesture_h |
||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
2020, 2021, 2022, 2023, 2024 |
||||||
|
Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** @file
|
||||||
|
* @brief Class @ref Magnum::Platform::TwoFingerGesture |
||||||
|
* @m_since_latest |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "Magnum/Magnum.h" |
||||||
|
#include "Magnum/Math/Functions.h" /* Math::sqrt() */ |
||||||
|
#include "Magnum/Math/Complex.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace Platform { |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Two-finger gesture recognition |
||||||
|
@m_since_latest |
||||||
|
|
||||||
|
Tracks position of a primary finger and an arbitrary secondary finger based on |
||||||
|
pointer events passed to @ref pressEvent(), @ref releaseEvent() and |
||||||
|
@ref moveEvent(). Once two fingers are pressed, the instance is contextually |
||||||
|
convertible to @cpp true @ce, and @ref position(), @ref direction(), |
||||||
|
@ref relativeTranslation(), @ref relativeRotation() and @ref relativeScaling() |
||||||
|
contain gesture properties. Example usage: |
||||||
|
|
||||||
|
@snippet Platform.cpp TwoFingerGesture |
||||||
|
|
||||||
|
The interface is designed primarily for @ref Sdl2Application "*Application" |
||||||
|
subclasses and their @relativeref{Sdl2Application,PointerEvent} and |
||||||
|
@relativeref{Sdl2Application,PointerMoveEvent} instances, but works also with |
||||||
|
any other types that provide appropriate |
||||||
|
@relativeref{Sdl2Application::PointerEvent,source()}, |
||||||
|
@relativeref{Sdl2Application::PointerEvent,isPrimary()}, |
||||||
|
@relativeref{Sdl2Application::PointerEvent,id()} and @relativeref{Sdl2Application::PointerEvent,position()} members. |
||||||
|
|
||||||
|
@experimental |
||||||
|
*/ |
||||||
|
class TwoFingerGesture { |
||||||
|
public: |
||||||
|
/**
|
||||||
|
* @brief Handle a press event |
||||||
|
* |
||||||
|
* Accepts a pointer event instance such as the one coming from |
||||||
|
* @ref Platform::Sdl2Application::pointerPressEvent(). If the event |
||||||
|
* comes from a primary finger, replaces the internal state with it, |
||||||
|
* waiting for the secondary finger press to happen. If the event comes |
||||||
|
* from a secondary finger, it's used only a primary finger is known, |
||||||
|
* there's no known secondary finger ID yet. or the ID matches the |
||||||
|
* known secondary finger ID. Events that don't come from a touch |
||||||
|
* source are ignored. Returns @cpp true @ce if the event was used, |
||||||
|
* @cpp false @ce if not. |
||||||
|
* |
||||||
|
* The function doesn't modify the event in any way. If needed, it's up |
||||||
|
* to the caller to call |
||||||
|
* @relativeref{Sdl2Application::PointerEvent,setAccepted()}. |
||||||
|
*/ |
||||||
|
template<class PointerEvent> bool pressEvent(const PointerEvent& event); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle a release event |
||||||
|
* |
||||||
|
* Accepts a pointer event instance such as the one coming from |
||||||
|
* @ref Platform::Sdl2Application::pointerReleaseEvent(). If the |
||||||
|
* release comes from a primary finger whose ID is known, resets the |
||||||
|
* state for both the primary and secondary touch, waiting for a |
||||||
|
* primary finger press to happen again. Otherwise, if the release |
||||||
|
* comes from a secondary finger whose ID is known. resets just the |
||||||
|
* secondary finger state, waiting for a different secondary finger |
||||||
|
* press to happen. Events that don't come from a touch source are |
||||||
|
* ignored. Returns @cpp true @ce if the event was used, @cpp false @ce |
||||||
|
* if not. |
||||||
|
* |
||||||
|
* The function doesn't modify the event in any way. If needed, it's up |
||||||
|
* to the caller to call |
||||||
|
* @relativeref{Sdl2Application::PointerEvent,setAccepted()}. |
||||||
|
*/ |
||||||
|
template<class PointerEvent> bool releaseEvent(const PointerEvent& event); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle a move event |
||||||
|
* |
||||||
|
* Accepts a pointer move event instance such as the one coming from |
||||||
|
* @ref Platform::Sdl2Application::pointerMoveEvent(). If the move |
||||||
|
* comes from a primary finger whose ID is known or from a secondary |
||||||
|
* finger whose ID is known, updates given finger state. Events that |
||||||
|
* don't come from a touch source are ignored. Returns @cpp true @ce if |
||||||
|
* the event was used, @cpp false @ce if not. |
||||||
|
* |
||||||
|
* The function doesn't modify the event in any way. If needed, it's up |
||||||
|
* to the caller to call |
||||||
|
* @relativeref{Sdl2Application::PointerEvent,setAccepted()}. |
||||||
|
*/ |
||||||
|
template<class PointerMoveEvent> bool moveEvent(const PointerMoveEvent& event); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Count of known pressed fingers |
||||||
|
* |
||||||
|
* Is @cpp 0 @ce if @ref pressEvent() wasn't called yet or |
||||||
|
* @ref releaseEvent() happened for the primary finger, @cpp 1 @ce |
||||||
|
* if only the primary finger is pressed or a secondary finger was |
||||||
|
* released and @cpp 2 @ce if both the primary and a secondary finger |
||||||
|
* is currently pressed. |
||||||
|
* @see @ref isGesture() |
||||||
|
*/ |
||||||
|
UnsignedInt fingerCount() const { |
||||||
|
return (_primaryTouchId != NoTouchId) + |
||||||
|
(_secondaryTouchId != NoTouchId); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Whether the internal state represents a two-finger gesture |
||||||
|
* |
||||||
|
* Returns @cpp true @ce if both the primary and a secondary finger |
||||||
|
* are pressed, @cpp false @ce otherwise. Same as @ref operator bool(). |
||||||
|
*/ |
||||||
|
bool isGesture() const { |
||||||
|
return fingerCount() == 2; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Whether the internal state represents a two-finger gesture |
||||||
|
* |
||||||
|
* Returns @cpp true @ce if both the primary and a secondary finger |
||||||
|
* are pressed, @cpp false @ce otherwise. Same as @ref operator bool(). |
||||||
|
*/ |
||||||
|
explicit operator bool() const { |
||||||
|
return fingerCount() == 2; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Centroid between the two known pressed finger positions |
||||||
|
* |
||||||
|
* If only one or no fingers are pressed --- i.e., @ref isGesture() is |
||||||
|
* @cpp false @ce --- returns a NaN vector. |
||||||
|
* @see @ref direction(), @ref relativeTranslation(), |
||||||
|
* @ref Math::isNan() |
||||||
|
*/ |
||||||
|
Vector2 position() const { |
||||||
|
return (_primaryTouchPosition + _secondaryTouchPosition)*0.5f; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Direction from the center to the primary finger position |
||||||
|
* |
||||||
|
* Negate the return value to get direction from the center to the |
||||||
|
* secondary finger. If only one or no fingers are pressed --- i.e., |
||||||
|
* @ref isGesture() is @cpp false @ce --- returns a NaN vector. |
||||||
|
* @see @ref position(), @ref relativeRotation(), |
||||||
|
* @ref relativeScaling(), @ref Math::isNan() |
||||||
|
*/ |
||||||
|
Vector2 direction() const { |
||||||
|
return (_primaryTouchPosition - _secondaryTouchPosition)*0.5f; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Translation of the centroid relative to the previous finger positions |
||||||
|
* |
||||||
|
* If there was no movement since the press, returns a zero vector. If |
||||||
|
* only one or no fingers are pressed --- i.e., @ref isGesture() is |
||||||
|
* @cpp false @ce --- returns a NaN vector. |
||||||
|
* @see @ref position(), @ref relativeRotation(), |
||||||
|
* @ref relativeScaling(), @ref Math::isNan() |
||||||
|
*/ |
||||||
|
Vector2 relativeTranslation() const { |
||||||
|
return (_primaryTouchPosition - _primaryPreviousTouchPosition + |
||||||
|
_secondaryTouchPosition - _secondaryPreviousTouchPosition)*0.5f; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rotation relative to the previous finger positions |
||||||
|
* |
||||||
|
* Note that given the event coordinates are in a Y down coordinate, |
||||||
|
* positive rotation angle is clockwise. If there was no movement since |
||||||
|
* the press, returns an identity rotation. If only one or no fingers |
||||||
|
* are pressed --- i.e., @ref isGesture() is @cpp false @ce --- returns |
||||||
|
* a complex NaN. |
||||||
|
* |
||||||
|
* The function returns a @relativeref{Math,Complex} instead of an |
||||||
|
* angle as the angle would likely be converted back to a rotation |
||||||
|
* representation anyway. Use @ref Complex::toMatrix(), |
||||||
|
* @ref Complex::transformVector() or @ref Complex::angle() if a |
||||||
|
* different representation is needed. |
||||||
|
* @see @ref direction(), @ref relativeTranslation(), |
||||||
|
* @ref relativeScaling(), @ref Math::isNan() |
||||||
|
*/ |
||||||
|
Complex relativeRotation() const { |
||||||
|
/* prev * rot = cur
|
||||||
|
prev^-1 * prev * rot = prev^-1 * cur |
||||||
|
rot = prev^-1 * cur */ |
||||||
|
return Complex{(_primaryPreviousTouchPosition - _secondaryPreviousTouchPosition).normalized()}.inverted()*Complex{(_primaryTouchPosition - _secondaryTouchPosition).normalized()}; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Scaling relative to the previous finger positions |
||||||
|
* |
||||||
|
* The returned value is always positive. Values less than |
||||||
|
* @cpp 1.0f @ce are when the points are getting closer, values larger |
||||||
|
* than @cpp 1.0f @ce are when the points are getting further apart. If |
||||||
|
* there was no movement since the press, returns @cpp 1.0f @ce. If |
||||||
|
* only one or no fingers are pressed --- i.e., @ref isGesture() is |
||||||
|
* @cpp false @ce --- returns a NaN. |
||||||
|
* @see @ref direction(), @ref relativeTranslation(), |
||||||
|
* @ref relativeRotation(), @ref Math::isNan() |
||||||
|
*/ |
||||||
|
Float relativeScaling() const { |
||||||
|
return Math::sqrt( |
||||||
|
(_secondaryTouchPosition - _primaryTouchPosition).dot()/ |
||||||
|
(_secondaryPreviousTouchPosition - _primaryPreviousTouchPosition).dot()); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
struct Touch { |
||||||
|
Long id; |
||||||
|
Vector2 position; |
||||||
|
}; |
||||||
|
|
||||||
|
/* ~Long{} is -1, which may collide with actual pointer IDs (SDL uses
|
||||||
|
it to denote a mouse, for example) */ |
||||||
|
constexpr static Long NoTouchId = 1ll << 63; |
||||||
|
|
||||||
|
Long _primaryTouchId = NoTouchId; |
||||||
|
Vector2 _primaryTouchPosition{Constants::nan()}, |
||||||
|
_primaryPreviousTouchPosition{Constants::nan()}; |
||||||
|
|
||||||
|
Long _secondaryTouchId = NoTouchId; |
||||||
|
Vector2 _secondaryTouchPosition{Constants::nan()}, |
||||||
|
_secondaryPreviousTouchPosition{Constants::nan()}; |
||||||
|
}; |
||||||
|
|
||||||
|
namespace Implementation { |
||||||
|
|
||||||
|
/* Not all Application::PointerEventSource enums have a Touch member (e.g.
|
||||||
|
GlfwApplication doesn't), this makes it return false for such types, instead |
||||||
|
of failing to compile */ |
||||||
|
template<class PointerEventSource> constexpr bool isTouchPointerEventSource(PointerEventSource p, decltype(PointerEventSource::Touch)* = nullptr) { |
||||||
|
return p == PointerEventSource::Touch; |
||||||
|
} |
||||||
|
constexpr bool isTouchPointerEventSource(...) { return false; } |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
template<class PointerEvent> bool TwoFingerGesture::pressEvent(const PointerEvent& event) { |
||||||
|
/* Filter away non-touch sources. Other than that just assume it's a finger
|
||||||
|
or something equivalent, capable of multi-touch -- i.e., don't even |
||||||
|
check pointers(). */ |
||||||
|
if(!Implementation::isTouchPointerEventSource(event.source())) |
||||||
|
return false; |
||||||
|
|
||||||
|
/* If this is the primary finger, unconditionally replace the primary touch
|
||||||
|
with it, and reset everything else */ |
||||||
|
if(event.isPrimary()) { |
||||||
|
_primaryTouchId = event.id(); |
||||||
|
_primaryTouchPosition = event.position(); |
||||||
|
_primaryPreviousTouchPosition = event.position(); |
||||||
|
_secondaryTouchId = NoTouchId; |
||||||
|
_secondaryTouchPosition = Vector2{Constants::nan()}; |
||||||
|
_secondaryPreviousTouchPosition = Vector2{Constants::nan()}; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/* If this is a secondary finger and a primary finger is already known,
|
||||||
|
remember it either if it has a matching ID or there's no recorded second |
||||||
|
touch yet */ |
||||||
|
if(_primaryTouchId != NoTouchId && (_secondaryTouchId == NoTouchId || event.id() == _secondaryTouchId)) { |
||||||
|
_secondaryTouchId = event.id(); |
||||||
|
_secondaryTouchPosition = event.position(); |
||||||
|
_secondaryPreviousTouchPosition = event.position(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/* If this is a secondary finger and a primary finger is not known yet, or
|
||||||
|
if this is a secondary finger with a different ID, do nothing */ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
template<class PointerEvent> bool TwoFingerGesture::releaseEvent(const PointerEvent& event) { |
||||||
|
/* Filter away non-touch sources. Other than that just assume it's a finger
|
||||||
|
or something equivalent, capable of multi-touch -- i.e., don't even |
||||||
|
check pointers(). */ |
||||||
|
if(!Implementation::isTouchPointerEventSource(event.source())) |
||||||
|
return false; |
||||||
|
|
||||||
|
/* If the primary finger is lifted, reset everything and wait for the next
|
||||||
|
time a primary finger is pressed again */ |
||||||
|
if(event.isPrimary() && event.id() == _primaryTouchId) { |
||||||
|
_primaryTouchId = NoTouchId; |
||||||
|
_primaryTouchPosition = Vector2{Constants::nan()}; |
||||||
|
_primaryPreviousTouchPosition = Vector2{Constants::nan()}; |
||||||
|
_secondaryTouchId = NoTouchId; |
||||||
|
_secondaryTouchPosition = Vector2{Constants::nan()}; |
||||||
|
_secondaryPreviousTouchPosition = Vector2{Constants::nan()}; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/* If this is a secondary finger, reset just that one, and wait for another
|
||||||
|
secondary finger press to take up its place */ |
||||||
|
if(!event.isPrimary() && event.id() == _secondaryTouchId) { |
||||||
|
_secondaryTouchId = NoTouchId; |
||||||
|
_secondaryTouchPosition = Vector2{Constants::nan()}; |
||||||
|
_secondaryPreviousTouchPosition = Vector2{Constants::nan()}; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/* If the IDs don't match or their primary/secondary state doesn't match,
|
||||||
|
do nothing */ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
template<class PointerMoveEvent> bool TwoFingerGesture::moveEvent(const PointerMoveEvent& event) { |
||||||
|
/* Filter away non-touch sources. Other than that just assume it's a finger
|
||||||
|
or something equivalent, capable of multi-touch -- i.e., don't even |
||||||
|
check pointers(). */ |
||||||
|
if(!Implementation::isTouchPointerEventSource(event.source())) |
||||||
|
return false; |
||||||
|
|
||||||
|
/* If the event matches any of the recorded IDs, update the corresponding
|
||||||
|
values */ |
||||||
|
if(event.isPrimary() && event.id() == _primaryTouchId) { |
||||||
|
_primaryPreviousTouchPosition = _primaryTouchPosition; |
||||||
|
_primaryTouchPosition = event.position(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
if(!event.isPrimary() && event.id() == _secondaryTouchId) { |
||||||
|
_secondaryPreviousTouchPosition = _secondaryTouchPosition; |
||||||
|
_secondaryTouchPosition = event.position(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/* If the IDs don't match or their primary/secondary state doesn't match,
|
||||||
|
do nothing */ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
}} |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,444 @@ |
|||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
2020, 2021, 2022, 2023, 2024 |
||||||
|
Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <Corrade/TestSuite/Tester.h> |
||||||
|
|
||||||
|
#include "Magnum/Platform/Gesture.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace Platform { namespace Test { namespace { |
||||||
|
|
||||||
|
struct GestureTest: TestSuite::Tester { |
||||||
|
explicit GestureTest(); |
||||||
|
|
||||||
|
void twoFinger(); |
||||||
|
void twoFingerPressPrimaryAgain(); |
||||||
|
void twoFingerPressPrimaryAfterSecondary(); |
||||||
|
void twoFingerSecondaryWithoutPrimary(); |
||||||
|
void twoFingerUnknownSecondary(); |
||||||
|
void twoFingerReleasePrimary(); |
||||||
|
void twoFingerReleaseSecondary(); |
||||||
|
template<class T> void twoFingerNonTouchEvents(); |
||||||
|
}; |
||||||
|
|
||||||
|
using namespace Math::Literals; |
||||||
|
|
||||||
|
enum class PointerEventSource { |
||||||
|
Mouse = -1337, |
||||||
|
Touch = 12 |
||||||
|
}; |
||||||
|
|
||||||
|
class PointerEvent { |
||||||
|
public: |
||||||
|
explicit PointerEvent(bool primary, Long id, const Vector2& position): _primary{primary}, _id{id}, _position{position} {} |
||||||
|
|
||||||
|
PointerEventSource source() const { return PointerEventSource::Touch; } |
||||||
|
bool isPrimary() const { return _primary; } |
||||||
|
Long id() const { return _id; } |
||||||
|
Vector2 position() const { return _position; } |
||||||
|
|
||||||
|
private: |
||||||
|
bool _primary; |
||||||
|
Long _id; |
||||||
|
Vector2 _position; |
||||||
|
}; |
||||||
|
|
||||||
|
enum class PointerEventSourceMouseOnly { |
||||||
|
Mouse = -1337 |
||||||
|
}; |
||||||
|
|
||||||
|
GestureTest::GestureTest() { |
||||||
|
addTests({&GestureTest::twoFinger, |
||||||
|
&GestureTest::twoFingerPressPrimaryAgain, |
||||||
|
&GestureTest::twoFingerPressPrimaryAfterSecondary, |
||||||
|
&GestureTest::twoFingerSecondaryWithoutPrimary, |
||||||
|
&GestureTest::twoFingerUnknownSecondary, |
||||||
|
&GestureTest::twoFingerReleasePrimary, |
||||||
|
&GestureTest::twoFingerReleaseSecondary, |
||||||
|
|
||||||
|
&GestureTest::twoFingerNonTouchEvents<PointerEventSource>, |
||||||
|
&GestureTest::twoFingerNonTouchEvents<PointerEventSourceMouseOnly>}); |
||||||
|
} |
||||||
|
|
||||||
|
void GestureTest::twoFinger() { |
||||||
|
/* Initially there's nothing */ |
||||||
|
TwoFingerGesture g; |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 0); |
||||||
|
CORRADE_VERIFY(!g.isGesture()); |
||||||
|
CORRADE_VERIFY(!g); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.position()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.direction()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.relativeTranslation()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(Vector2{g.relativeRotation()}), BitVector2{3}); |
||||||
|
CORRADE_VERIFY(Math::isNan(g.relativeScaling())); |
||||||
|
|
||||||
|
/* After pressing just the primary there's no gesture yet. Using large IDs
|
||||||
|
to verify they're stored as full 64-bit numbers */ |
||||||
|
CORRADE_VERIFY(g.pressEvent(PointerEvent{true, 1ll << 37, {10.0f, 20.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 1); |
||||||
|
CORRADE_VERIFY(!g.isGesture()); |
||||||
|
CORRADE_VERIFY(!g); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.position()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.direction()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.relativeTranslation()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(Vector2{g.relativeRotation()}), BitVector2{3}); |
||||||
|
CORRADE_VERIFY(Math::isNan(g.relativeScaling())); |
||||||
|
|
||||||
|
/* With a secondary press there's a gesture. We don't check the ID in this
|
||||||
|
case, just the primary/secondary distinction, so it's fine if both are |
||||||
|
the same. */ |
||||||
|
CORRADE_VERIFY(g.pressEvent(PointerEvent{false, 1ll << 37, {20.0f, 10.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 2); |
||||||
|
CORRADE_VERIFY(g.isGesture()); |
||||||
|
CORRADE_VERIFY(g); |
||||||
|
CORRADE_COMPARE(g.position(), Vector2{15.0f}); |
||||||
|
CORRADE_COMPARE(g.direction(), (Vector2{-5.0f, 5.0f})); |
||||||
|
/* Positive direction should point to the primary event, negative to the
|
||||||
|
secondary */ |
||||||
|
CORRADE_COMPARE(g.position() + g.direction(), (Vector2{10.0f, 20.0f})); |
||||||
|
CORRADE_COMPARE(g.position() - g.direction(), (Vector2{20.0f, 10.0f})); |
||||||
|
/* No movement yet, so default values */ |
||||||
|
CORRADE_COMPARE(g.relativeTranslation(), Vector2{}); |
||||||
|
CORRADE_COMPARE(g.relativeRotation(), Complex{}); |
||||||
|
CORRADE_COMPARE(g.relativeScaling(), 1.0f); |
||||||
|
|
||||||
|
/* Move primary finger to the other side of the secondary, forming a
|
||||||
|
translation and a 180° rotation */ |
||||||
|
CORRADE_VERIFY(g.moveEvent(PointerEvent{true, 1ll << 37, {30.0f, 0.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 2); |
||||||
|
CORRADE_VERIFY(g.isGesture()); |
||||||
|
CORRADE_VERIFY(g); |
||||||
|
CORRADE_COMPARE(g.position(), (Vector2{25.0f, 5.0f})); |
||||||
|
CORRADE_COMPARE(g.direction(), (Vector2{5.0f, -5.0f})); |
||||||
|
CORRADE_COMPARE(g.relativeTranslation(), (Vector2{10.0f, -10.0f})); |
||||||
|
CORRADE_COMPARE(g.relativeRotation(), Complex::rotation(180.0_degf)); |
||||||
|
CORRADE_COMPARE(g.relativeScaling(), 1.0f); |
||||||
|
|
||||||
|
/* Move secondary finger slightly to the right, forming a counterclockwise
|
||||||
|
rotation, thus less than 180° */ |
||||||
|
CORRADE_VERIFY(g.moveEvent(PointerEvent{false, 1ll << 37, {25.0f, 10.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 2); |
||||||
|
CORRADE_VERIFY(g.isGesture()); |
||||||
|
CORRADE_VERIFY(g); |
||||||
|
CORRADE_COMPARE(g.position(), (Vector2{27.5f, 5.0f})); |
||||||
|
CORRADE_COMPARE(g.direction(), (Vector2{2.5f, -5.0f})); |
||||||
|
CORRADE_COMPARE(g.relativeTranslation(), (Vector2{12.5f, -10.0f})); |
||||||
|
CORRADE_COMPARE(g.relativeRotation(), Complex::rotation(161.565001424104_degf)); |
||||||
|
CORRADE_COMPARE(g.relativeScaling(), 0.790569f); |
||||||
|
|
||||||
|
/* Moving primary and secondary fingers back results in the same absolute
|
||||||
|
values as initially, and relative values inverted compared to above */ |
||||||
|
CORRADE_VERIFY(g.moveEvent(PointerEvent{true, 1ll << 37, {10.0f, 20.0f}})); |
||||||
|
CORRADE_VERIFY(g.moveEvent(PointerEvent{false, 1ll << 37, {20.0f, 10.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 2); |
||||||
|
CORRADE_VERIFY(g.isGesture()); |
||||||
|
CORRADE_VERIFY(g); |
||||||
|
CORRADE_COMPARE(g.position(), Vector2{15.0f}); |
||||||
|
CORRADE_COMPARE(g.direction(), (Vector2{-5.0f, 5.0f})); |
||||||
|
CORRADE_COMPARE(g.relativeTranslation(), (Vector2{-12.5f, 10.0f})); |
||||||
|
CORRADE_COMPARE(g.relativeRotation(), Complex::rotation(-161.565001424104_degf)); |
||||||
|
CORRADE_COMPARE(g.relativeScaling(), 1.0f/0.790569f); |
||||||
|
} |
||||||
|
|
||||||
|
void GestureTest::twoFingerPressPrimaryAgain() { |
||||||
|
TwoFingerGesture g; |
||||||
|
|
||||||
|
CORRADE_VERIFY(g.pressEvent(PointerEvent{true, 37, {10.0f, 20.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 1); |
||||||
|
CORRADE_VERIFY(!g.isGesture()); |
||||||
|
CORRADE_VERIFY(!g); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.position()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.direction()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.relativeTranslation()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(Vector2{g.relativeRotation()}), BitVector2{3}); |
||||||
|
CORRADE_VERIFY(Math::isNan(g.relativeScaling())); |
||||||
|
|
||||||
|
/* Another primary press replaces the original */ |
||||||
|
CORRADE_VERIFY(g.pressEvent(PointerEvent{true, 76, {10.0f, 20.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 1); |
||||||
|
CORRADE_VERIFY(!g.isGesture()); |
||||||
|
CORRADE_VERIFY(!g); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.position()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.direction()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.relativeTranslation()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(Vector2{g.relativeRotation()}), BitVector2{3}); |
||||||
|
CORRADE_VERIFY(Math::isNan(g.relativeScaling())); |
||||||
|
} |
||||||
|
|
||||||
|
void GestureTest::twoFingerPressPrimaryAfterSecondary() { |
||||||
|
TwoFingerGesture g; |
||||||
|
|
||||||
|
CORRADE_VERIFY(g.pressEvent(PointerEvent{true, 37, {10.0f, 20.0f}})); |
||||||
|
CORRADE_VERIFY(g.pressEvent(PointerEvent{false, 26, {20.0f, 10.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 2); |
||||||
|
CORRADE_VERIFY(g.isGesture()); |
||||||
|
CORRADE_VERIFY(g); |
||||||
|
CORRADE_COMPARE(g.position(), Vector2{15.0f}); |
||||||
|
CORRADE_COMPARE(g.direction(), (Vector2{-5.0f, 5.0f})); |
||||||
|
CORRADE_COMPARE(g.relativeTranslation(), Vector2{}); |
||||||
|
CORRADE_COMPARE(g.relativeRotation(), Complex{}); |
||||||
|
CORRADE_COMPARE(g.relativeScaling(), 1.0f); |
||||||
|
|
||||||
|
/* Another primary press replaces both */ |
||||||
|
CORRADE_VERIFY(g.pressEvent(PointerEvent{true, 76, {10.0f, 20.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 1); |
||||||
|
CORRADE_VERIFY(!g.isGesture()); |
||||||
|
CORRADE_VERIFY(!g); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.position()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.direction()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.relativeTranslation()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(Vector2{g.relativeRotation()}), BitVector2{3}); |
||||||
|
CORRADE_VERIFY(Math::isNan(g.relativeScaling())); |
||||||
|
} |
||||||
|
|
||||||
|
void GestureTest::twoFingerSecondaryWithoutPrimary() { |
||||||
|
TwoFingerGesture g; |
||||||
|
|
||||||
|
/* Pressing a secondary pointer without a primary being recorded first
|
||||||
|
does nothing, neither does move or release */ |
||||||
|
CORRADE_VERIFY(!g.pressEvent(PointerEvent{false, 26, {20.0f, 10.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 0); |
||||||
|
CORRADE_VERIFY(!g.isGesture()); |
||||||
|
CORRADE_VERIFY(!g); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.position()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.direction()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.relativeTranslation()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(Vector2{g.relativeRotation()}), BitVector2{3}); |
||||||
|
CORRADE_VERIFY(Math::isNan(g.relativeScaling())); |
||||||
|
|
||||||
|
CORRADE_VERIFY(!g.moveEvent(PointerEvent{false, 26, {20.0f, 10.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 0); |
||||||
|
CORRADE_VERIFY(!g.isGesture()); |
||||||
|
CORRADE_VERIFY(!g); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.position()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.direction()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.relativeTranslation()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(Vector2{g.relativeRotation()}), BitVector2{3}); |
||||||
|
CORRADE_VERIFY(Math::isNan(g.relativeScaling())); |
||||||
|
|
||||||
|
CORRADE_VERIFY(!g.releaseEvent(PointerEvent{false, 26, {20.0f, 10.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 0); |
||||||
|
CORRADE_VERIFY(!g.isGesture()); |
||||||
|
CORRADE_VERIFY(!g); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.position()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.direction()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.relativeTranslation()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(Vector2{g.relativeRotation()}), BitVector2{3}); |
||||||
|
CORRADE_VERIFY(Math::isNan(g.relativeScaling())); |
||||||
|
} |
||||||
|
|
||||||
|
void GestureTest::twoFingerUnknownSecondary() { |
||||||
|
TwoFingerGesture g; |
||||||
|
|
||||||
|
/* Using large IDs to verify they're stored as full 64-bit numbers */ |
||||||
|
CORRADE_VERIFY(g.pressEvent(PointerEvent{true, 1ll << 39, {10.0f, 20.0f}})); |
||||||
|
CORRADE_VERIFY(g.pressEvent(PointerEvent{false, 1ll << 37, {20.0f, 10.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 2); |
||||||
|
CORRADE_VERIFY(g.isGesture()); |
||||||
|
CORRADE_VERIFY(g); |
||||||
|
CORRADE_COMPARE(g.position(), Vector2{15.0f}); |
||||||
|
CORRADE_COMPARE(g.direction(), (Vector2{-5.0f, 5.0f})); |
||||||
|
CORRADE_COMPARE(g.relativeTranslation(), Vector2{}); |
||||||
|
CORRADE_COMPARE(g.relativeRotation(), Complex{}); |
||||||
|
CORRADE_COMPARE(g.relativeScaling(), 1.0f); |
||||||
|
|
||||||
|
/* Neither of these should affect the internal state in any way as it has a
|
||||||
|
different ID than the first recorded secondary press */ |
||||||
|
CORRADE_VERIFY(!g.pressEvent(PointerEvent{false, 1ll << 39, {0.0f, 0.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 2); |
||||||
|
CORRADE_VERIFY(g.isGesture()); |
||||||
|
CORRADE_VERIFY(g); |
||||||
|
CORRADE_COMPARE(g.position(), Vector2{15.0f}); |
||||||
|
CORRADE_COMPARE(g.direction(), (Vector2{-5.0f, 5.0f})); |
||||||
|
CORRADE_COMPARE(g.relativeTranslation(), Vector2{}); |
||||||
|
CORRADE_COMPARE(g.relativeRotation(), Complex{}); |
||||||
|
CORRADE_COMPARE(g.relativeScaling(), 1.0f); |
||||||
|
|
||||||
|
CORRADE_VERIFY(!g.moveEvent(PointerEvent{false, 1ll << 39, {0.0f, 0.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 2); |
||||||
|
CORRADE_VERIFY(g.isGesture()); |
||||||
|
CORRADE_VERIFY(g); |
||||||
|
CORRADE_COMPARE(g.position(), Vector2{15.0f}); |
||||||
|
CORRADE_COMPARE(g.direction(), (Vector2{-5.0f, 5.0f})); |
||||||
|
CORRADE_COMPARE(g.relativeTranslation(), Vector2{}); |
||||||
|
CORRADE_COMPARE(g.relativeRotation(), Complex{}); |
||||||
|
CORRADE_COMPARE(g.relativeScaling(), 1.0f); |
||||||
|
|
||||||
|
CORRADE_VERIFY(!g.releaseEvent(PointerEvent{false, 1ll << 39, {0.0f, 0.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 2); |
||||||
|
CORRADE_VERIFY(g.isGesture()); |
||||||
|
CORRADE_VERIFY(g); |
||||||
|
CORRADE_COMPARE(g.position(), Vector2{15.0f}); |
||||||
|
CORRADE_COMPARE(g.direction(), (Vector2{-5.0f, 5.0f})); |
||||||
|
CORRADE_COMPARE(g.relativeTranslation(), Vector2{}); |
||||||
|
CORRADE_COMPARE(g.relativeRotation(), Complex{}); |
||||||
|
CORRADE_COMPARE(g.relativeScaling(), 1.0f); |
||||||
|
} |
||||||
|
|
||||||
|
void GestureTest::twoFingerReleasePrimary() { |
||||||
|
TwoFingerGesture g; |
||||||
|
|
||||||
|
/* Using large IDs to verify they're stored as full 64-bit numbers */ |
||||||
|
CORRADE_VERIFY(g.pressEvent(PointerEvent{true, 1ll << 37, {10.0f, 20.0f}})); |
||||||
|
CORRADE_VERIFY(g.pressEvent(PointerEvent{false, 1ll << 26, {20.0f, 10.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 2); |
||||||
|
CORRADE_VERIFY(g.isGesture()); |
||||||
|
CORRADE_VERIFY(g); |
||||||
|
CORRADE_COMPARE(g.position(), Vector2{15.0f}); |
||||||
|
CORRADE_COMPARE(g.direction(), (Vector2{-5.0f, 5.0f})); |
||||||
|
CORRADE_COMPARE(g.relativeTranslation(), Vector2{}); |
||||||
|
CORRADE_COMPARE(g.relativeRotation(), Complex{}); |
||||||
|
CORRADE_COMPARE(g.relativeScaling(), 1.0f); |
||||||
|
|
||||||
|
/* Releasing a primary pointer with a different ID shouldn't affect
|
||||||
|
anything */ |
||||||
|
CORRADE_VERIFY(!g.releaseEvent(PointerEvent{true, 1ll << 26, {10.0f, 20.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 2); |
||||||
|
CORRADE_VERIFY(g.isGesture()); |
||||||
|
CORRADE_VERIFY(g); |
||||||
|
CORRADE_COMPARE(g.position(), Vector2{15.0f}); |
||||||
|
CORRADE_COMPARE(g.direction(), (Vector2{-5.0f, 5.0f})); |
||||||
|
CORRADE_COMPARE(g.relativeTranslation(), Vector2{}); |
||||||
|
CORRADE_COMPARE(g.relativeRotation(), Complex{}); |
||||||
|
CORRADE_COMPARE(g.relativeScaling(), 1.0f); |
||||||
|
|
||||||
|
/* Releasing the recorded primary pointer resets everything, it'll wait for
|
||||||
|
a new primary touch */ |
||||||
|
CORRADE_VERIFY(g.releaseEvent(PointerEvent{true, 1ll << 37, {10.0f, 20.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 0); |
||||||
|
CORRADE_VERIFY(!g.isGesture()); |
||||||
|
CORRADE_VERIFY(!g); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.position()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.direction()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.relativeTranslation()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(Vector2{g.relativeRotation()}), BitVector2{3}); |
||||||
|
CORRADE_VERIFY(Math::isNan(g.relativeScaling())); |
||||||
|
} |
||||||
|
|
||||||
|
void GestureTest::twoFingerReleaseSecondary() { |
||||||
|
TwoFingerGesture g; |
||||||
|
|
||||||
|
/* Using large IDs to verify they're stored as full 64-bit numbers */ |
||||||
|
CORRADE_VERIFY(g.pressEvent(PointerEvent{true, 1ll << 37, {10.0f, 20.0f}})); |
||||||
|
CORRADE_VERIFY(g.pressEvent(PointerEvent{false, 1ll << 26, {20.0f, 10.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 2); |
||||||
|
CORRADE_VERIFY(g.isGesture()); |
||||||
|
CORRADE_VERIFY(g); |
||||||
|
CORRADE_COMPARE(g.position(), Vector2{15.0f}); |
||||||
|
CORRADE_COMPARE(g.direction(), (Vector2{-5.0f, 5.0f})); |
||||||
|
CORRADE_COMPARE(g.relativeTranslation(), Vector2{}); |
||||||
|
CORRADE_COMPARE(g.relativeRotation(), Complex{}); |
||||||
|
CORRADE_COMPARE(g.relativeScaling(), 1.0f); |
||||||
|
|
||||||
|
/* Releasing a secondary pointer with a different ID shouldn't affect
|
||||||
|
anything */ |
||||||
|
CORRADE_VERIFY(!g.releaseEvent(PointerEvent{false, 1ll << 37, {10.0f, 20.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 2); |
||||||
|
CORRADE_VERIFY(g.isGesture()); |
||||||
|
CORRADE_VERIFY(g); |
||||||
|
CORRADE_COMPARE(g.position(), Vector2{15.0f}); |
||||||
|
CORRADE_COMPARE(g.direction(), (Vector2{-5.0f, 5.0f})); |
||||||
|
CORRADE_COMPARE(g.relativeTranslation(), Vector2{}); |
||||||
|
CORRADE_COMPARE(g.relativeRotation(), Complex{}); |
||||||
|
CORRADE_COMPARE(g.relativeScaling(), 1.0f); |
||||||
|
|
||||||
|
/* Releasing the recorded secondary pointer resets just the secondary
|
||||||
|
pointer */ |
||||||
|
CORRADE_VERIFY(g.releaseEvent(PointerEvent{false, 1ll << 26, {10.0f, 20.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 1); |
||||||
|
CORRADE_VERIFY(!g.isGesture()); |
||||||
|
CORRADE_VERIFY(!g); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.position()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.direction()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.relativeTranslation()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(Vector2{g.relativeRotation()}), BitVector2{3}); |
||||||
|
CORRADE_VERIFY(Math::isNan(g.relativeScaling())); |
||||||
|
|
||||||
|
/* Press another secondary pointer (though with the same ID), but at other
|
||||||
|
side of the primary */ |
||||||
|
CORRADE_VERIFY(g.pressEvent(PointerEvent{false, 1ll << 26, {0.0f, 30.0f}})); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 2); |
||||||
|
CORRADE_VERIFY(g.isGesture()); |
||||||
|
CORRADE_VERIFY(g); |
||||||
|
CORRADE_COMPARE(g.position(), (Vector2{5.0f, 25.0f})); |
||||||
|
CORRADE_COMPARE(g.direction(), (Vector2{5.0f, -5.0f})); |
||||||
|
/* Positive direction should point to the primary event, negative to the
|
||||||
|
secondary */ |
||||||
|
CORRADE_COMPARE(g.position() + g.direction(), (Vector2{10.0f, 20.0f})); |
||||||
|
CORRADE_COMPARE(g.position() - g.direction(), (Vector2{0.0f, 30.0f})); |
||||||
|
/* The relative values shouldn't take the previous press into account,
|
||||||
|
should be identities. */ |
||||||
|
CORRADE_COMPARE(g.relativeTranslation(), Vector2{}); |
||||||
|
CORRADE_COMPARE(g.relativeRotation(), Complex{}); |
||||||
|
CORRADE_COMPARE(g.relativeScaling(), 1.0f); |
||||||
|
} |
||||||
|
|
||||||
|
template<class T> void GestureTest::twoFingerNonTouchEvents() { |
||||||
|
setTestCaseTemplateName(std::is_same<T, PointerEventSourceMouseOnly>::value ? "PointerEventSourceMouseOnly" : "PointerEventSource"); |
||||||
|
|
||||||
|
TwoFingerGesture g; |
||||||
|
|
||||||
|
struct { |
||||||
|
T source() const { return T::Mouse; } |
||||||
|
bool isPrimary() const { return true; } |
||||||
|
Long id() const { return 0; } |
||||||
|
Vector2 position() const { return {}; } |
||||||
|
} event; |
||||||
|
|
||||||
|
/* The event should be ignored by all APIs because it's not a touch one */ |
||||||
|
CORRADE_VERIFY(!g.pressEvent(event)); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 0); |
||||||
|
CORRADE_VERIFY(!g.isGesture()); |
||||||
|
CORRADE_VERIFY(!g); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.position()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.direction()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.relativeTranslation()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(Vector2{g.relativeRotation()}), BitVector2{3}); |
||||||
|
CORRADE_VERIFY(Math::isNan(g.relativeScaling())); |
||||||
|
|
||||||
|
CORRADE_VERIFY(!g.moveEvent(event)); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 0); |
||||||
|
CORRADE_VERIFY(!g.isGesture()); |
||||||
|
CORRADE_VERIFY(!g); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.position()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.direction()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.relativeTranslation()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(Vector2{g.relativeRotation()}), BitVector2{3}); |
||||||
|
CORRADE_VERIFY(Math::isNan(g.relativeScaling())); |
||||||
|
|
||||||
|
CORRADE_VERIFY(!g.releaseEvent(event)); |
||||||
|
CORRADE_COMPARE(g.fingerCount(), 0); |
||||||
|
CORRADE_VERIFY(!g.isGesture()); |
||||||
|
CORRADE_VERIFY(!g); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.position()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.direction()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(g.relativeTranslation()), BitVector2{3}); |
||||||
|
CORRADE_COMPARE(Math::isNan(Vector2{g.relativeRotation()}), BitVector2{3}); |
||||||
|
CORRADE_VERIFY(Math::isNan(g.relativeScaling())); |
||||||
|
} |
||||||
|
|
||||||
|
}}}} |
||||||
|
|
||||||
|
CORRADE_TEST_MAIN(Magnum::Platform::Test::GestureTest) |
||||||
Loading…
Reference in new issue