Mercurial > hg > orthanc-stone
changeset 457:3b4df9925db6 am-touch-events
added support for 'touch' in mouse trackers. This is still a bit hacky and we need to refactor it to make it clean. Thanks to that, Pan and zoom are available together with 2 touches
line wrap: on
line diff
--- a/Applications/Qt/QCairoWidget.cpp Wed Jan 23 13:58:51 2019 +0100 +++ b/Applications/Qt/QCairoWidget.cpp Thu Jan 24 16:42:27 2019 +0100 @@ -125,7 +125,7 @@ { OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_); - locker.GetCentralViewport().MouseDown(button, event->pos().x(), event->pos().y(), stoneModifiers); + locker.GetCentralViewport().MouseDown(button, event->pos().x(), event->pos().y(), stoneModifiers, std::vector<OrthancStone::Touch>()); } } @@ -140,7 +140,7 @@ void QCairoWidget::mouseMoveEvent(QMouseEvent* event) { OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_); - locker.GetCentralViewport().MouseMove(event->pos().x(), event->pos().y()); + locker.GetCentralViewport().MouseMove(event->pos().x(), event->pos().y(), std::vector<OrthancStone::Touch>()); }
--- a/Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp Wed Jan 23 13:58:51 2019 +0100 +++ b/Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp Thu Jan 24 16:42:27 2019 +0100 @@ -32,7 +32,8 @@ int viewportY, double x, double y, - IStatusBar* statusBar) + IStatusBar* statusBar, + const std::vector<Touch>& displayTouches) { if (button == MouseButton_Left) {
--- a/Applications/Samples/SimpleViewer/MainWidgetInteractor.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Applications/Samples/SimpleViewer/MainWidgetInteractor.h Thu Jan 24 16:42:27 2019 +0100 @@ -48,7 +48,8 @@ int viewportY, double x, double y, - IStatusBar* statusBar); + IStatusBar* statusBar, + const std::vector<Touch>& displayTouches); virtual void MouseOver(CairoContext& context, WorldSceneWidget& widget,
--- a/Applications/Samples/SimpleViewer/ThumbnailInteractor.cpp Wed Jan 23 13:58:51 2019 +0100 +++ b/Applications/Samples/SimpleViewer/ThumbnailInteractor.cpp Thu Jan 24 16:42:27 2019 +0100 @@ -32,7 +32,8 @@ int viewportY, double x, double y, - IStatusBar* statusBar) + IStatusBar* statusBar, + const std::vector<Touch>& displayTouches) { if (button == MouseButton_Left) {
--- a/Applications/Samples/SimpleViewer/ThumbnailInteractor.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Applications/Samples/SimpleViewer/ThumbnailInteractor.h Thu Jan 24 16:42:27 2019 +0100 @@ -47,7 +47,8 @@ int viewportY, double x, double y, - IStatusBar* statusBar); + IStatusBar* statusBar, + const std::vector<Touch>& displayTouches); virtual void MouseOver(CairoContext& context, WorldSceneWidget& widget,
--- a/Applications/Samples/SimpleViewerApplicationSingleFile.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Applications/Samples/SimpleViewerApplicationSingleFile.h Thu Jan 24 16:42:27 2019 +0100 @@ -66,7 +66,8 @@ int viewportY, double x, double y, - IStatusBar* statusBar) + IStatusBar* statusBar, + const std::vector<Touch>& displayTouches) { if (button == MouseButton_Left) {
--- a/Applications/Samples/SingleFrameApplication.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Applications/Samples/SingleFrameApplication.h Thu Jan 24 16:42:27 2019 +0100 @@ -60,7 +60,8 @@ int viewportY, double x, double y, - IStatusBar* statusBar) + IStatusBar* statusBar, + const std::vector<Touch>& displayTouches) { return NULL; }
--- a/Applications/Samples/SingleFrameEditorApplication.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Applications/Samples/SingleFrameEditorApplication.h Thu Jan 24 16:42:27 2019 +0100 @@ -96,7 +96,8 @@ int viewportY, double x, double y, - IStatusBar* statusBar) + IStatusBar* statusBar, + const std::vector<Touch>& displayTouches) { RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget);
--- a/Framework/Layers/CircleMeasureTracker.cpp Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Layers/CircleMeasureTracker.cpp Thu Jan 24 16:42:27 2019 +0100 @@ -91,7 +91,9 @@ void CircleMeasureTracker::MouseMove(int displayX, int displayY, double x, - double y) + double y, + const std::vector<Touch>& displayTouches, + const std::vector<Touch>& sceneTouches) { x2_ = x; y2_ = y;
--- a/Framework/Layers/CircleMeasureTracker.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Layers/CircleMeasureTracker.h Thu Jan 24 16:42:27 2019 +0100 @@ -72,6 +72,8 @@ virtual void MouseMove(int displayX, int displayY, double x, - double y); + double y, + const std::vector<Touch>& displayTouches, + const std::vector<Touch>& sceneTouches); }; }
--- a/Framework/Layers/LineMeasureTracker.cpp Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Layers/LineMeasureTracker.cpp Thu Jan 24 16:42:27 2019 +0100 @@ -87,7 +87,9 @@ void LineMeasureTracker::MouseMove(int displayX, int displayY, double x, - double y) + double y, + const std::vector<Touch>& displayTouches, + const std::vector<Touch>& sceneTouches) { x2_ = x; y2_ = y;
--- a/Framework/Layers/LineMeasureTracker.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Layers/LineMeasureTracker.h Thu Jan 24 16:42:27 2019 +0100 @@ -71,6 +71,8 @@ virtual void MouseMove(int displayX, int displayY, double x, - double y); + double y, + const std::vector<Touch>& displayTouches, + const std::vector<Touch>& sceneTouches); }; }
--- a/Framework/Radiography/RadiographyLayerCropTracker.cpp Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Radiography/RadiographyLayerCropTracker.cpp Thu Jan 24 16:42:27 2019 +0100 @@ -101,7 +101,9 @@ void RadiographyLayerCropTracker::MouseMove(int displayX, int displayY, double sceneX, - double sceneY) + double sceneY, + const std::vector<Touch>& displayTouches, + const std::vector<Touch>& sceneTouches) { if (accessor_.IsValid()) {
--- a/Framework/Radiography/RadiographyLayerCropTracker.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Radiography/RadiographyLayerCropTracker.h Thu Jan 24 16:42:27 2019 +0100 @@ -63,6 +63,8 @@ virtual void MouseMove(int displayX, int displayY, double sceneX, - double sceneY); + double sceneY, + const std::vector<Touch>& displayTouches, + const std::vector<Touch>& sceneTouches); }; }
--- a/Framework/Radiography/RadiographyLayerMoveTracker.cpp Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Radiography/RadiographyLayerMoveTracker.cpp Thu Jan 24 16:42:27 2019 +0100 @@ -97,7 +97,9 @@ void RadiographyLayerMoveTracker::MouseMove(int displayX, int displayY, double sceneX, - double sceneY) + double sceneY, + const std::vector<Touch>& displayTouches, + const std::vector<Touch>& sceneTouches) { if (accessor_.IsValid()) {
--- a/Framework/Radiography/RadiographyLayerMoveTracker.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Radiography/RadiographyLayerMoveTracker.h Thu Jan 24 16:42:27 2019 +0100 @@ -61,6 +61,8 @@ virtual void MouseMove(int displayX, int displayY, double sceneX, - double sceneY); + double sceneY, + const std::vector<Touch>& displayTouches, + const std::vector<Touch>& sceneTouches); }; }
--- a/Framework/Radiography/RadiographyLayerResizeTracker.cpp Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Radiography/RadiographyLayerResizeTracker.cpp Thu Jan 24 16:42:27 2019 +0100 @@ -159,7 +159,9 @@ void RadiographyLayerResizeTracker::MouseMove(int displayX, int displayY, double sceneX, - double sceneY) + double sceneY, + const std::vector<Touch>& displayTouches, + const std::vector<Touch>& sceneTouches) { static const double ROUND_SCALING = 0.1;
--- a/Framework/Radiography/RadiographyLayerResizeTracker.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Radiography/RadiographyLayerResizeTracker.h Thu Jan 24 16:42:27 2019 +0100 @@ -66,6 +66,8 @@ virtual void MouseMove(int displayX, int displayY, double sceneX, - double sceneY); + double sceneY, + const std::vector<Touch>& displayTouches, + const std::vector<Touch>& sceneTouches); }; }
--- a/Framework/Radiography/RadiographyLayerRotateTracker.cpp Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Radiography/RadiographyLayerRotateTracker.cpp Thu Jan 24 16:42:27 2019 +0100 @@ -132,7 +132,9 @@ void RadiographyLayerRotateTracker::MouseMove(int displayX, int displayY, double sceneX, - double sceneY) + double sceneY, + const std::vector<Touch>& displayTouches, + const std::vector<Touch>& sceneTouches) { static const double ROUND_ANGLE = 15.0 / 180.0 * boost::math::constants::pi<double>();
--- a/Framework/Radiography/RadiographyLayerRotateTracker.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Radiography/RadiographyLayerRotateTracker.h Thu Jan 24 16:42:27 2019 +0100 @@ -68,6 +68,8 @@ virtual void MouseMove(int displayX, int displayY, double sceneX, - double sceneY); + double sceneY, + const std::vector<Touch>& displayTouches, + const std::vector<Touch>& sceneTouches); }; }
--- a/Framework/Radiography/RadiographyWindowingTracker.cpp Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Radiography/RadiographyWindowingTracker.cpp Thu Jan 24 16:42:27 2019 +0100 @@ -163,7 +163,9 @@ void RadiographyWindowingTracker::MouseMove(int displayX, int displayY, double sceneX, - double sceneY) + double sceneY, + const std::vector<Touch>& displayTouches, + const std::vector<Touch>& sceneTouches) { // This follows the behavior of the Osimis Web viewer: // https://bitbucket.org/osimis/osimis-webviewer-plugin/src/master/frontend/src/app/viewport/image-plugins/windowing-viewport-tool.class.js
--- a/Framework/Radiography/RadiographyWindowingTracker.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Radiography/RadiographyWindowingTracker.h Thu Jan 24 16:42:27 2019 +0100 @@ -82,6 +82,8 @@ virtual void MouseMove(int displayX, int displayY, double sceneX, - double sceneY); + double sceneY, + const std::vector<Touch>& displayTouches, + const std::vector<Touch>& sceneTouches); }; }
--- a/Framework/Toolbox/ViewportGeometry.cpp Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Toolbox/ViewportGeometry.cpp Thu Jan 24 16:42:27 2019 +0100 @@ -121,6 +121,18 @@ } + void ViewportGeometry::MapPixelCenterToScene(std::vector<Touch>& sceneTouches /* out */, + const std::vector<Touch>& displayTouches) const + { + double sceneX, sceneY; + sceneTouches.clear(); + for (size_t t = 0; t < displayTouches.size(); t++) + { + MapPixelCenterToScene(sceneX, sceneY, displayTouches[t].x, displayTouches[t].y); + sceneTouches.push_back(Touch((float)sceneX, (float)sceneY)); + } + } + void ViewportGeometry::MapPixelCenterToScene(double& sceneX, double& sceneY, int x,
--- a/Framework/Toolbox/ViewportGeometry.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Toolbox/ViewportGeometry.h Thu Jan 24 16:42:27 2019 +0100 @@ -24,6 +24,7 @@ #include "../Viewport/CairoContext.h" #include "Extent2D.h" #include "LinearAlgebra.h" +#include "../Viewport/IMouseTracker.h" // to include "Touch" definition namespace OrthancStone { @@ -69,6 +70,9 @@ int x, int y) const; + void MapPixelCenterToScene(std::vector<Touch>& sceneTouches /* out */, + const std::vector<Touch>& displayTouches) const; + void MapSceneToDisplay(int& displayX /* out */, int& displayY /* out */, double x,
--- a/Framework/Viewport/IMouseTracker.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Viewport/IMouseTracker.h Thu Jan 24 16:42:27 2019 +0100 @@ -22,9 +22,28 @@ #pragma once #include "CairoSurface.h" +#include <vector> namespace OrthancStone { + struct Touch + { + float x; + float y; + + Touch(float x, float y) + : x(x), + y(y) + { + } + Touch() + : x(0.0f), + y(0.0f) + { + } + }; + + // this is tracking a mouse in screen coordinates/pixels unlike // the IWorldSceneMouseTracker that is tracking a mouse // in scene coordinates/mm. @@ -41,6 +60,9 @@ // Returns "true" iff. the background scene must be repainted virtual void MouseMove(int x, - int y) = 0; + int y, + const std::vector<Touch>& displayTouches) = 0; + + virtual bool IsTouchTracker() const {return false;} }; }
--- a/Framework/Viewport/IViewport.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Viewport/IViewport.h Thu Jan 24 16:42:27 2019 +0100 @@ -26,6 +26,7 @@ #include "../Messages/IObservable.h" #include <Core/Images/ImageAccessor.h> +#include "../Viewport/IMouseTracker.h" // only to get the "Touch" definition namespace OrthancStone { @@ -58,12 +59,14 @@ virtual void MouseDown(MouseButton button, int x, int y, - KeyboardModifiers modifiers) = 0; + KeyboardModifiers modifiers, + const std::vector<Touch>& touches) = 0; virtual void MouseUp() = 0; virtual void MouseMove(int x, - int y) = 0; + int y, + const std::vector<Touch>& displayTouches) = 0; virtual void MouseEnter() = 0;
--- a/Framework/Viewport/WidgetViewport.cpp Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Viewport/WidgetViewport.cpp Thu Jan 24 16:42:27 2019 +0100 @@ -137,18 +137,36 @@ return true; } + void WidgetViewport::TouchStart(const std::vector<Touch>& displayTouches) + { + MouseDown(MouseButton_Left, (int)displayTouches[0].x, (int)displayTouches[0].y, KeyboardModifiers_None, displayTouches); // one touch is equivalent to a mouse tracker without left button -> set the mouse coordinates to the first touch coordinates + } + + void WidgetViewport::TouchMove(const std::vector<Touch>& displayTouches) + { + MouseMove((int)displayTouches[0].x, (int)displayTouches[0].y, displayTouches); // one touch is equivalent to a mouse tracker without left button -> set the mouse coordinates to the first touch coordinates + } + + void WidgetViewport::TouchEnd(const std::vector<Touch>& displayTouches) + { + // note: TouchEnd is not triggered when a single touch gesture ends (it is only triggered when + // going from 2 touches to 1 touch, ...) + MouseUp(); + } void WidgetViewport::MouseDown(MouseButton button, int x, int y, - KeyboardModifiers modifiers) + KeyboardModifiers modifiers, + const std::vector<Touch>& displayTouches + ) { lastMouseX_ = x; lastMouseY_ = y; if (centralWidget_.get() != NULL) { - mouseTracker_.reset(centralWidget_->CreateMouseTracker(button, x, y, modifiers)); + mouseTracker_.reset(centralWidget_->CreateMouseTracker(button, x, y, modifiers, displayTouches)); } else { @@ -171,7 +189,8 @@ void WidgetViewport::MouseMove(int x, - int y) + int y, + const std::vector<Touch>& displayTouches) { if (centralWidget_.get() == NULL) { @@ -185,7 +204,7 @@ if (mouseTracker_.get() != NULL) { - mouseTracker_->MouseMove(x, y); + mouseTracker_->MouseMove(x, y, displayTouches); repaint = true; } else
--- a/Framework/Viewport/WidgetViewport.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Viewport/WidgetViewport.h Thu Jan 24 16:42:27 2019 +0100 @@ -59,17 +59,25 @@ virtual void MouseDown(MouseButton button, int x, int y, - KeyboardModifiers modifiers); + KeyboardModifiers modifiers, + const std::vector<Touch>& displayTouches); virtual void MouseUp(); virtual void MouseMove(int x, - int y); + int y, + const std::vector<Touch>& displayTouches); virtual void MouseEnter(); virtual void MouseLeave(); + virtual void TouchStart(const std::vector<Touch>& touches); + + virtual void TouchMove(const std::vector<Touch>& touches); + + virtual void TouchEnd(const std::vector<Touch>& touches); + virtual void MouseWheel(MouseWheelDirection direction, int x, int y,
--- a/Framework/Widgets/EmptyWidget.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Widgets/EmptyWidget.h Thu Jan 24 16:42:27 2019 +0100 @@ -76,7 +76,8 @@ virtual IMouseTracker* CreateMouseTracker(MouseButton button, int x, int y, - KeyboardModifiers modifiers) + KeyboardModifiers modifiers, + const std::vector<Touch>& touches) { return NULL; }
--- a/Framework/Widgets/IWidget.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Widgets/IWidget.h Thu Jan 24 16:42:27 2019 +0100 @@ -52,7 +52,8 @@ virtual IMouseTracker* CreateMouseTracker(MouseButton button, int x, int y, - KeyboardModifiers modifiers) = 0; + KeyboardModifiers modifiers, + const std::vector<Touch>& touches) = 0; virtual void RenderMouseOver(Orthanc::ImageAccessor& target, int x,
--- a/Framework/Widgets/IWorldSceneInteractor.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Widgets/IWorldSceneInteractor.h Thu Jan 24 16:42:27 2019 +0100 @@ -46,7 +46,8 @@ int viewportY, double x, double y, - IStatusBar* statusBar) = 0; + IStatusBar* statusBar, + const std::vector<Touch>& touches) = 0; virtual void MouseOver(CairoContext& context, WorldSceneWidget& widget,
--- a/Framework/Widgets/IWorldSceneMouseTracker.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Widgets/IWorldSceneMouseTracker.h Thu Jan 24 16:42:27 2019 +0100 @@ -22,6 +22,7 @@ #pragma once #include "../Viewport/CairoContext.h" +#include "../Viewport/IMouseTracker.h" // only to get the "Touch" definition namespace OrthancStone { @@ -46,6 +47,8 @@ virtual void MouseMove(int displayX, int displayY, double sceneX, - double sceneY) = 0; + double sceneY, + const std::vector<Touch>& displayTouches, + const std::vector<Touch>& sceneTouches) = 0; }; }
--- a/Framework/Widgets/LayoutWidget.cpp Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Widgets/LayoutWidget.cpp Thu Jan 24 16:42:27 2019 +0100 @@ -68,9 +68,16 @@ } virtual void MouseMove(int x, - int y) + int y, + const std::vector<Touch>& displayTouches) { - tracker_->MouseMove(x - left_, y - top_); + std::vector<Touch> relativeTouches; + for (size_t t = 0; t < displayTouches.size(); t++) + { + relativeTouches.push_back(Touch((int)displayTouches[t].x - left_, (int)displayTouches[t].y - top_)); + } + + tracker_->MouseMove(x - left_, y - top_, relativeTouches); } }; @@ -150,14 +157,16 @@ IMouseTracker* CreateMouseTracker(MouseButton button, int x, int y, - KeyboardModifiers modifiers) + KeyboardModifiers modifiers, + const std::vector<Touch>& touches) { if (Contains(x, y)) { IMouseTracker* tracker = widget_->CreateMouseTracker(button, x - left_, y - top_, - modifiers); + modifiers, + touches); if (tracker) { return new LayoutMouseTracker(tracker, left_, top_, width_, height_); @@ -413,11 +422,12 @@ IMouseTracker* LayoutWidget::CreateMouseTracker(MouseButton button, int x, int y, - KeyboardModifiers modifiers) + KeyboardModifiers modifiers, + const std::vector<Touch>& touches) { for (size_t i = 0; i < children_.size(); i++) { - IMouseTracker* tracker = children_[i]->CreateMouseTracker(button, x, y, modifiers); + IMouseTracker* tracker = children_[i]->CreateMouseTracker(button, x, y, modifiers, touches); if (tracker != NULL) { return tracker;
--- a/Framework/Widgets/LayoutWidget.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Widgets/LayoutWidget.h Thu Jan 24 16:42:27 2019 +0100 @@ -106,7 +106,8 @@ virtual IMouseTracker* CreateMouseTracker(MouseButton button, int x, int y, - KeyboardModifiers modifiers); + KeyboardModifiers modifiers, + const std::vector<Touch>& touches); virtual void RenderMouseOver(Orthanc::ImageAccessor& target, int x,
--- a/Framework/Widgets/PanMouseTracker.cpp Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Widgets/PanMouseTracker.cpp Thu Jan 24 16:42:27 2019 +0100 @@ -46,7 +46,9 @@ void PanMouseTracker::MouseMove(int displayX, int displayY, double x, - double y) + double y, + const std::vector<Touch>& displayTouches, + const std::vector<Touch>& sceneTouches) { ViewportGeometry view = that_.GetView(); view.SetPan(originalPanX_ + (x - downX_) * view.GetZoom(),
--- a/Framework/Widgets/PanMouseTracker.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Widgets/PanMouseTracker.h Thu Jan 24 16:42:27 2019 +0100 @@ -54,6 +54,8 @@ virtual void MouseMove(int displayX, int displayY, double x, - double y); + double y, + const std::vector<Touch>& displayTouches, + const std::vector<Touch>& sceneTouches); }; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/PanZoomMouseTracker.cpp Thu Jan 24 16:42:27 2019 +0100 @@ -0,0 +1,137 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "PanZoomMouseTracker.h" + +#include <Core/Logging.h> +#include <Core/OrthancException.h> +#include <math.h> + +namespace OrthancStone +{ + Touch GetCenter(const std::vector<Touch>& touches) + { + return Touch((touches[0].x + touches[1].x) / 2.0f, (touches[0].y + touches[1].y) / 2.0f); + } + + double GetDistance(const std::vector<Touch>& touches) + { + float dx = touches[0].x - touches[1].x; + float dy = touches[0].y - touches[1].y; + return sqrt((double)(dx * dx) + (double)(dy * dy)); + } + + + PanZoomMouseTracker::PanZoomMouseTracker(WorldSceneWidget& that, + const std::vector<Touch>& startTouches) + : that_(that), + originalZoom_(that.GetView().GetZoom()) + { + that.GetView().GetPan(originalPanX_, originalPanY_); + that.GetView().MapPixelCenterToScene(originalSceneTouches_, startTouches); + + originalDisplayCenter_ = GetCenter(startTouches); + originalSceneCenter_ = GetCenter(originalSceneTouches_); + originalDisplayDistanceBetweenTouches_ = GetDistance(startTouches); + +// printf("original Pan %f %f\n", originalPanX_, originalPanY_); +// printf("original Zoom %f \n", originalZoom_); +// printf("original distance %f \n", (float)originalDisplayDistanceBetweenTouches_); +// printf("original display touches 0 %f %f\n", startTouches[0].x, startTouches[0].y); +// printf("original display touches 1 %f %f\n", startTouches[1].x, startTouches[1].y); +// printf("original Scene center %f %f\n", originalSceneCenter_.x, originalSceneCenter_.y); + + unsigned int height = that.GetView().GetDisplayHeight(); + + if (height <= 3) + { + idle_ = true; + LOG(WARNING) << "image is too small to zoom (current height = " << height << ")"; + } + else + { + idle_ = false; + normalization_ = 1.0 / static_cast<double>(height - 1); + } + + } + + + void PanZoomMouseTracker::Render(CairoContext& context, + double zoom) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + + void PanZoomMouseTracker::MouseMove(int displayX, + int displayY, + double x, + double y, + const std::vector<Touch>& displayTouches, + const std::vector<Touch>& sceneTouches) + { + ViewportGeometry view = that_.GetView(); + +// printf("Display touches 0 %f %f\n", displayTouches[0].x, displayTouches[0].y); +// printf("Display touches 1 %f %f\n", displayTouches[1].x, displayTouches[1].y); +// printf("Scene touches 0 %f %f\n", sceneTouches[0].x, sceneTouches[0].y); +// printf("Scene touches 1 %f %f\n", sceneTouches[1].x, sceneTouches[1].y); + +// printf("zoom = %f\n", view.GetZoom()); + Touch currentSceneCenter = GetCenter(sceneTouches); + double panX = originalPanX_ + (currentSceneCenter.x - originalSceneCenter_.x) * view.GetZoom(); + double panY = originalPanY_ + (currentSceneCenter.y - originalSceneCenter_.y) * view.GetZoom(); + + view.SetPan(panX, panY); + + static const double MIN_ZOOM = -4; + static const double MAX_ZOOM = 4; + + if (!idle_) + { + double currentDistanceBetweenTouches = GetDistance(displayTouches); + + double dy = static_cast<double>(currentDistanceBetweenTouches - originalDisplayDistanceBetweenTouches_) * normalization_; // In the range [-1,1] + double z; + + // Linear interpolation from [-1, 1] to [MIN_ZOOM, MAX_ZOOM] + if (dy < -1.0) + { + z = MIN_ZOOM; + } + else if (dy > 1.0) + { + z = MAX_ZOOM; + } + else + { + z = MIN_ZOOM + (MAX_ZOOM - MIN_ZOOM) * (dy + 1.0) / 2.0; + } + + z = pow(2.0, z); + + view.SetZoom(z * originalZoom_); + } + + that_.SetView(view); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/PanZoomMouseTracker.h Thu Jan 24 16:42:27 2019 +0100 @@ -0,0 +1,65 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "WorldSceneWidget.h" + +namespace OrthancStone +{ + class PanZoomMouseTracker : public IWorldSceneMouseTracker + { + private: + WorldSceneWidget& that_; + std::vector<Touch> originalSceneTouches_; + Touch originalSceneCenter_; + Touch originalDisplayCenter_; + double originalPanX_; + double originalPanY_; + double originalZoom_; + double originalDisplayDistanceBetweenTouches_; + bool idle_; + double normalization_; + + public: + PanZoomMouseTracker(WorldSceneWidget& that, + const std::vector<Touch>& startTouches); + + virtual bool HasRender() const + { + return false; + } + + virtual void MouseUp() + { + } + + virtual void Render(CairoContext& context, + double zoom); + + virtual void MouseMove(int displayX, + int displayY, + double x, + double y, + const std::vector<Touch>& displayTouches, + const std::vector<Touch>& sceneTouches); + }; +}
--- a/Framework/Widgets/TestCairoWidget.cpp Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Widgets/TestCairoWidget.cpp Thu Jan 24 16:42:27 2019 +0100 @@ -99,7 +99,8 @@ IMouseTracker* TestCairoWidget::CreateMouseTracker(MouseButton button, int x, int y, - KeyboardModifiers modifiers) + KeyboardModifiers modifiers, + const std::vector<Touch>& touches) { UpdateStatusBar("Click"); return NULL;
--- a/Framework/Widgets/TestCairoWidget.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Widgets/TestCairoWidget.h Thu Jan 24 16:42:27 2019 +0100 @@ -51,7 +51,8 @@ virtual IMouseTracker* CreateMouseTracker(MouseButton button, int x, int y, - KeyboardModifiers modifiers); + KeyboardModifiers modifiers, + const std::vector<Touch>& touches); virtual void MouseWheel(MouseWheelDirection direction, int x,
--- a/Framework/Widgets/TestWorldSceneWidget.cpp Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Widgets/TestWorldSceneWidget.cpp Thu Jan 24 16:42:27 2019 +0100 @@ -41,7 +41,8 @@ int viewportY, double x, double y, - IStatusBar* statusBar) + IStatusBar* statusBar, + const std::vector<Touch>& touches) { if (statusBar) {
--- a/Framework/Widgets/WorldSceneWidget.cpp Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Widgets/WorldSceneWidget.cpp Thu Jan 24 16:42:27 2019 +0100 @@ -23,6 +23,7 @@ #include "PanMouseTracker.h" #include "ZoomMouseTracker.h" +#include "PanZoomMouseTracker.h" #include <Core/Logging.h> #include <Core/OrthancException.h> @@ -72,11 +73,20 @@ } virtual void MouseMove(int x, - int y) + int y, + const std::vector<Touch>& displayTouches) { double sceneX, sceneY; view_.MapPixelCenterToScene(sceneX, sceneY, x, y); - tracker_->MouseMove(x, y, sceneX, sceneY); + + std::vector<Touch> sceneTouches; + for (size_t t = 0; t < displayTouches.size(); t++) + { + double sx, sy; + view_.MapPixelCenterToScene(sx, sy, (int)displayTouches[t].x, (int)displayTouches[t].y); + sceneTouches.push_back(Touch(sx, sy)); + } + tracker_->MouseMove(x, y, sceneX, sceneY, displayTouches, sceneTouches); } }; @@ -145,7 +155,8 @@ IMouseTracker* WorldSceneWidget::CreateMouseTracker(MouseButton button, int x, int y, - KeyboardModifiers modifiers) + KeyboardModifiers modifiers, + const std::vector<Touch>& touches) { double sceneX, sceneY; view_.MapPixelCenterToScene(sceneX, sceneY, x, y); @@ -155,7 +166,7 @@ if (interactor_) { - tracker.reset(interactor_->CreateMouseTracker(*this, view_, button, modifiers, x, y, sceneX, sceneY, GetStatusBar())); + tracker.reset(interactor_->CreateMouseTracker(*this, view_, button, modifiers, x, y, sceneX, sceneY, GetStatusBar(), touches)); } if (tracker.get() != NULL) @@ -164,17 +175,26 @@ } else if (hasDefaultMouseEvents_) { - switch (button) + printf("has default mouse events\n"); + if (touches.size() == 2) { - case MouseButton_Middle: - return new SceneMouseTracker(view_, new PanMouseTracker(*this, x, y)); + printf("2 touches !\n"); + return new SceneMouseTracker(view_, new PanZoomMouseTracker(*this, touches)); + } + else + { + switch (button) + { + case MouseButton_Middle: + return new SceneMouseTracker(view_, new PanMouseTracker(*this, x, y)); - case MouseButton_Right: - return new SceneMouseTracker(view_, new ZoomMouseTracker(*this, x, y)); + case MouseButton_Right: + return new SceneMouseTracker(view_, new ZoomMouseTracker(*this, x, y)); - default: - return NULL; - } + default: + return NULL; + } + } } else {
--- a/Framework/Widgets/WorldSceneWidget.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Widgets/WorldSceneWidget.h Thu Jan 24 16:42:27 2019 +0100 @@ -88,7 +88,8 @@ virtual IMouseTracker* CreateMouseTracker(MouseButton button, int x, int y, - KeyboardModifiers modifiers); + KeyboardModifiers modifiers, + const std::vector<Touch>& touches); virtual void MouseWheel(MouseWheelDirection direction, int x,
--- a/Framework/Widgets/ZoomMouseTracker.cpp Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Widgets/ZoomMouseTracker.cpp Thu Jan 24 16:42:27 2019 +0100 @@ -61,7 +61,9 @@ void ZoomMouseTracker::MouseMove(int displayX, int displayY, double x, - double y) + double y, + const std::vector<Touch>& displayTouches, + const std::vector<Touch>& sceneTouches) { static const double MIN_ZOOM = -4; static const double MAX_ZOOM = 4;
--- a/Framework/Widgets/ZoomMouseTracker.h Wed Jan 23 13:58:51 2019 +0100 +++ b/Framework/Widgets/ZoomMouseTracker.h Thu Jan 24 16:42:27 2019 +0100 @@ -57,6 +57,8 @@ virtual void MouseMove(int displayX, int displayY, double x, - double y); + double y, + const std::vector<Touch>& displayTouches, + const std::vector<Touch>& sceneTouches); }; }
--- a/Platforms/Wasm/Defaults.cpp Wed Jan 23 13:58:51 2019 +0100 +++ b/Platforms/Wasm/Defaults.cpp Thu Jan 24 16:42:27 2019 +0100 @@ -49,7 +49,7 @@ viewports_.push_back(viewport); - printf("There are now %d viewports in C++\n", viewports_.size()); + printf("There are now %lu viewports in C++\n", viewports_.size()); viewport->SetStatusBar(statusBar_); @@ -64,7 +64,7 @@ void EMSCRIPTEN_KEEPALIVE ReleaseCppViewport(ViewportHandle viewport) { viewports_.remove_if([viewport](const std::shared_ptr<OrthancStone::WidgetViewport>& v) { return v.get() == viewport;}); - printf("There are now %d viewports in C++\n", viewports_.size()); + printf("There are now %lu viewports in C++\n", viewports_.size()); } void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle viewport) { @@ -191,7 +191,7 @@ return; // Unknown button } - viewport->MouseDown(button, x, y, OrthancStone::KeyboardModifiers_None /* TODO */); + viewport->MouseDown(button, x, y, OrthancStone::KeyboardModifiers_None, std::vector<OrthancStone::Touch>()); } @@ -222,9 +222,82 @@ int x, int y) { - viewport->MouseMove(x, y); + viewport->MouseMove(x, y, std::vector<OrthancStone::Touch>()); + } + + void GetTouchVector(std::vector<OrthancStone::Touch>& output, + int touchCount, + float x0, + float y0, + float x1, + float y1, + float x2, + float y2) + { + // TODO: it might be nice to try to pass all the x0,y0 coordinates as arrays but that's not so easy to pass array between JS and C++ + if (touchCount > 0) + { + output.push_back(OrthancStone::Touch(x0, y0)); + } + if (touchCount > 1) + { + output.push_back(OrthancStone::Touch(x1, y1)); + } + if (touchCount > 2) + { + output.push_back(OrthancStone::Touch(x2, y2)); + } + } - + + void EMSCRIPTEN_KEEPALIVE ViewportTouchStart(ViewportHandle viewport, + int touchCount, + float x0, + float y0, + float x1, + float y1, + float x2, + float y2) + { + printf("touch start with %d touches\n", touchCount); + + std::vector<OrthancStone::Touch> touches; + GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2); + viewport->TouchStart(touches); + } + + void EMSCRIPTEN_KEEPALIVE ViewportTouchMove(ViewportHandle viewport, + int touchCount, + float x0, + float y0, + float x1, + float y1, + float x2, + float y2) + { + printf("touch move with %d touches\n", touchCount); + + std::vector<OrthancStone::Touch> touches; + GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2); + viewport->TouchMove(touches); + } + + void EMSCRIPTEN_KEEPALIVE ViewportTouchEnd(ViewportHandle viewport, + int touchCount, + float x0, + float y0, + float x1, + float y1, + float x2, + float y2) + { + printf("touch end with %d touches remaining\n", touchCount); + + std::vector<OrthancStone::Touch> touches; + GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2); + viewport->TouchEnd(touches); + } + void EMSCRIPTEN_KEEPALIVE ViewportKeyPressed(ViewportHandle viewport, int key, const char* keyChar,
--- a/Platforms/Wasm/wasm-viewport.ts Wed Jan 23 13:58:51 2019 +0100 +++ b/Platforms/Wasm/wasm-viewport.ts Thu Jan 24 16:42:27 2019 +0100 @@ -60,6 +60,9 @@ private ViewportMouseLeave : Function; private ViewportMouseWheel : Function; private ViewportKeyPressed : Function; + private ViewportTouchStart : Function; + private ViewportTouchMove : Function; + private ViewportTouchEnd : Function; private pimpl_ : any; // Private pointer to the underlying WebAssembly C++ object @@ -86,6 +89,9 @@ this.ViewportMouseLeave = this.module_.cwrap('ViewportMouseLeave', null, [ 'number' ]); this.ViewportMouseWheel = this.module_.cwrap('ViewportMouseWheel', null, [ 'number', 'number', 'number', 'number', 'number' ]); this.ViewportKeyPressed = this.module_.cwrap('ViewportKeyPressed', null, [ 'number', 'number', 'string', 'number', 'number' ]); + this.ViewportTouchStart = this.module_.cwrap('ViewportTouchStart', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]); + this.ViewportTouchMove = this.module_.cwrap('ViewportTouchMove', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]); + this.ViewportTouchEnd = this.module_.cwrap('ViewportTouchEnd', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]); } public GetCppViewport() : number { @@ -192,7 +198,8 @@ this.htmlCanvas_.addEventListener('mousedown', function(event) { var x = event.pageX - this.offsetLeft; var y = event.pageY - this.offsetTop; - that.ViewportMouseDown(that.pimpl_, event.button, x, y, 0 /* TODO detect modifier keys*/); + + that.ViewportMouseDown(that.pimpl_, event.button, x, y, 0 /* TODO detect modifier keys*/); }); this.htmlCanvas_.addEventListener('mousemove', function(event) { @@ -224,12 +231,32 @@ event.preventDefault(); }); - this.htmlCanvas_.addEventListener('touchstart', function(event) { + this.htmlCanvas_.addEventListener('touchstart', function(event: TouchEvent) { // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport) event.preventDefault(); event.stopPropagation(); - that.ResetTouch(); + // TODO: find a way to pass the coordinates as an array between JS and C++ + var x0 = 0; + var y0 = 0; + var x1 = 0; + var y1 = 0; + var x2 = 0; + var y2 = 0; + if (event.targetTouches.length > 0) { + x0 = event.targetTouches[0].pageX; + y0 = event.targetTouches[0].pageY; + } + if (event.targetTouches.length > 1) { + x1 = event.targetTouches[1].pageX; + y1 = event.targetTouches[1].pageY; + } + if (event.targetTouches.length > 2) { + x2 = event.targetTouches[2].pageX; + y2 = event.targetTouches[2].pageY; + } + + that.ViewportTouchStart(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2); }); this.htmlCanvas_.addEventListener('touchend', function(event) { @@ -237,7 +264,27 @@ event.preventDefault(); event.stopPropagation(); - that.ResetTouch(); + // TODO: find a way to pass the coordinates as an array between JS and C++ + var x0 = 0; + var y0 = 0; + var x1 = 0; + var y1 = 0; + var x2 = 0; + var y2 = 0; + if (event.targetTouches.length > 0) { + x0 = event.targetTouches[0].pageX; + y0 = event.targetTouches[0].pageY; + } + if (event.targetTouches.length > 1) { + x1 = event.targetTouches[1].pageX; + y1 = event.targetTouches[1].pageY; + } + if (event.targetTouches.length > 2) { + x2 = event.targetTouches[2].pageX; + y2 = event.targetTouches[2].pageY; + } + + that.ViewportTouchEnd(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2); }); this.htmlCanvas_.addEventListener('touchmove', function(event: TouchEvent) { @@ -246,6 +293,30 @@ event.preventDefault(); event.stopPropagation(); + + // TODO: find a way to pass the coordinates as an array between JS and C++ + var x0 = 0; + var y0 = 0; + var x1 = 0; + var y1 = 0; + var x2 = 0; + var y2 = 0; + if (event.targetTouches.length > 0) { + x0 = event.targetTouches[0].pageX; + y0 = event.targetTouches[0].pageY; + } + if (event.targetTouches.length > 1) { + x1 = event.targetTouches[1].pageX; + y1 = event.targetTouches[1].pageY; + } + if (event.targetTouches.length > 2) { + x2 = event.targetTouches[2].pageX; + y2 = event.targetTouches[2].pageY; + } + + that.ViewportTouchMove(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2); + return; + // if (!that.touchGestureInProgress_) { // // starting a new gesture // that.touchCount_ = event.targetTouches.length;
--- a/Resources/CMake/OrthancStoneConfiguration.cmake Wed Jan 23 13:58:51 2019 +0100 +++ b/Resources/CMake/OrthancStoneConfiguration.cmake Thu Jan 24 16:42:27 2019 +0100 @@ -296,6 +296,7 @@ ${ORTHANC_STONE_ROOT}/Framework/Toolbox/ViewportGeometry.cpp ${ORTHANC_STONE_ROOT}/Framework/Viewport/CairoContext.cpp ${ORTHANC_STONE_ROOT}/Framework/Viewport/CairoSurface.cpp + ${ORTHANC_STONE_ROOT}/Framework/Viewport/IMouseTracker.h ${ORTHANC_STONE_ROOT}/Framework/Viewport/IStatusBar.h ${ORTHANC_STONE_ROOT}/Framework/Viewport/IViewport.h ${ORTHANC_STONE_ROOT}/Framework/Viewport/WidgetViewport.cpp @@ -309,6 +310,7 @@ ${ORTHANC_STONE_ROOT}/Framework/Widgets/IWorldSceneMouseTracker.h ${ORTHANC_STONE_ROOT}/Framework/Widgets/LayoutWidget.cpp ${ORTHANC_STONE_ROOT}/Framework/Widgets/PanMouseTracker.cpp + ${ORTHANC_STONE_ROOT}/Framework/Widgets/PanZoomMouseTracker.cpp ${ORTHANC_STONE_ROOT}/Framework/Widgets/SliceViewerWidget.cpp ${ORTHANC_STONE_ROOT}/Framework/Widgets/TestCairoWidget.cpp ${ORTHANC_STONE_ROOT}/Framework/Widgets/TestWorldSceneWidget.cpp