Mercurial > hg > orthanc-stone
changeset 1989:e8b9a2ba1df1
Added left/right rotation buttons
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 31 Oct 2022 20:59:59 +0100 |
parents | 3c84c34322d7 |
children | f03a827f8b47 |
files | Applications/StoneWebViewer/NEWS Applications/StoneWebViewer/WebApplication/app.js Applications/StoneWebViewer/WebApplication/index.html Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp OrthancStone/Sources/Scene2D/Scene2D.cpp OrthancStone/Sources/Scene2D/Scene2D.h OrthancStone/Sources/Toolbox/AffineTransform2D.cpp OrthancStone/Sources/Toolbox/AffineTransform2D.h |
diffstat | 8 files changed, 189 insertions(+), 43 deletions(-) [+] |
line wrap: on
line diff
--- a/Applications/StoneWebViewer/NEWS Mon Oct 31 17:45:53 2022 +0100 +++ b/Applications/StoneWebViewer/NEWS Mon Oct 31 20:59:59 2022 +0100 @@ -5,6 +5,7 @@ - Pixel probe - Rectangle probe - Ellipse probe +* Added left/right rotation buttons * Added vertical slider showing position of the current frame inside the series * Display of orientation markers * The text field with the instance number is editable to go to a specific instance
--- a/Applications/StoneWebViewer/WebApplication/app.js Mon Oct 31 17:45:53 2022 +0100 +++ b/Applications/StoneWebViewer/WebApplication/app.js Mon Oct 31 20:59:59 2022 +0100 @@ -899,6 +899,20 @@ } }, + RotateLeft: function() { + var canvas = this.GetActiveCanvas(); + if (canvas != '') { + stone.RotateLeft(canvas); + } + }, + + RotateRight: function() { + var canvas = this.GetActiveCanvas(); + if (canvas != '') { + stone.RotateRight(canvas); + } + }, + ApplyPreferences: function() { this.modalPreferences = false;
--- a/Applications/StoneWebViewer/WebApplication/index.html Mon Oct 31 17:45:53 2022 +0100 +++ b/Applications/StoneWebViewer/WebApplication/index.html Mon Oct 31 20:59:59 2022 +0100 @@ -485,6 +485,22 @@ <div class="ng-scope inline-object"> <button class="wvButton--underline text-center" + data-toggle="tooltip" data-title="Rotate to the left" + v-on:click="RotateLeft()"> + <i class="fas fa-undo"></i> + </button> + </div> + + <div class="ng-scope inline-object"> + <button class="wvButton--underline text-center" + data-toggle="tooltip" data-title="Rotate to the right" + v-on:click="RotateRight()"> + <i class="fas fa-undo fa-flip-horizontal"></i> + </button> + </div> + + <div class="ng-scope inline-object"> + <button class="wvButton--underline text-center" data-toggle="tooltip" data-title="Flip horizontally" v-on:click="FlipX()"> <i class="fas fa-exchange-alt"></i>
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Mon Oct 31 17:45:53 2022 +0100 +++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Mon Oct 31 20:59:59 2022 +0100 @@ -96,10 +96,13 @@ #include "../../../OrthancStone/Sources/Platforms/WebAssembly/WebGLViewport.h" -#include <boost/math/special_functions/round.hpp> +#include <algorithm> #include <boost/make_shared.hpp> +#include <boost/math/constants/constants.hpp> +#include <boost/math/special_functions/round.hpp> #include <stdio.h> -#include <algorithm> + +static const double PI = boost::math::constants::pi<double>(); #if !defined(STONE_WEB_VIEWER_EXPORT) // We are not running ParseWebAssemblyExports.py, but we're compiling the wasm @@ -1993,8 +1996,6 @@ std::vector<float> windowingPresetWidths_; unsigned int cineRate_; bool inverted_; - bool flipX_; - bool flipY_; bool fitNextContent_; std::list<PrefetchItem> prefetchQueue_; bool serverSideTranscoding_; @@ -2155,8 +2156,6 @@ assert(layer.get() != NULL); layer->SetLinearInterpolation(true); - layer->SetFlipX(flipX_); - layer->SetFlipY(flipY_); double pixelSpacingX, pixelSpacingY; @@ -2210,8 +2209,6 @@ if (accessor.IsValid()) { overlay.reset(accessor.CreateTexture()); - overlay->SetFlipX(flipX_); - overlay->SetFlipY(flipY_); } } @@ -2496,25 +2493,6 @@ lock->GetController().GetScene().GetLayer(LAYER_TEXTURE)). SetCustomWindowing(windowingCenter_, windowingWidth_); } - - { - OrthancStone::TextureBaseSceneLayer& layer = - dynamic_cast<OrthancStone::TextureBaseSceneLayer&>( - lock->GetController().GetScene().GetLayer(LAYER_TEXTURE)); - - layer.SetFlipX(flipX_); - layer.SetFlipY(flipY_); - } - - if (lock->GetController().GetScene().HasLayer(LAYER_OVERLAY)) - { - OrthancStone::TextureBaseSceneLayer& layer = - dynamic_cast<OrthancStone::TextureBaseSceneLayer&>( - lock->GetController().GetScene().GetLayer(LAYER_OVERLAY)); - - layer.SetFlipX(flipX_); - layer.SetFlipY(flipY_); - } lock->Invalidate(); } @@ -2530,8 +2508,6 @@ source_(source), framesCache_(cache), fitNextContent_(true), - flipX_(false), - flipY_(false), hasFocusOnInstance_(false), focusFrameNumber_(0), synchronizationOffset_(OrthancStone::LinearAlgebra::CreateVector(0, 0, 0)), @@ -2764,8 +2740,6 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); } - flipX_ = false; - flipY_ = false; fitNextContent_ = true; cineRate_ = DEFAULT_CINE_RATE; inverted_ = false; @@ -3097,14 +3071,42 @@ void FlipX() { - flipX_ = !flipX_; - UpdateCurrentTextureParameters(); + { + std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); + lock->GetController().GetScene().FlipViewportX( + lock->GetCompositor().GetCanvasWidth(), lock->GetCompositor().GetCanvasHeight()); + lock->Invalidate(); + } } void FlipY() { - flipY_ = !flipY_; - UpdateCurrentTextureParameters(); + { + std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); + lock->GetController().GetScene().FlipViewportY( + lock->GetCompositor().GetCanvasWidth(), lock->GetCompositor().GetCanvasHeight()); + lock->Invalidate(); + } + } + + void RotateLeft() + { + { + std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); + lock->GetController().GetScene().RotateViewport( + -PI / 2.0, lock->GetCompositor().GetCanvasWidth(), lock->GetCompositor().GetCanvasHeight()); + lock->Invalidate(); + } + } + + void RotateRight() + { + { + std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); + lock->GetController().GetScene().RotateViewport( + PI / 2.0, lock->GetCompositor().GetCanvasWidth(), lock->GetCompositor().GetCanvasHeight()); + lock->Invalidate(); + } } void Invert() @@ -4212,6 +4214,28 @@ EMSCRIPTEN_KEEPALIVE + void RotateLeft(const char* canvas) + { + try + { + GetViewport(canvas)->RotateLeft(); + } + EXTERN_CATCH_EXCEPTIONS; + } + + + EMSCRIPTEN_KEEPALIVE + void RotateRight(const char* canvas) + { + try + { + GetViewport(canvas)->RotateRight(); + } + EXTERN_CATCH_EXCEPTIONS; + } + + + EMSCRIPTEN_KEEPALIVE void SetSoftwareRendering(int softwareRendering) { softwareRendering_ = softwareRendering;
--- a/OrthancStone/Sources/Scene2D/Scene2D.cpp Mon Oct 31 17:45:53 2022 +0100 +++ b/OrthancStone/Sources/Scene2D/Scene2D.cpp Mon Oct 31 20:59:59 2022 +0100 @@ -241,17 +241,34 @@ } } - void Scene2D::FitContent(unsigned int canvasWidth, + + static void AddTransformedPoint(Extent2D& extent, + const AffineTransform2D& forcedTransform, + double x, + double y) + { + forcedTransform.Apply(x, y); + extent.AddPoint(x, y); + } + + + void Scene2D::FitContent(const AffineTransform2D& forcedTransform, + unsigned int canvasWidth, unsigned int canvasHeight) { Extent2D extent; - GetBoundingBox(extent); if (!extent.IsEmpty()) { - double zoomX = static_cast<double>(canvasWidth) / extent.GetWidth(); - double zoomY = static_cast<double>(canvasHeight) / extent.GetHeight(); + Extent2D extent2; + AddTransformedPoint(extent2, forcedTransform, extent.GetX1(), extent.GetY1()); + AddTransformedPoint(extent2, forcedTransform, extent.GetX1(), extent.GetY2()); + AddTransformedPoint(extent2, forcedTransform, extent.GetX2(), extent.GetY2()); + AddTransformedPoint(extent2, forcedTransform, extent.GetX2(), extent.GetY1()); + + double zoomX = static_cast<double>(canvasWidth) / extent2.GetWidth(); + double zoomY = static_cast<double>(canvasHeight) / extent2.GetHeight(); double zoom = std::min(zoomX, zoomY); if (LinearAlgebra::IsCloseToZero(zoom)) @@ -259,8 +276,8 @@ zoom = 1; } - double panX = extent.GetCenterX(); - double panY = extent.GetCenterY(); + double panX = extent2.GetCenterX(); + double panY = extent2.GetCenterY(); // Bring the center of the scene to (0,0) AffineTransform2D t1 = AffineTransform2D::CreateOffset(-panX, -panY); @@ -268,7 +285,45 @@ // Scale the scene AffineTransform2D t2 = AffineTransform2D::CreateScaling(zoom, zoom); - SetSceneToCanvasTransform(AffineTransform2D::Combine(t2, t1)); + SetSceneToCanvasTransform(AffineTransform2D::Combine(t2, t1, forcedTransform)); } } + + + void Scene2D::FitContent(unsigned int canvasWidth, + unsigned int canvasHeight) + { + FitContent(AffineTransform2D() /* identity transform */, canvasWidth, canvasHeight); + } + + + void Scene2D::RotateViewport(double angle, + unsigned int canvasWidth, + unsigned int canvasHeight) + { + AffineTransform2D transform = AffineTransform2D::Combine( + AffineTransform2D::CreateRotation(angle), + GetSceneToCanvasTransform()); + FitContent(transform, canvasWidth, canvasHeight); + } + + + void Scene2D::FlipViewportX(unsigned int canvasWidth, + unsigned int canvasHeight) + { + AffineTransform2D transform = AffineTransform2D::Combine( + AffineTransform2D::CreateFlipX(), + GetSceneToCanvasTransform()); + FitContent(transform, canvasWidth, canvasHeight); + } + + + void Scene2D::FlipViewportY(unsigned int canvasWidth, + unsigned int canvasHeight) + { + AffineTransform2D transform = AffineTransform2D::Combine( + AffineTransform2D::CreateFlipY(), + GetSceneToCanvasTransform()); + FitContent(transform, canvasWidth, canvasHeight); + } }
--- a/OrthancStone/Sources/Scene2D/Scene2D.h Mon Oct 31 17:45:53 2022 +0100 +++ b/OrthancStone/Sources/Scene2D/Scene2D.h Mon Oct 31 20:59:59 2022 +0100 @@ -60,6 +60,10 @@ Scene2D(const Scene2D& other); + void FitContent(const AffineTransform2D& forcedTransform, + unsigned int canvasWidth, + unsigned int canvasHeight); + public: Scene2D() : layerCounter_(0) { @@ -118,5 +122,15 @@ unsigned int canvasHeight); void GetBoundingBox(Extent2D& target) const; + + void RotateViewport(double angle, + unsigned int canvasWidth, + unsigned int canvasHeight); + + void FlipViewportX(unsigned int canvasWidth, + unsigned int canvasHeight); + + void FlipViewportY(unsigned int canvasWidth, + unsigned int canvasHeight); }; }
--- a/OrthancStone/Sources/Toolbox/AffineTransform2D.cpp Mon Oct 31 17:45:53 2022 +0100 +++ b/OrthancStone/Sources/Toolbox/AffineTransform2D.cpp Mon Oct 31 20:59:59 2022 +0100 @@ -293,4 +293,22 @@ return t; } } + + + AffineTransform2D AffineTransform2D::CreateFlipX() + { + AffineTransform2D t; + t.matrix_(0, 0) = -1; + t.matrix_(1, 1) = 1; + return t; + } + + + AffineTransform2D AffineTransform2D::CreateFlipY() + { + AffineTransform2D t; + t.matrix_(0, 0) = 1; + t.matrix_(1, 1) = -1; + return t; + } }
--- a/OrthancStone/Sources/Toolbox/AffineTransform2D.h Mon Oct 31 17:45:53 2022 +0100 +++ b/OrthancStone/Sources/Toolbox/AffineTransform2D.h Mon Oct 31 20:59:59 2022 +0100 @@ -36,7 +36,7 @@ Matrix matrix_; public: - AffineTransform2D(); + AffineTransform2D(); // Create the identity transform // The matrix must be 3x3, without perspective effects explicit AffineTransform2D(const Matrix& m); @@ -106,5 +106,9 @@ bool flipY, unsigned int width, unsigned int height); + + static AffineTransform2D CreateFlipX(); + + static AffineTransform2D CreateFlipY(); }; }