double CanvasGL::getDepthValueAtCoord(ivec2 coord, const LayerRAM* depthLayerRAM) const{ const LayerRAM* dlr = depthLayerRAM; if (!dlr) dlr = getDepthLayerRAM(); if (dlr) { auto screenDims(getScreenDimensions()); auto depthDims = dlr->getDimensions(); coord = glm::max(coord, ivec2(0)); double depthScreenRatioX = static_cast<double>(depthDims.x) / static_cast<double>(screenDims.x); double depthScreenRatioY = static_cast<double>(depthDims.y) / static_cast<double>(screenDims.y); size2_t coordDepth; coordDepth.x = static_cast<size_t>(depthScreenRatioX*static_cast<double>(coord.x)); coordDepth.y = static_cast<size_t>(depthScreenRatioY*static_cast<double>(coord.y)); depthDims -= 1; coordDepth = glm::min(coordDepth, depthDims); double depthValue = dlr->getValueAsSingleDouble(coordDepth); // Convert to normalized device coordinates return 2.0*depthValue - 1.0; } else return 1.0; }
void CanvasQt::touchEvent(QTouchEvent* touch) { size_t nTouchPoints = touch->touchPoints().size(); if (nTouchPoints < 1) { return; } QTouchEvent::TouchPoint firstPoint = touch->touchPoints()[0]; switch (firstPoint.state()) { case Qt::TouchPointPressed: gestureMode_ = nTouchPoints > 1; // Treat single touch point as mouse event break; case Qt::TouchPointMoved: gestureMode_ = nTouchPoints > 1; // Treat single touch point as mouse event break; case Qt::TouchPointStationary: gestureMode_ = nTouchPoints > 1; // Treat single touch point as mouse event break; case Qt::TouchPointReleased: gestureMode_ = false; break; default: gestureMode_ = false; } // Copy touch points std::vector<TouchPoint> touchPoints; touchPoints.reserve(touch->touchPoints().size()); // Fetch layer before loop (optimization) const LayerRAM* depthLayerRAM = getDepthLayerRAM(); vec2 screenSize(getScreenDimensions()); std::vector<int> endedTouchIds; for (auto& touchPoint : touch->touchPoints()) { vec2 screenTouchPos(touchPoint.pos().x(), touchPoint.pos().y()); vec2 prevScreenTouchPos(touchPoint.lastPos().x(), touchPoint.lastPos().y()); TouchPoint::TouchState touchState; switch (touchPoint.state()) { case Qt::TouchPointPressed: touchState = TouchPoint::TOUCH_STATE_STARTED; break; case Qt::TouchPointMoved: touchState = TouchPoint::TOUCH_STATE_UPDATED; break; case Qt::TouchPointStationary: touchState = TouchPoint::TOUCH_STATE_STATIONARY; break; case Qt::TouchPointReleased: touchState = TouchPoint::TOUCH_STATE_ENDED; break; default: touchState = TouchPoint::TOUCH_STATE_NONE; } ivec2 pixelCoord = ivec2(static_cast<int>(screenTouchPos.x), screenSize.y - 1 - static_cast<int>(screenTouchPos.y)); // Note that screenTouchPos/prevScreenTouchPos are in [0 screenDim] and does not need to be // adjusted to become centered in the pixel (+0.5) // Saving id order to preserve order of touch points at next touch event const auto lastIdIdx = std::find(lastTouchIds_.begin(), lastTouchIds_.end(), touchPoint.id()); if (lastIdIdx != lastTouchIds_.end()) { if (touchState == TouchPoint::TOUCH_STATE_ENDED){ endedTouchIds.push_back(touchPoint.id()); } } else{ lastTouchIds_.push_back(touchPoint.id()); } touchPoints.emplace_back(touchPoint.id(), screenTouchPos, (screenTouchPos) / screenSize, prevScreenTouchPos, (prevScreenTouchPos) / screenSize, touchState, getDepthValueAtCoord(pixelCoord, depthLayerRAM)); } // Ensure that the order to the touch points are the same as last touch event. // Note that the ID of a touch point is always the same but the order in which // they are given can vary. // Example // lastTouchIds_ touchPoints // 0 0 // 3 1 // 2 2 // 4 // Will result in: // touchPoints // 0 (no swap) // 2 (2 will swap with 1) // 1 auto touchIndex = 0; // Index to first unsorted element in touchPoints array for (const auto& lastTouchPointId : lastTouchIds_) { const auto touchPointIt = std::find_if(touchPoints.begin(), touchPoints.end(), [lastTouchPointId](const TouchPoint& p) { return p.getId() == lastTouchPointId; }); // Swap current location in the container with the location it was in last touch event. if (touchPointIt != touchPoints.end() && std::distance(touchPoints.begin(), touchPointIt) != touchIndex) { std::swap(*(touchPoints.begin() + touchIndex), *touchPointIt); ++touchIndex; } } for (auto& endedId : endedTouchIds) { std::vector<int>::iterator foundIdx = std::find(lastTouchIds_.begin(), lastTouchIds_.end(), endedId); if (foundIdx != lastTouchIds_.end()) lastTouchIds_.erase(foundIdx); } TouchEvent touchEvent(touchPoints, getScreenDimensions()); touch->accept(); // We need to send out touch event all the time to support one -> two finger touch switch Canvas::touchEvent(&touchEvent); // Mouse events will be triggered for touch events by Qt4 and Qt >= 5.3.0 // https://bugreports.qt.io/browse/QTBUG-40038 #if defined(USING_QT5) && (QT_VERSION < QT_VERSION_CHECK(5, 3, 0)) if(touch->touchPoints().size() == 1 && lastNumFingers_ < 2){ MouseEvent* mouseEvent = nullptr; ivec2 pos = ivec2(static_cast<int>(firstPoint.pos().x()), static_cast<int>(firstPoint.pos().y())); uvec2 screenPosInvY(static_cast<unsigned int>(pos.x), static_cast<unsigned int>(getScreenDimensions().y-1-pos.y)); double depth = getDepthValueAtCoord(screenPosInvY); switch (touchPoints.front().state()) { case TouchPoint::TOUCH_STATE_STARTED: mouseEvent = new MouseEvent(pos, MouseEvent::MOUSE_BUTTON_LEFT, MouseEvent::MOUSE_STATE_PRESS, EventConverterQt::getModifier(touch), getScreenDimensions(), depth); Canvas::mousePressEvent(mouseEvent); break; case TouchPoint::TOUCH_STATE_UPDATED: mouseEvent = new MouseEvent(pos, MouseEvent::MOUSE_BUTTON_LEFT, MouseEvent::MOUSE_STATE_MOVE, EventConverterQt::getModifier(touch), getScreenDimensions(), depth); Canvas::mouseMoveEvent(mouseEvent); break; case TouchPoint::TOUCH_STATE_STATIONARY: break; // Do not fire event while standing still. case TouchPoint::TOUCH_STATE_ENDED: mouseEvent = new MouseEvent(pos, MouseEvent::MOUSE_BUTTON_LEFT, MouseEvent::MOUSE_STATE_RELEASE, EventConverterQt::getModifier(touch), getScreenDimensions(), depth); Canvas::mouseReleaseEvent(mouseEvent); break; default: break; } delete mouseEvent; } #endif lastNumFingers_ = static_cast<int>(touch->touchPoints().size()); screenPositionNormalized_ = touchEvent.getCenterPointNormalized(); }