# HG changeset patch # User am@osimis.io # Date 1535443291 -7200 # Node ID 300d8b8c48b3b0460df92833657a66f9273b7513 # Parent 829163c6efc1b7bf99e7d63a70face1a04166bc6 mouse tracker tuning diff -r 829163c6efc1 -r 300d8b8c48b3 Applications/Samples/SimpleViewerApplication.h --- a/Applications/Samples/SimpleViewerApplication.h Mon Aug 27 16:22:08 2018 +0200 +++ b/Applications/Samples/SimpleViewerApplication.h Tue Aug 28 10:01:31 2018 +0200 @@ -24,6 +24,7 @@ #include "SampleApplicationBase.h" #include "../../Framework/Layers/OrthancFrameLayerSource.h" +#include "../../Framework/Layers/LineMeasureTracker.h" #include "../../Framework/Widgets/LayerWidget.h" #include "../../Framework/Widgets/LayoutWidget.h" #include "../../Framework/Messages/IObserver.h" @@ -53,6 +54,7 @@ virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, const ViewportGeometry& view, MouseButton button, + KeyboardModifiers modifiers, double x, double y, IStatusBar* statusBar) @@ -83,17 +85,17 @@ char key, KeyboardModifiers modifiers, IStatusBar* statusBar) - {}; + {} }; - class Interactor : public IWorldSceneInteractor + class MainWidgetInteractor : public IWorldSceneInteractor { private: SimpleViewerApplication& application_; public: - Interactor(SimpleViewerApplication& application) : + MainWidgetInteractor(SimpleViewerApplication& application) : application_(application) { } @@ -101,10 +103,15 @@ virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, const ViewportGeometry& view, MouseButton button, + KeyboardModifiers modifiers, double x, double y, IStatusBar* statusBar) { + if (button == MouseButton_Left) + { + return new LineMeasureTracker(statusBar, dynamic_cast(widget).GetSlice(), x, y, 255, 0, 0, 10); + } return NULL; } @@ -166,7 +173,7 @@ }; - std::unique_ptr interactor_; + std::unique_ptr mainWidgetInteractor_; std::unique_ptr thumbnailInteractor_; LayoutWidget* mainLayout_; LayoutWidget* thumbnailsLayout_; @@ -248,8 +255,8 @@ smartLoader_->SetImageQuality(SliceImageQuality_FullPam); mainLayout_->SetTransmitMouseOver(true); - interactor_.reset(new Interactor(*this)); - mainWidget_->SetInteractor(*interactor_); + mainWidgetInteractor_.reset(new MainWidgetInteractor(*this)); + mainWidget_->SetInteractor(*mainWidgetInteractor_); thumbnailInteractor_.reset(new ThumbnailInteractor(*this)); } diff -r 829163c6efc1 -r 300d8b8c48b3 Framework/Viewport/IMouseTracker.h --- a/Framework/Viewport/IMouseTracker.h Mon Aug 27 16:22:08 2018 +0200 +++ b/Framework/Viewport/IMouseTracker.h Tue Aug 28 10:01:31 2018 +0200 @@ -25,6 +25,9 @@ namespace OrthancStone { + // this is tracking a mouse in screen coordinates/pixels unlike + // the IWorldSceneMouseTracker that is tracking a mouse + // in scene coordinates/mm. class IMouseTracker : public boost::noncopyable { public: diff -r 829163c6efc1 -r 300d8b8c48b3 Framework/Widgets/IWorldSceneInteractor.h --- a/Framework/Widgets/IWorldSceneInteractor.h Mon Aug 27 16:22:08 2018 +0200 +++ b/Framework/Widgets/IWorldSceneInteractor.h Tue Aug 28 10:01:31 2018 +0200 @@ -41,6 +41,7 @@ virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, const ViewportGeometry& view, MouseButton button, + KeyboardModifiers modifiers, double x, double y, IStatusBar* statusBar) = 0; diff -r 829163c6efc1 -r 300d8b8c48b3 Framework/Widgets/IWorldSceneMouseTracker.h --- a/Framework/Widgets/IWorldSceneMouseTracker.h Mon Aug 27 16:22:08 2018 +0200 +++ b/Framework/Widgets/IWorldSceneMouseTracker.h Tue Aug 28 10:01:31 2018 +0200 @@ -13,7 +13,7 @@ * 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 . **/ @@ -25,16 +25,20 @@ namespace OrthancStone { + + // this is tracking a mouse in scene coordinates/mm unlike + // the IMouseTracker that is tracking a mouse + // in screen coordinates/pixels. class IWorldSceneMouseTracker : public boost::noncopyable { public: virtual ~IWorldSceneMouseTracker() { } - + virtual void Render(CairoContext& context, double zoom) = 0; - + virtual void MouseUp() = 0; virtual void MouseMove(double x, diff -r 829163c6efc1 -r 300d8b8c48b3 Framework/Widgets/TestWorldSceneWidget.cpp --- a/Framework/Widgets/TestWorldSceneWidget.cpp Mon Aug 27 16:22:08 2018 +0200 +++ b/Framework/Widgets/TestWorldSceneWidget.cpp Tue Aug 28 10:01:31 2018 +0200 @@ -34,6 +34,7 @@ virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, const ViewportGeometry& view, MouseButton button, + KeyboardModifiers modifiers, double x, double y, IStatusBar* statusBar) diff -r 829163c6efc1 -r 300d8b8c48b3 Framework/Widgets/WorldSceneWidget.cpp --- a/Framework/Widgets/WorldSceneWidget.cpp Mon Aug 27 16:22:08 2018 +0200 +++ b/Framework/Widgets/WorldSceneWidget.cpp Tue Aug 28 10:01:31 2018 +0200 @@ -13,7 +13,7 @@ * 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 . **/ @@ -59,12 +59,16 @@ }; + // this is an adapter between a IWorldSceneMouseTracker + // that is tracking a mouse in scene coordinates/mm and + // an IMouseTracker that is tracking a mouse + // in screen coordinates/pixels. class WorldSceneWidget::SceneMouseTracker : public IMouseTracker { private: ViewportGeometry view_; std::auto_ptr tracker_; - + public: SceneMouseTracker(const ViewportGeometry& view, IWorldSceneMouseTracker* tracker) : @@ -77,17 +81,17 @@ virtual void Render(Orthanc::ImageAccessor& target) { CairoSurface surface(target); - CairoContext context(surface); + CairoContext context(surface); view_.ApplyTransform(context); tracker_->Render(context, view_.GetZoom()); } - virtual void MouseUp() + virtual void MouseUp() { tracker_->MouseUp(); } - virtual void MouseMove(int x, + virtual void MouseMove(int x, int y) { double sceneX, sceneY; @@ -97,121 +101,80 @@ }; - class WorldSceneWidget::PanMouseTracker : public IMouseTracker + WorldSceneWidget::PanMouseTracker::PanMouseTracker(WorldSceneWidget& that, + int x, + int y) : + that_(that), + downX_(x), + downY_(y) { - private: - WorldSceneWidget& that_; - double previousPanX_; - double previousPanY_; - double downX_; - double downY_; + that_.view_.GetPan(previousPanX_, previousPanY_); + } + + void WorldSceneWidget::PanMouseTracker::MouseMove(int x, int y) + { + that_.view_.SetPan(previousPanX_ + x - downX_, + previousPanY_ + y - downY_); + + that_.observers_.Apply(that_, &IWorldObserver::NotifyViewChange, that_.view_); + } - public: - PanMouseTracker(WorldSceneWidget& that, - int x, - int y) : - that_(that), - downX_(x), - downY_(y) + WorldSceneWidget::ZoomMouseTracker::ZoomMouseTracker(WorldSceneWidget& that, + int x, + int y) : + that_(that), + downX_(x), + downY_(y) + { + oldZoom_ = that_.view_.GetZoom(); + MapMouseToScene(centerX_, centerY_, that_.view_, downX_, downY_); + } + + void WorldSceneWidget::ZoomMouseTracker::MouseMove(int x, + int y) + { + static const double MIN_ZOOM = -4; + static const double MAX_ZOOM = 4; + + if (that_.view_.GetDisplayHeight() <= 3) { - that_.view_.GetPan(previousPanX_, previousPanY_); - } - - virtual void Render(Orthanc::ImageAccessor& surface) - { - } - - virtual void MouseUp() - { + return; // Cannot zoom on such a small image } - virtual void MouseMove(int x, - int y) - { - that_.view_.SetPan(previousPanX_ + x - downX_, - previousPanY_ + y - downY_); - - that_.observers_.Apply(that_, &IWorldObserver::NotifyViewChange, that_.view_); - } - }; - + double dy = (static_cast(y - downY_) / + static_cast(that_.view_.GetDisplayHeight() - 1)); // In the range [-1,1] + double z; - class WorldSceneWidget::ZoomMouseTracker : public IMouseTracker - { - private: - WorldSceneWidget& that_; - int downX_; - int downY_; - double centerX_; - double centerY_; - double oldZoom_; - - public: - ZoomMouseTracker(WorldSceneWidget& that, - int x, - int y) : - that_(that), - downX_(x), - downY_(y) + // Linear interpolation from [-1, 1] to [MIN_ZOOM, MAX_ZOOM] + if (dy < -1.0) { - oldZoom_ = that_.view_.GetZoom(); - MapMouseToScene(centerX_, centerY_, that_.view_, downX_, downY_); + z = MIN_ZOOM; } - - virtual void Render(Orthanc::ImageAccessor& surface) + else if (dy > 1.0) { + z = MAX_ZOOM; } - - virtual void MouseUp() + else { + z = MIN_ZOOM + (MAX_ZOOM - MIN_ZOOM) * (dy + 1.0) / 2.0; } - virtual void MouseMove(int x, - int y) - { - static const double MIN_ZOOM = -4; - static const double MAX_ZOOM = 4; + z = pow(2.0, z); - if (that_.view_.GetDisplayHeight() <= 3) - { - return; // Cannot zoom on such a small image - } - - double dy = (static_cast(y - downY_) / - static_cast(that_.view_.GetDisplayHeight() - 1)); // In the range [-1,1] - double z; + that_.view_.SetZoom(oldZoom_ * 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); + // Correct the pan so that the original click point is kept at + // the same location on the display + double panX, panY; + that_.view_.GetPan(panX, panY); - that_.view_.SetZoom(oldZoom_ * z); - - // Correct the pan so that the original click point is kept at - // the same location on the display - double panX, panY; - that_.view_.GetPan(panX, panY); + int tx, ty; + that_.view_.MapSceneToDisplay(tx, ty, centerX_, centerY_); + that_.view_.SetPan(panX + static_cast(downX_ - tx), + panY + static_cast(downY_ - ty)); - int tx, ty; - that_.view_.MapSceneToDisplay(tx, ty, centerX_, centerY_); - that_.view_.SetPan(panX + static_cast(downX_ - tx), - panY + static_cast(downY_ - ty)); - - that_.observers_.Apply(that_, &IWorldObserver::NotifyViewChange, that_.view_); - } - }; + that_.observers_.Apply(that_, &IWorldObserver::NotifyViewChange, that_.view_); + } bool WorldSceneWidget::RenderCairo(CairoContext& context) @@ -302,24 +265,25 @@ double sceneX, sceneY; MapMouseToScene(sceneX, sceneY, view_, x, y); + // asks the Widget Interactor to provide a mouse tracker std::auto_ptr tracker - (CreateMouseSceneTracker(view_, button, sceneX, sceneY, modifiers)); + (CreateMouseSceneTracker(view_, button, sceneX, sceneY, modifiers)); if (tracker.get() != NULL) { return new SceneMouseTracker(view_, tracker.release()); } - + //TODO: allow Interactor to create Pan & Zoom switch (button) { - case MouseButton_Middle: - return new PanMouseTracker(*this, x, y); + case MouseButton_Middle: + return new PanMouseTracker(*this, x, y); - case MouseButton_Right: - return new ZoomMouseTracker(*this, x, y); + case MouseButton_Right: + return new ZoomMouseTracker(*this, x, y); - default: - return NULL; + default: + return NULL; } } @@ -343,7 +307,7 @@ { if (interactor_) { - return interactor_->CreateMouseTracker(*this, view, button, x, y, GetStatusBar()); + return interactor_->CreateMouseTracker(*this, view, button, modifiers, x, y, GetStatusBar()); } else { @@ -355,7 +319,7 @@ void WorldSceneWidget::MouseWheel(MouseWheelDirection direction, int x, int y, - KeyboardModifiers modifiers) + KeyboardModifiers modifiers) { if (interactor_) { diff -r 829163c6efc1 -r 300d8b8c48b3 Framework/Widgets/WorldSceneWidget.h --- a/Framework/Widgets/WorldSceneWidget.h Mon Aug 27 16:22:08 2018 +0200 +++ b/Framework/Widgets/WorldSceneWidget.h Tue Aug 28 10:01:31 2018 +0200 @@ -13,7 +13,7 @@ * 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 . **/ @@ -46,12 +46,49 @@ const ViewportGeometry& view) = 0; }; + class PanMouseTracker : public IMouseTracker + { + private: + WorldSceneWidget& that_; + double previousPanX_; + double previousPanY_; + double downX_; + double downY_; + + public: + PanMouseTracker(WorldSceneWidget& that, int x, int y); + + virtual void Render(Orthanc::ImageAccessor& surface) {} + + virtual void MouseUp() {} + + virtual void MouseMove(int x, int y); + }; + + class ZoomMouseTracker : public IMouseTracker + { + private: + WorldSceneWidget& that_; + int downX_; + int downY_; + double centerX_; + double centerY_; + double oldZoom_; + + public: + ZoomMouseTracker(WorldSceneWidget& that, int x, int y); + + void Render(Orthanc::ImageAccessor& surface) {} + + virtual void MouseUp() {} + + virtual void MouseMove(int x, int y); + }; + private: struct SizeChangeFunctor; class SceneMouseTracker; - class PanMouseTracker; - class ZoomMouseTracker; typedef ObserversRegistry Observers; diff -r 829163c6efc1 -r 300d8b8c48b3 Resources/CMake/OrthancStoneConfiguration.cmake --- a/Resources/CMake/OrthancStoneConfiguration.cmake Mon Aug 27 16:22:08 2018 +0200 +++ b/Resources/CMake/OrthancStoneConfiguration.cmake Tue Aug 28 10:01:31 2018 +0200 @@ -253,6 +253,7 @@ ${ORTHANC_STONE_ROOT}/Framework/Volumes/VolumeReslicer.cpp ${ORTHANC_STONE_ROOT}/Framework/Widgets/CairoWidget.cpp ${ORTHANC_STONE_ROOT}/Framework/Widgets/EmptyWidget.cpp + ${ORTHANC_STONE_ROOT}/Framework/Widgets/IWorldSceneMouseTracker.h ${ORTHANC_STONE_ROOT}/Framework/Widgets/LayerWidget.cpp ${ORTHANC_STONE_ROOT}/Framework/Widgets/LayoutWidget.cpp ${ORTHANC_STONE_ROOT}/Framework/Widgets/TestCairoWidget.cpp diff -r 829163c6efc1 -r 300d8b8c48b3 TODO --- a/TODO Mon Aug 27 16:22:08 2018 +0200 +++ b/TODO Tue Aug 28 10:01:31 2018 +0200 @@ -9,7 +9,9 @@ * Documentation * Interface with DICOMweb * LayoutPetCtFusionApplication: fix initial view - +* Allow Interactor to create Pan/ZoomMouseTracker in IWorldSceneMouseTracker* CreateMouseTracker + (problem: PanMouseTracker is a IMouseTracker and CreateMouseTracker shall return a IWorldSceneMouseTracker). + WorldSceneWidet shall not create Pan/ZoomMouseTracker when the Interactor does not create one --------------------------------- Radiotherapy and nuclear medicine @@ -25,8 +27,8 @@ Optimizations ------------- +* Add cache in "SmartLoader" by returning a "OrthancFrameLayerSource" for a frame that has already been loaded * Tune number of loading threads in LayeredSceneWidget -* Add cache over IOrthancServices (for SDL/Qt/...) * LayoutWidget: Do not update full background if only 1 widget has changed * LayoutWidget: Threads to refresh each child * Implement binary search to speed up search for closest slice @@ -38,7 +40,6 @@ Platform-specific ----------------- -* Qt widget example * Add precompiled headers for Microsoft Visual Studio * Investigate crash in CurlOrthancConnection if using MinGW32 in Release mode