4#include "../Logging/Logger.h"
13Cogs::Gestures::Gestures()
19void Cogs::Gestures::update()
21 for (
const auto& [
id, pointer] : currentPointers) {
23 if (pointer.curr.timestamp_ms - pointer.prev.timestamp_ms > 10000.0) {
24 if (pointer.state == PointerState::Pressed || (pointer.state == PointerState::Moving && pointer.curr.position == pointer.prev.position)) {
25 LOG_WARNING(logger,
"Discarding out-dated pointer id=%zu",
id);
26 pointerUp(pointer.type,
id, pointer.button, pointer.curr.position,
static_cast<double>(Timer::currentTimeMilliseconds()));
31 presentedGestures = std::move(currentGestures);
35void Cogs::Gestures::pointerDown(PointerType pointerType, PointerId pointerId, MouseButton button, glm::vec2 position,
double timestamp_ms)
37 currPointerType = pointerType;
39 PointerState& pointer = currentPointers[pointerId];
40 pointer.type = pointerType;
41 pointer.button = button;
42 pointer.init.timestamp_ms = timestamp_ms;
43 pointer.init.position = position;
44 pointer.prev = pointer.init;
45 pointer.curr = pointer.init;
46 pointer.state = PointerState::Pressed;
55 size_t pointersCount = currentPointers.size();
56 if (pointersCount == 1) {
60 currPress->id = nextGestureId();
63 currPress->press = { button, {pointer.curr.position.x, pointer.curr.position.y} };
64 currentGestures.push_back(*currPress);
69 currDrag->id = nextGestureId();
72 currDrag->drag = { button, {pointer.init.position.x, pointer.init.position.y}, {pointer.curr.position.x, pointer.curr.position.y} };
75 else if (pointersCount == 2) {
76 const PointerState& leftPointer = currentPointers.begin()->second;
77 const PointerState& rightPointer = std::next(currentPointers.begin())->second;
78 glm::vec2 midCurr(0.5f * (leftPointer.curr.position.x + rightPointer.curr.position.x),
79 0.5f * (leftPointer.curr.position.y + rightPointer.curr.position.y));
84 currPan->id = nextGestureId();
87 currPan->pan.midStartCoord = midCurr;
88 currPan->pan.midCurrCoord = midCurr;
94 currRotate->id = nextGestureId();
97 currRotate->rotate.center = midCurr;
98 currRotate->rotate.angle = 0;
104 currPinch->id = nextGestureId();
107 currPinch->pinch.center = midCurr;
108 currPinch->pinch.scale = 1;
113void Cogs::Gestures::pointerUp(PointerType pointerType, PointerId pointerId, MouseButton button, glm::vec2 position,
double timestamp_ms)
115 currPointerType = pointerType;
116 auto pointerIt = currentPointers.find(pointerId);
117 if (pointerIt == currentPointers.end()) {
118 LOG_WARNING(logger,
"Discarding pointer release event from unknown pointer.");
123 PointerState& pointer = pointerIt->second;
124 pointer.curr.timestamp_ms = timestamp_ms;
125 pointer.curr.position.x = position.x;
126 pointer.curr.position.y = position.y;
127 pointer.state = PointerState::Released;
132 float dx =
static_cast<float>(pointer.curr.position.x - pointer.init.position.x);
133 float dy =
static_cast<float>(pointer.curr.position.y - pointer.init.position.y);
134 float dt =
static_cast<float>(pointer.curr.timestamp_ms - pointer.init.timestamp_ms);
135 float distance = std::sqrt(dx * dx + dy * dy);
136 float velocity = 1000.f * distance / dt;
137 float velocityTreshold = displayScale * (pointerType == PointerType::Mouse ? mouseVelocityThreshold : touchVelocityThreshold);
140 if (swipeEnable && (velocityTreshold < velocity) && (timestamp_ms - pointer.init.timestamp_ms < swipeMaxDuration)) {
145 swipe.id = nextGestureId();
148 swipe.swipe.button = button;
149 if (std::fabs(dx) > std::fabs(dy)) {
150 swipe.swipe.direction = dx < 0 ? Gesture::Swipe::Left : Gesture::Swipe::Right;
153 swipe.swipe.direction = dy < 0 ? Gesture::Swipe::Up : Gesture::Swipe::Down;
155 swipe.swipe.startCoord = { pointer.init.position.x, pointer.init.position.y };
156 swipe.swipe.velocity = velocity;
158 currentGestures.push_back(std::move(swipe));
163 currDrag->drag.currCoord = {pointer.curr.position.x, pointer.curr.position.y};
164 currentGestures.push_back(*currDrag);
165 currDrag = std::nullopt;
169 currDrag = std::nullopt;
175 if (timestamp_ms - pointer.init.timestamp_ms < tapMaxDuration) {
180 if (doubleTapEnable && timestamp_ms - lastTapTimestamp < doubleTapTreshold) {
182 dtap.id = nextGestureId();
185 dtap.doubleTap = { button, { pointer.curr.position.x, pointer.curr.position.y } };
186 currentGestures.push_back(std::move(dtap));
189 lastTapTimestamp = timestamp_ms;
192 tap.id = nextGestureId();
195 tap.doubleTap = { button, { pointer.curr.position.x, pointer.curr.position.y } };
196 currentGestures.push_back(std::move(tap));
200 currPress->press.coord = {pointer.curr.position.x, pointer.curr.position.y};
211 currentPointers.erase(pointerId);
214void Cogs::Gestures::pointerMove(PointerType pointerType, PointerId pointerId, glm::vec2 position,
double timestamp_ms)
216 currPointerType = pointerType;
217 auto pointerIt = currentPointers.find(pointerId);
218 if (pointerIt == currentPointers.end()) {
222 g.id = nextGestureId();
225 g.hover = { { position.x, position.y } };
226 currentGestures.push_back(std::move(g));
231 PointerState& pointer = pointerIt->second;
232 pointer.prev = pointer.curr;
233 pointer.curr.timestamp_ms = timestamp_ms;
234 pointer.curr.position = position;
235 pointer.state = PointerState::Moving;
238 bool isMouse = pointerType == PointerType::Mouse;
239 if (currentPointers.size() == 1) {
241 float dx =
static_cast<float>(pointer.curr.position.x - pointer.init.position.x);
242 float dy =
static_cast<float>(pointer.curr.position.y - pointer.init.position.y);
243 float distanceSq = dx * dx + dy * dy;
244 float distanceTreshold = displayScale * (isMouse ? mouseMoveThreshold : touchMoveThreshold);
250 currentGestures.push_back(*currDrag);
254 (currDrag->drag.currCoord.x != pointer.curr.position.x || currDrag->drag.currCoord.y != pointer.curr.position.y)) {
256 currDrag->drag.currCoord = { pointer.curr.position.x, pointer.curr.position.y };
257 currentGestures.push_back(*currDrag);
261 else if (currentPointers.size() == 2) {
262 const PointerState& leftPointer = currentPointers.begin()->second;
263 const PointerState& rightPointer = std::next(currentPointers.begin())->second;
264 float midCurrX = 0.5f * (leftPointer.curr.position.x + rightPointer.curr.position.x);
265 float midCurrY = 0.5f * (leftPointer.curr.position.y + rightPointer.curr.position.y);
266 glm::vec2 midCurr(midCurrX, midCurrY);
269 float midInitX = 0.5f * (leftPointer.init.position.x + rightPointer.init.position.x);
270 float midInitY = 0.5f * (leftPointer.init.position.y + rightPointer.init.position.y);
271 float dx = midCurr.x - midInitX;
272 float dy = midCurr.y - midInitY;
273 float distanceSq = dx * dx + dy * dy;
274 float distanceTreshold = displayScale * (isMouse ? mouseMoveThreshold : touchMoveThreshold);
278 currentGestures.push_back(*currPan);
282 (currPan->pan.midCurrCoord.x != midCurr.x || currPan->pan.midCurrCoord.y != midCurr.y)) {
284 currPan->pan.midCurrCoord = { midCurr.x, midCurr.y };
285 currentGestures.push_back(*currPan);
290 glm::vec2 vi = glm::vec2(rightPointer.init.position - leftPointer.init.position);
291 glm::vec2 vc = glm::vec2(rightPointer.curr.position - leftPointer.curr.position);
292 float s = 1.f / (glm::length(vi) * glm::length(vc));
293 if (std::isfinite(s)) {
294 float angle_cos = s * glm::dot(vc, vi);
295 float angle_sin = s * glm::cross(glm::vec3(vc, 0.f), glm::vec3(vi, 0.f)).z;
296 float angle = std::atan2(angle_sin, angle_cos);
299 currentGestures.push_back(*currRotate);
304 currRotate->rotate.center = { midCurr.x, midCurr.y };
305 currRotate->rotate.angle = angle;
306 currentGestures.push_back(*currRotate);
312 glm::vec2 pi0(leftPointer.init.position);
313 glm::vec2 pi1(rightPointer.init.position);
314 glm::vec2 pc0(leftPointer.curr.position);
315 glm::vec2 pc1(rightPointer.curr.position);
316 float scale = glm::distance(pc1, pc0) / glm::distance(pi1, pi0);
317 if (std::isfinite(scale)) {
320 currentGestures.push_back(*currPinch);
325 currPinch->pinch.center = { midCurr.x, midCurr.y };
326 currPinch->pinch.scale = scale;
327 currentGestures.push_back(*currPinch);
335void Cogs::Gestures::mouseWheelMove(int32_t delta) {
338 g.id = nextGestureId();
340 g.wheel.delta = delta;
341 currentGestures.push_back(std::move(g));
344void Cogs::Gestures::reset() {
345 currentGestures = {};
348void Cogs::Gestures::endUpdatingGesture(std::optional<Gesture>& gesture,
Gesture::State state)
353 gesture->state = state;
354 currentGestures.push_back(*gesture);
356 gesture = std::nullopt;
362 return currPointerType;
PointerType getPointerType()
Get the type of pointer that was last registered.
std::unordered_map< PointerId, PointerState > currentPointers
Set of currently active pointers.
Log implementation class.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
@ Press
A long press, a press that was too long to be a tap, events on press start and end.
@ Pinch
Two-finger movement where the two fingers comes closer or farther away, see pinchThreshold,...
@ Tap
A short press, see tapMaxDuration, single-fire event.
@ Swipe
Swift pointer movement with button pressed or touch, see swipeMaxDuration, mouseVelocityThreshold and...
@ Rotate
Two-finger rotation movement where fingers are roughly equidistant, see rotateThreshold,...
@ DoubleTap
Two taps in quick succession, see doubleTapTreshold, single-fire event.
@ Wheel
Movemement of the mouse wheel, single-fire event.
@ Drag
Pointer movement with a button pressed or touch, see mouseMoveThreshold and touchMoveThreshold,...
@ Hover
Mouse pointer hovers over window without any buttons pressed, single-fire event.
@ Pan
Two-finger touch movement where fingers are roughly equidistant, see mouseMoveThreshold and touchMove...
@ Ended
Gesture ended or single-fire events, used by all gestures.
@ Changed
Gesture changed, used by: Drag, Swipe, Pan, Rotate, Pinch.
@ Started
Gesture started, used by: Press, Drag, Swipe, Pan, Rotate, Pinch.
@ Cancelled
Gesture cancelled, used by: Press, Drag, Swipe, Pan, Rotate, Pinch.