From d0492b25162cd72a43bdafd6aa577c555d6bec27 Mon Sep 17 00:00:00 2001 From: Jonathan Bosson Date: Tue, 6 Jun 2017 14:17:53 -0600 Subject: [PATCH] fix misinterpretation on roll, fixed step size in LMA gradient the projectionmatrix caused, fixed infinite spin bug on LMA failure --- modules/touch/include/TouchInteraction.h | 3 +- modules/touch/src/TouchInteraction.cpp | 86 +++++++++--------------- 2 files changed, 32 insertions(+), 57 deletions(-) diff --git a/modules/touch/include/TouchInteraction.h b/modules/touch/include/TouchInteraction.h index 95559bfb1c..066fbeaac1 100644 --- a/modules/touch/include/TouchInteraction.h +++ b/modules/touch/include/TouchInteraction.h @@ -96,7 +96,7 @@ class TouchInteraction : public properties::PropertyOwner private: bool guiMode(const std::vector& list); - void directControl(const std::vector& list, const std::vector& lastProcessed); + void directControl(const std::vector& list); void findSelectedNode(const std::vector& list); int interpretInteraction(const std::vector& list, const std::vector& lastProcessed); void computeVelocities(const std::vector& list, const std::vector& lastProcessed); @@ -122,6 +122,7 @@ class TouchInteraction : public properties::PropertyOwner properties::FloatProperty _panSpeedThreshold; properties::FloatProperty _spinSensitivity; properties::FloatProperty _inputStillThreshold; + properties::FloatProperty _centroidStillThreshold; properties::FloatProperty _interpretPan; properties::FloatProperty _slerpTime; properties::IVec2Property _guiButton; diff --git a/modules/touch/src/TouchInteraction.cpp b/modules/touch/src/TouchInteraction.cpp index 3c7927e0cd..f36b8c3ba7 100644 --- a/modules/touch/src/TouchInteraction.cpp +++ b/modules/touch/src/TouchInteraction.cpp @@ -56,11 +56,11 @@ namespace { using namespace TUIO; namespace openspace { - TouchInteraction::TouchInteraction() - : properties::PropertyOwner("TouchInteraction"), - _origin("origin", "Origin", ""), - _unitTest("Click to take a unit test", "Take a unit test saving the LM data into file", false), - _onlyPan("Toggle Panning Mode", "Toggle pan interaction on direct-manipulation three finger case (FOR FEEDBACK)", false), // temp +TouchInteraction::TouchInteraction() + : properties::PropertyOwner("TouchInteraction"), + _origin("origin", "Origin", ""), + _unitTest("Click to take a unit test", "Take a unit test saving the LM data into file", false), + _onlyPan("Toggle Panning Mode", "Toggle pan interaction on direct-manipulation three finger case (FOR FEEDBACK)", false), // temp _touchActive("TouchEvents", "True if we have a touch event", false, properties::Property::Visibility::Hidden), _reset("Default Values", "Reset all properties to default", false), _maxTapTime("Max Tap Time", "Max tap delay (in ms) for double tap", 300, 10, 1000), @@ -73,10 +73,11 @@ namespace openspace { _panSpeedThreshold("Activate pan spinning", "Threshold to activate pan spinning in direct-manipulation", 0.0005f, 0.0, 0.01), _spinSensitivity("Sensitivity of Spinning", "Sensitivity of spinning in direct-manipulation", 1.0f, 0, 2), _inputStillThreshold("Input still", "Threshold for interpreting input as still", 0.0005f, 0, 0.001), + _centroidStillThreshold("Centroid stationary", "Threshold for stationary centroid", 0.0018f, 0, 0.01), // used to void wrongly interpreted roll interactions _interpretPan("Pan delta distance", "Delta distance between fingers allowed for interpreting pan interaction", 0.015f, 0, 0.1), _slerpTime("Time to slerp", "Time to slerp in seconds to new orientation with new node picking", 1, 0, 5), _guiButton("GUI Button", "GUI button size in pixels.", glm::ivec2(32, 64), glm::ivec2(8, 16), glm::ivec2(128, 256)), - _friction("Friction", "Friction for different interactions (orbit, zoom, roll, pan)", glm::vec4(0.01, 0.02, 0.02, 0.02), glm::vec4(0.0), glm::vec4(0.2)), + _friction("Friction", "Friction for different interactions (orbit, zoom, roll, pan)", glm::vec4(0.01, 0.025, 0.02, 0.02), glm::vec4(0.0), glm::vec4(0.2)), _vel{ glm::dvec2(0.0), 0.0, 0.0, glm::dvec2(0.0) }, _sensitivity{glm::dvec2(0.0808181818181818, 0.0454545454545455), 4.0, 2.75, glm::dvec2(0.0808181818181818, 0.0454545454545455) }, @@ -100,6 +101,7 @@ namespace openspace { addProperty(_panSpeedThreshold); addProperty(_spinSensitivity); addProperty(_inputStillThreshold); + addProperty(_centroidStillThreshold); addProperty(_interpretPan); addProperty(_slerpTime); addProperty(_guiButton); @@ -132,7 +134,7 @@ void TouchInteraction::updateStateFromInput(const std::vector& list, if (!guiMode(list)) { if (_directTouchMode && _selected.size() > 0 && list.size() == _selected.size()) { - directControl(list, lastProcessed); + directControl(list); } if (_lmSuccess) { findSelectedNode(list); @@ -172,7 +174,7 @@ bool TouchInteraction::guiMode(const std::vector& list) { } // Sets _vel to update _camera according to direct-manipulation (L2 error) -void TouchInteraction::directControl(const std::vector& list, const std::vector& lastProcessed) { +void TouchInteraction::directControl(const std::vector& list) { // Reset old velocities upon new interaction _vel.orbit = glm::dvec2(0.0, 0.0); _vel.zoom = 0.0; @@ -253,25 +255,7 @@ void TouchInteraction::directControl(const std::vector& list, const double scale = log10(ptr->node->boundingSphere()); // scale value to find minimum step size h, dependant on planet size std::vector dPar(ptr->nDOF, 0.0); dPar.assign(par, par + ptr->nDOF); - /* - Paper: - p = h(Vx) - * p = position in screen space - * x = position in world space - * V = projection * worldToCam - * h = redefine to 2D = [x/w,y/w] - Mine: - p = h * projection * worldToCam * x - worldToCam contains the parameter q - - h'(x) = [1/w, 0 - 0, 1/w - 0, 0 - -x/w2, -y/w2] - J = h'(projection * worldToCam * x) * d(Vx)/d(q) - p = Jq - linear function of q even though its nonlinear - */ for (int i = 0; i < ptr->nDOF; ++i) { h = 1e-8; lastG = 1; @@ -283,12 +267,13 @@ void TouchInteraction::directControl(const std::vector& list, const // scale up to get a good initial guess value h *= scale * scale * scale; - // find optimal h + // clamp max step size to a fraction of the incoming parameter if (i == 2) { - h = std::max(std::abs(dPar.at(i)) / 1e4, h); // choose zoom step size dependant on incoming zoom parameter + double epsilon = 1e-4; + h = std::max(std::max(std::abs(dPar.at(i)), epsilon) * 0.01, h); // make sure incoming parameter is larger than 0 } else if (ptr->nDOF == 2) { - h = std::max(std::abs(dPar.at(i)) * 0.01, h); // make sure the angle step size isnt smaller than a fraction of the incoming parameter for 1 finger + h = std::max(std::abs(dPar.at(i)) * 0.001, h); } // calculate f1 with good h for finite difference @@ -309,7 +294,6 @@ void TouchInteraction::directControl(const std::vector& list, const dPar.at(i) = par[i]; } g[i] = (f1 - f0) / h; - //std::cout << "h(" << i << "): " << h << ", g(" << i << "): " << g[i] << "\n"; } if (ptr->nDOF == 2) { // normalize on 1 finger case to allow for horizontal/vertical movement for (int i = 0; i < 2; ++i) { @@ -318,11 +302,12 @@ void TouchInteraction::directControl(const std::vector& list, const } else if (ptr->nDOF == 6) { for (int i = 0; i < ptr->nDOF; ++i) { // lock to only pan and zoom on 3 finger case - g[i] = (i == 2) ? g[i] : g[i] / std::abs(g[i]); // no zoom, weird roll sometimes, otherwise only pan + g[i] = (i == 2) ? g[i] : g[i] / std::abs(g[i]); } } }; + // project back a 3D point in model view to clip space [-1,1] coordinates on the view plane auto castToNDC = [](glm::dvec3 vec, Camera& camera, SceneGraphNode* node) { glm::dvec3 posInCamSpace = glm::inverse(camera.rotationQuaternion()) * ((node->rotationMatrix() * vec) + (node->worldPosition() - camera.positionVec3()) ); @@ -333,26 +318,9 @@ void TouchInteraction::directControl(const std::vector& list, const const int nFingers = list.size(); int nDOF = std::min(nFingers * 2, 6); - std::vector par(nDOF, 0.0); // use _lastVel, - - // check if zoom direction has changed - /*double distance = std::accumulate(list.begin(), list.end(), 0.0, [&](double d, const TuioCursor& c) { - return d + c.getDistance(_centroid.x, _centroid.y); - }) / list.size(); - double lastDistance = std::accumulate(lastProcessed.begin(), lastProcessed.end(), 0.0f, [&](float d, const Point& p) { - return d + p.second.getDistance(_centroid.x, _centroid.y); - }) / lastProcessed.size();*/ - - par.at(0) = _lastVel.orbit.x; + std::vector par(nDOF, 0.0); + par.at(0) = _lastVel.orbit.x; // use _lastVel for orbit par.at(1) = _lastVel.orbit.y; - if (nDOF > 2) { - par.at(2) = _lastVel.zoom; //((distance < lastDistance && _lastVel.zoom < 0.0) || (distance > lastDistance && _lastVel.zoom > 0.0)) ? _lastVel.zoom : -_lastVel.zoom; - par.at(3) = _lastVel.roll; - if (nDOF > 4) { - par.at(4) = _lastVel.pan.x; - par.at(5) = _lastVel.pan.y; - } - } std::vector selectedPoints; std::vector screenPoints; @@ -366,8 +334,6 @@ void TouchInteraction::directControl(const std::vector& list, const void* dataPtr = reinterpret_cast(&fData); _lmSuccess = levmarq(nDOF, par.data(), nFingers, NULL, distToMinimize, gradient, dataPtr, &_lmstat); // finds best transform values and stores them in par - - // debug //std::cout << "Levmarq success after " << _lmstat.final_it << " iterations\n"; if (_lmSuccess && !_unitTest) { // if good values were found set new camera state @@ -389,6 +355,9 @@ void TouchInteraction::directControl(const std::vector& list, const _vel.roll = 0.0; _vel.pan = glm::dvec2(0.0, 0.0); } + else { // prevents touch to infinitely be active (due to windows bridge case where event doesnt get consumed) + OnScreenGUIModule::touchInput = { 1, glm::dvec2(0.0, 0.0), 1 }; + } } // Traces the touch input into the scene and finds the surface coordinates of touched planets (if occuring) @@ -448,13 +417,16 @@ void TouchInteraction::findSelectedNode(const std::vector& list) { //debugging /*for (auto it : newSelected) { - std::cout << it.node->name() << " hit with cursor " << it.id << ". Surface Coordinates: " << glm::to_string(it.coordinates) << "\n"; + std::cout << it.node->name() << " hit with cursor " << it.id << ". Surface Coordinates: " << glm::to_string(it.coordinates) + << "\n"; }*/ } // Interprets the input gesture to a specific interaction int TouchInteraction::interpretInteraction(const std::vector& list, const std::vector& lastProcessed) { //ghoul_precondition(!list.empty(), "List must not be empty"); + + glm::dvec3 lastCentroid = _centroid; _centroid.x = std::accumulate(list.begin(), list.end(), 0.0f, [](double x, const TuioCursor& c) { return x + c.getX(); }) / list.size(); _centroid.y = std::accumulate(list.begin(), list.end(), 0.0f, [](double y, const TuioCursor& c) { return y + c.getY(); }) / list.size(); @@ -514,7 +486,8 @@ int TouchInteraction::interpretInteraction(const std::vector& list, if (std::abs(dist - lastDist) / list.at(0).getMotionSpeed() < _interpretPan && list.size() == 3) { // if distance between fingers is constant we have panning return PAN; } - else if (std::abs(minDiff) < _inputStillThreshold || std::abs(rollOn) < 100.0) { // if one finger is 'still' (epsilon) and another moving, we have roll + // we have roll if one finger is still, or the total roll angles around the centroid is over _rollAngleThreshold (_centroidStillThreshold is used to void misinterpretations) + else if (std::abs(minDiff) < _inputStillThreshold || (std::abs(rollOn) < 100.0 && glm::distance(_centroid, lastCentroid) / list.size() < _centroidStillThreshold)) { return ROLL; // also interpret if angles are high } else { @@ -702,7 +675,7 @@ void TouchInteraction::unitTest() { // call update findSelectedNode(lastFrame); - //directControl(currFrame); + directControl(currFrame); // save lmstats.data into a file and clear it char buffer[32]; @@ -789,10 +762,11 @@ void TouchInteraction::resetToDefault() { _panSpeedThreshold.set(0.004f); _spinSensitivity.set(1.0f); _inputStillThreshold.set(0.0005f); + _centroidStillThreshold.set(0.0018f); _interpretPan.set(0.015f); _slerpTime.set(1.0f); _guiButton.set(glm::ivec2(32, 64)); - _friction.set(glm::vec4(0.01, 0.02, 0.02, 0.02)); + _friction.set(glm::vec4(0.01, 0.025, 0.02, 0.02)); } void TouchInteraction::tap() {