Mercurial > hg > orthanc-stone
changeset 2240:515bb5c5c8db
added support for pinch/zoom and panning gestures
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 23 May 2025 18:40:26 +0200 (2 weeks ago) |
parents | 66b79075ac58 |
children | 4cb5be924923 |
files | Applications/StoneWebViewer/NEWS Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyViewport.cpp OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyViewport.h OrthancStone/Sources/Scene2D/PinchZoomTracker.cpp OrthancStone/Sources/Scene2D/PinchZoomTracker.h OrthancStone/Sources/Toolbox/AffineTransform2D.h OrthancStone/Sources/Viewport/DefaultViewportInteractor.cpp |
diffstat | 9 files changed, 289 insertions(+), 2 deletions(-) [+] |
line wrap: on
line diff
--- a/Applications/StoneWebViewer/NEWS Sun Apr 27 20:55:48 2025 +0200 +++ b/Applications/StoneWebViewer/NEWS Fri May 23 18:40:26 2025 +0200 @@ -1,6 +1,7 @@ Pending changes in the mainline =============================== +* Added support for pinch/zoom and panning gestures * Experimental support for DICOM SR, display of textual reports * Experimental support for DICOM SR "Measurement Report" (TID 1500 - only polylines) * Added "Print" and "Download" buttons in the PDF viewer toolbar
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Sun Apr 27 20:55:48 2025 +0200 +++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Fri May 23 18:40:26 2025 +0200 @@ -932,6 +932,8 @@ studies_, PRIORITY_HIGH, source_, Orthanc::ResourceType_Study, filter, tags, new Orthanc::SingleValueObject<Orthanc::ResourceType>(Orthanc::ResourceType_Study)); + pending_++; + // Secondly, load the series if (!seriesInstanceUid.empty()) { @@ -944,7 +946,7 @@ series_, PRIORITY_HIGH, source_, Orthanc::ResourceType_Series, filter, tags, new Orthanc::SingleValueObject<Orthanc::ResourceType>(Orthanc::ResourceType_Series)); - pending_ += 2; + pending_++; } @@ -1426,7 +1428,7 @@ std::string sopClassUid, sopInstanceUid; if (accessor.GetInstance(i).LookupStringValue(sopClassUid, Orthanc::DICOM_TAG_SOP_CLASS_UID, false) && accessor.GetInstance(i).LookupStringValue(sopInstanceUid, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false) && - sopClassUid == "1.2.840.10008.5.1.4.1.1.104.1") + OrthancStone::StringToSopClassUid(sopClassUid) == OrthancStone::SopClassUid_EncapsulatedPdf) { std::unique_ptr<OrthancStone::ILoadersContext::ILock> lock(context_.Lock()); lock->Schedule(
--- a/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake Sun Apr 27 20:55:48 2025 +0200 +++ b/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake Fri May 23 18:40:26 2025 +0200 @@ -276,6 +276,7 @@ ${ORTHANC_STONE_ROOT}/Scene2D/MacroSceneLayer.cpp ${ORTHANC_STONE_ROOT}/Scene2D/MagnifyingGlassTracker.cpp ${ORTHANC_STONE_ROOT}/Scene2D/PanSceneTracker.cpp + ${ORTHANC_STONE_ROOT}/Scene2D/PinchZoomTracker.cpp ${ORTHANC_STONE_ROOT}/Scene2D/PointerEvent.cpp ${ORTHANC_STONE_ROOT}/Scene2D/PolylineSceneLayer.cpp ${ORTHANC_STONE_ROOT}/Scene2D/RotateSceneTracker.cpp
--- a/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyViewport.cpp Sun Apr 27 20:55:48 2025 +0200 +++ b/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyViewport.cpp Fri May 23 18:40:26 2025 +0200 @@ -330,6 +330,57 @@ { } + EM_BOOL WebAssemblyViewport::OnTouch(int eventType, + const EmscriptenTouchEvent *touchEvent, + void *userData) + { + WebAssemblyViewport* that = reinterpret_cast<WebAssemblyViewport*>(userData); + + if (that->compositor_.get() != NULL && + that->interactor_.get() != NULL) + { + const ICompositor& compositor = *that->compositor_; + + PointerEvent event; + for (int i = 0; i < touchEvent->numTouches; i++) + { + event.AddPosition(compositor.GetPixelCenterCoordinates(touchEvent->touches[i].targetX, touchEvent->touches[i].targetY)); + } + + switch (eventType) + { + case EMSCRIPTEN_EVENT_TOUCHSTART: + that->controller_->HandleMousePress(*that->interactor_, event, + that->compositor_->GetCanvasWidth(), + that->compositor_->GetCanvasHeight()); + that->Invalidate(); + break; + + case EMSCRIPTEN_EVENT_TOUCHMOVE: + if (that->controller_->HasActiveTracker() && + that->controller_->HandleMouseMove(event)) + { + that->Invalidate(); + } + break; + + case EMSCRIPTEN_EVENT_TOUCHEND: + case EMSCRIPTEN_EVENT_TOUCHCANCEL: + if (that->controller_->HasActiveTracker()) + { + that->controller_->HandleMouseRelease(event); + that->Invalidate(); + } + break; + + default: + break; + } + } + + return true; + } + void WebAssemblyViewport::PostConstructor() { boost::shared_ptr<IViewport> viewport = shared_from_this(); @@ -385,6 +436,11 @@ reinterpret_cast<void*>(this), false, OnMouseUp); + + emscripten_set_touchstart_callback(canvasCssSelector_.c_str(), reinterpret_cast<void*>(this), false, OnTouch); + emscripten_set_touchend_callback(canvasCssSelector_.c_str(), reinterpret_cast<void*>(this), false, OnTouch); + emscripten_set_touchmove_callback(canvasCssSelector_.c_str(), reinterpret_cast<void*>(this), false, OnTouch); + emscripten_set_touchcancel_callback(canvasCssSelector_.c_str(), reinterpret_cast<void*>(this), false, OnTouch); } }
--- a/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyViewport.h Sun Apr 27 20:55:48 2025 +0200 +++ b/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyViewport.h Fri May 23 18:40:26 2025 +0200 @@ -74,6 +74,8 @@ static EM_BOOL OnMouseUp(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); + static EM_BOOL OnTouch(int eventType, const EmscriptenTouchEvent *mouseEvent, void *userData); + protected: void Invalidate();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancStone/Sources/Scene2D/PinchZoomTracker.cpp Fri May 23 18:40:26 2025 +0200 @@ -0,0 +1,127 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2023 Osimis S.A., Belgium + * Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/>. + **/ + + +#include "PinchZoomTracker.h" + +#include "../Scene2DViewport/ViewportController.h" +#include "../Viewport/ViewportLocker.h" + +namespace OrthancStone +{ + static ScenePoint2D GetCenter(const PointerEvent& event) + { + assert(event.GetPositionsCount() == 2); + return ScenePoint2D((event.GetPosition(0).GetX() + event.GetPosition(1).GetX()) / 2.0, + (event.GetPosition(0).GetY() + event.GetPosition(1).GetY()) / 2.0); + } + + + PinchZoomTracker::PinchZoomTracker(boost::weak_ptr<IViewport> viewport, + const PointerEvent& event) : + viewport_(viewport), + state_(State_Dead) + { + ViewportLocker locker(viewport_); + + if (locker.IsValid()) + { + originalSceneToCanvas_ = locker.GetController().GetSceneToCanvasTransform(); + originalCanvasToScene_ = locker.GetController().GetCanvasToSceneTransform(); + + if (event.GetPositionsCount() == 1) + { + state_ = State_OneFinger; + pivot_ = event.GetPosition(0).Apply(originalCanvasToScene_); + originalDistance_ = 0; + } + else if (event.GetPositionsCount() == 2) + { + state_ = State_TwoFingers; + pivot_ = GetCenter(event).Apply(originalCanvasToScene_); + originalDistance_ = ScenePoint2D::DistancePtPt(event.GetPosition(0), event.GetPosition(1)); + } + } + } + + + void PinchZoomTracker::PointerMove(const PointerEvent &event, + const Scene2D &scene) + { + if (state_ == State_OneFinger && + event.GetPositionsCount() == 2) + { + // Upgrade from 1 finger to 2 fingers, keeping the original pivot point + state_ = State_Upgraded; + originalDistance_ = ScenePoint2D::DistancePtPt(event.GetPosition(0), event.GetPosition(1)); + } + + ScenePoint2D p; + double zoom; + + if (event.GetPositionsCount() == 1 && + state_ == State_OneFinger) + { + p = event.GetPosition(0).Apply(originalCanvasToScene_); + zoom = 1; + } + else if (event.GetPositionsCount() == 2) + { + if (state_ == State_TwoFingers) + { + p = GetCenter(event).Apply(originalCanvasToScene_); + } + else if (state_ == State_Upgraded) + { + p = event.GetPosition(0).Apply(originalCanvasToScene_); + } + else + { + state_ = State_Dead; + return; + } + + double distance = ScenePoint2D::DistancePtPt(event.GetPosition(0), event.GetPosition(1)); + zoom = distance / originalDistance_; + } + else + { + state_ = State_Dead; + return; + } + + { + ViewportLocker locker(viewport_); + + if (locker.IsValid()) + { + locker.GetController().SetSceneToCanvasTransform( + AffineTransform2D::Combine( + originalSceneToCanvas_, + AffineTransform2D::CreateOffset(p.GetX(), p.GetY()), + AffineTransform2D::CreateScaling(zoom), + AffineTransform2D::CreateOffset(-pivot_.GetX(), -pivot_.GetY()))); + locker.Invalidate(); + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancStone/Sources/Scene2D/PinchZoomTracker.h Fri May 23 18:40:26 2025 +0200 @@ -0,0 +1,79 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2023 Osimis S.A., Belgium + * Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Scene2DViewport/IFlexiblePointerTracker.h" +#include "../Viewport/IViewport.h" +#include "PointerEvent.h" + + +namespace OrthancStone +{ + class PinchZoomTracker : public IFlexiblePointerTracker + { + private: + enum State + { + State_OneFinger, + State_TwoFingers, + State_Upgraded, + State_Dead + }; + + boost::weak_ptr<IViewport> viewport_; + State state_; + AffineTransform2D originalSceneToCanvas_; + AffineTransform2D originalCanvasToScene_; + ScenePoint2D pivot_; + double originalDistance_; + + public: + PinchZoomTracker(boost::weak_ptr<IViewport> viewport, + const PointerEvent& event); + + virtual void PointerMove(const PointerEvent &event, + const Scene2D &scene) ORTHANC_OVERRIDE; + + virtual void PointerUp(const PointerEvent &event, + const Scene2D &scene) ORTHANC_OVERRIDE + { + state_ = State_Dead; + } + + virtual void PointerDown(const PointerEvent &event, + const Scene2D &scene) ORTHANC_OVERRIDE + { + } + + virtual bool IsAlive() const ORTHANC_OVERRIDE + { + return state_ != State_Dead; + } + + virtual void Cancel(const Scene2D &scene) ORTHANC_OVERRIDE + { + state_ = State_Dead; + } + }; +}
--- a/OrthancStone/Sources/Toolbox/AffineTransform2D.h Sun Apr 27 20:55:48 2025 +0200 +++ b/OrthancStone/Sources/Toolbox/AffineTransform2D.h Fri May 23 18:40:26 2025 +0200 @@ -93,6 +93,11 @@ static AffineTransform2D CreateScaling(double sx, double sy); + static AffineTransform2D CreateScaling(double s) + { + return CreateScaling(s, s); + } + static AffineTransform2D CreateRotation(double angle); // CW rotation in radians static AffineTransform2D CreateRotation(double angle, // CW rotation in radians
--- a/OrthancStone/Sources/Viewport/DefaultViewportInteractor.cpp Sun Apr 27 20:55:48 2025 +0200 +++ b/OrthancStone/Sources/Viewport/DefaultViewportInteractor.cpp Fri May 23 18:40:26 2025 +0200 @@ -25,12 +25,15 @@ #include "../Scene2D/GrayscaleWindowingSceneTracker.h" #include "../Scene2D/MagnifyingGlassTracker.h" #include "../Scene2D/PanSceneTracker.h" +#include "../Scene2D/PinchZoomTracker.h" #include "../Scene2D/RotateSceneTracker.h" #include "../Scene2D/ZoomSceneTracker.h" #include "../Scene2DViewport/ViewportController.h" #include <OrthancException.h> +#include <emscripten.h> + namespace OrthancStone { IFlexiblePointerTracker* DefaultViewportInteractor::CreateTrackerInternal( @@ -109,6 +112,17 @@ action = rightButtonAction_; break; + case MouseButton_None: + if (event.GetPositionsCount() == 1 || + event.GetPositionsCount() == 2) + { + return new PinchZoomTracker(viewport, event); + } + else + { + return NULL; + } + default: return NULL; }