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::ivec2 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) {
59 currPress = Gesture{};
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 Gesture::Coord midCurr = {
79 static_cast<int32_t
>(0.5f * (leftPointer.curr.position.x + rightPointer.curr.position.x)),
80 static_cast<int32_t
>(0.5f * (leftPointer.curr.position.y + rightPointer.curr.position.y))
86 currPan->id = nextGestureId();
89 currPan->pan.midStartCoord = midCurr;
90 currPan->pan.midCurrCoord = midCurr;
95 currRotate = Gesture{};
96 currRotate->id = nextGestureId();
99 currRotate->rotate.center = midCurr;
100 currRotate->rotate.angle = 0;
105 currPinch = Gesture{};
106 currPinch->id = nextGestureId();
109 currPinch->pinch.center = midCurr;
110 currPinch->pinch.scale = 1;
115void Cogs::Gestures::pointerUp(PointerType pointerType, PointerId pointerId, MouseButton button, glm::ivec2 position,
double timestamp_ms)
117 currPointerType = pointerType;
118 auto pointerIt = currentPointers.find(pointerId);
119 if (pointerIt == currentPointers.end()) {
120 LOG_WARNING(logger,
"Discarding pointer release event from unknown pointer.");
125 PointerState& pointer = pointerIt->second;
126 pointer.curr.timestamp_ms = timestamp_ms;
127 pointer.curr.position.x = position.x;
128 pointer.curr.position.y = position.y;
129 pointer.state = PointerState::Released;
134 float dx =
static_cast<float>(pointer.curr.position.x - pointer.init.position.x);
135 float dy =
static_cast<float>(pointer.curr.position.y - pointer.init.position.y);
136 float dt =
static_cast<float>(pointer.curr.timestamp_ms - pointer.init.timestamp_ms);
137 float distance = std::sqrt(dx * dx + dy * dy);
138 float velocity = 1000.f * distance / dt;
139 float velocityTreshold = displayScale * (pointerType == PointerType::Mouse ? mouseVelocityThreshold : touchVelocityThreshold);
142 if (swipeEnable && (velocityTreshold < velocity) && (timestamp_ms - pointer.init.timestamp_ms < swipeMaxDuration)) {
147 swipe.id = nextGestureId();
150 swipe.swipe.button = button;
151 if (std::fabs(dx) > std::fabs(dy)) {
152 swipe.swipe.direction = dx < 0 ? Gesture::Swipe::Left : Gesture::Swipe::Right;
155 swipe.swipe.direction = dy < 0 ? Gesture::Swipe::Up : Gesture::Swipe::Down;
157 swipe.swipe.startCoord = { pointer.init.position.x, pointer.init.position.y };
158 swipe.swipe.velocity = velocity;
160 currentGestures.push_back(std::move(swipe));
165 currDrag->drag.currCoord = {pointer.curr.position.x, pointer.curr.position.y};
166 currentGestures.push_back(*currDrag);
167 currDrag = std::nullopt;
171 currDrag = std::nullopt;
177 if (timestamp_ms - pointer.init.timestamp_ms < tapMaxDuration) {
182 if (doubleTapEnable && timestamp_ms - lastTapTimestamp < doubleTapTreshold) {
184 dtap.id = nextGestureId();
187 dtap.doubleTap = { button, { pointer.curr.position.x, pointer.curr.position.y } };
188 currentGestures.push_back(std::move(dtap));
191 lastTapTimestamp = timestamp_ms;
194 tap.id = nextGestureId();
197 tap.doubleTap = { button, { pointer.curr.position.x, pointer.curr.position.y } };
198 currentGestures.push_back(std::move(tap));
202 currPress->press.coord = {pointer.curr.position.x, pointer.curr.position.y};
213 currentPointers.erase(pointerId);
216void Cogs::Gestures::pointerMove(PointerType pointerType, PointerId pointerId, glm::ivec2 position,
double timestamp_ms)
218 currPointerType = pointerType;
219 auto pointerIt = currentPointers.find(pointerId);
220 if (pointerIt == currentPointers.end()) {
224 g.id = nextGestureId();
227 g.hover = { { position.x, position.y } };
228 currentGestures.push_back(std::move(g));
233 PointerState& pointer = pointerIt->second;
234 pointer.prev = pointer.curr;
235 pointer.curr.timestamp_ms = timestamp_ms;
236 pointer.curr.position = position;
237 pointer.state = PointerState::Moving;
240 bool isMouse = pointerType == PointerType::Mouse;
241 if (currentPointers.size() == 1) {
243 float dx =
static_cast<float>(pointer.curr.position.x - pointer.init.position.x);
244 float dy =
static_cast<float>(pointer.curr.position.y - pointer.init.position.y);
245 float distanceSq = dx * dx + dy * dy;
246 float distanceTreshold = displayScale * (isMouse ? mouseMoveThreshold : touchMoveThreshold);
252 currentGestures.push_back(*currDrag);
256 (currDrag->drag.currCoord.x != pointer.curr.position.x || currDrag->drag.currCoord.y != pointer.curr.position.y)) {
258 currDrag->drag.currCoord = { pointer.curr.position.x, pointer.curr.position.y };
259 currentGestures.push_back(*currDrag);
263 else if (currentPointers.size() == 2) {
264 const PointerState& leftPointer = currentPointers.begin()->second;
265 const PointerState& rightPointer = std::next(currentPointers.begin())->second;
266 float midCurrX = 0.5f * (leftPointer.curr.position.x + rightPointer.curr.position.x);
267 float midCurrY = 0.5f * (leftPointer.curr.position.y + rightPointer.curr.position.y);
268 Gesture::Coord midCurr = {
static_cast<int32_t
>(midCurrX),
static_cast<int32_t
>(midCurrY) };
271 float midInitX = 0.5f * (leftPointer.init.position.x + rightPointer.init.position.x);
272 float midInitY = 0.5f * (leftPointer.init.position.y + rightPointer.init.position.y);
273 float dx = midCurr.x - midInitX;
274 float dy = midCurr.y - midInitY;
275 float distanceSq = dx * dx + dy * dy;
276 float distanceTreshold = displayScale * (isMouse ? mouseMoveThreshold : touchMoveThreshold);
280 currentGestures.push_back(*currPan);
284 (currPan->pan.midCurrCoord.x != midCurr.x || currPan->pan.midCurrCoord.y != midCurr.y)) {
286 currPan->pan.midCurrCoord = { midCurr.x, midCurr.y };
287 currentGestures.push_back(*currPan);
292 glm::vec2 vi = glm::vec2(rightPointer.init.position - leftPointer.init.position);
293 glm::vec2 vc = glm::vec2(rightPointer.curr.position - leftPointer.curr.position);
294 float s = 1.f / (glm::length(vi) * glm::length(vc));
295 if (std::isfinite(s)) {
296 float angle_cos = s * glm::dot(vc, vi);
297 float angle_sin = s * glm::cross(glm::vec3(vc, 0.f), glm::vec3(vi, 0.f)).z;
298 float angle = std::atan2(angle_sin, angle_cos);
301 currentGestures.push_back(*currRotate);
306 currRotate->rotate.center = { midCurr.x, midCurr.y };
307 currRotate->rotate.angle = angle;
308 currentGestures.push_back(*currRotate);
314 glm::vec2 pi0(leftPointer.init.position);
315 glm::vec2 pi1(rightPointer.init.position);
316 glm::vec2 pc0(leftPointer.curr.position);
317 glm::vec2 pc1(rightPointer.curr.position);
318 float scale = glm::distance(pc1, pc0) / glm::distance(pi1, pi0);
319 if (std::isfinite(scale)) {
322 currentGestures.push_back(*currPinch);
327 currPinch->pinch.center = { midCurr.x, midCurr.y };
328 currPinch->pinch.scale = scale;
329 currentGestures.push_back(*currPinch);
337void Cogs::Gestures::mouseWheelMove(int32_t delta) {
340 g.id = nextGestureId();
342 g.wheel.delta = delta;
343 currentGestures.push_back(std::move(g));
346void Cogs::Gestures::reset() {
347 currentGestures = {};
350void Cogs::Gestures::endUpdatingGesture(std::optional<Gesture>& gesture,
Gesture::State state)
355 gesture->state = state;
356 currentGestures.push_back(*gesture);
358 gesture = std::nullopt;
364 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.