changeset 1693:eafb10992e73

synchronized browsing
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 26 Nov 2020 13:46:50 +0100
parents e787b52d025f
children 7226b68e2742
files Applications/StoneWebViewer/WebApplication/app.js Applications/StoneWebViewer/WebApplication/index.html Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp
diffstat 3 files changed, 88 insertions(+), 36 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/StoneWebViewer/WebApplication/app.js	Wed Nov 25 18:11:42 2020 +0100
+++ b/Applications/StoneWebViewer/WebApplication/app.js	Thu Nov 26 13:46:50 2020 +0100
@@ -260,6 +260,7 @@
       activeViewport: 0,
       showInfo: true,
       showReferenceLines: true,
+      synchronizedBrowsing: false,
 
       modalWarning: false,
       modalNotDiagnostic: false,
@@ -333,6 +334,9 @@
     showReferenceLines: function(newVal, oldVal) {
       stone.ShowReferenceLines(newVal ? 1 : 0);
     },
+    synchronizedBrowsing: function(newVal, oldVal) {
+      stone.SetSynchronizedBrowsingEnabled(newVal ? 1 : 0);
+    },
     settingNotDiagnostic: function(newVal, oldVal) {
       localStorage.settingNotDiagnostic = (newVal ? '1' : '0');
     },
--- a/Applications/StoneWebViewer/WebApplication/index.html	Wed Nov 25 18:11:42 2020 +0100
+++ b/Applications/StoneWebViewer/WebApplication/index.html	Thu Nov 26 13:46:50 2020 +0100
@@ -446,10 +446,10 @@
             
             <div class="ng-scope inline-object">
               <button class="wvButton--underline text-center"
-                      data-toggle="tooltip" data-title="Show image information"
-                      v-bind:class="{ 'active' : showInfo }"
-                      v-on:click="showInfo = !showInfo">
-                <i class="fa fa-info-circle"></i>
+                      data-toggle="tooltip" data-title="Synchronized browsing"
+                      v-bind:class="{ 'active' : synchronizedBrowsing }"
+                      v-on:click="synchronizedBrowsing = !synchronizedBrowsing">
+                <i class="fa fa-link"></i>
               </button>
             </div>
 
@@ -464,6 +464,15 @@
 
             <div class="ng-scope inline-object">
               <button class="wvButton--underline text-center"
+                      data-toggle="tooltip" data-title="Show image information"
+                      v-bind:class="{ 'active' : showInfo }"
+                      v-on:click="showInfo = !showInfo">
+                <i class="fa fa-info-circle"></i>
+              </button>
+            </div>
+
+            <div class="ng-scope inline-object">
+              <button class="wvButton--underline text-center"
                       data-toggle="tooltip" data-title="User preferences"
                       v-on:click="modalPreferences = true">
                 <i class="fa fa-user"></i>
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Wed Nov 25 18:11:42 2020 +0100
+++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Thu Nov 26 13:46:50 2020 +0100
@@ -1033,7 +1033,8 @@
                                     size_t countFrames,
                                     DisplayedFrameQuality quality) = 0;
 
-    virtual void SignalCrosshair(const OrthancStone::Vector& click) = 0;
+    virtual void SignalCrosshair(const ViewerViewport& viewport,
+                                 const OrthancStone::Vector& click) = 0;
   };
 
 private:
@@ -1397,9 +1398,10 @@
   bool                                         flipX_;
   bool                                         flipY_;
   bool                                         fitNextContent_;
-  bool                                         isCtrlDown_;
   std::list<PrefetchItem>                      prefetchQueue_;
   bool                                         serverSideTranscoding_;
+  OrthancStone::Vector                         synchronizationOffset_;
+  bool                                         synchronizationEnabled_;
 
 
   bool         hasFocusOnInstance_;
@@ -1782,11 +1784,12 @@
     source_(source),
     framesCache_(cache),
     fitNextContent_(true),
-    isCtrlDown_(false),
     flipX_(false),
     flipY_(false),
     hasFocusOnInstance_(false),
-    focusFrameNumber_(0)
+    focusFrameNumber_(0),
+    synchronizationOffset_(OrthancStone::LinearAlgebra::CreateVector(0, 0, 0)),
+    synchronizationEnabled_(false)
   {
     if (!framesCache_)
     {
@@ -1812,26 +1815,10 @@
     }
     
     emscripten_set_wheel_callback(viewport_->GetCanvasCssSelector().c_str(), this, true, OnWheel);
-    emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, OnKey);
-    emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, OnKey);
 
     SetWindowingPreset();
   }
 
-  static EM_BOOL OnKey(int eventType,
-                       const EmscriptenKeyboardEvent *event,
-                       void *userData)
-  {
-    /**
-     * WARNING: There is a problem with Firefox 71 that seems to mess
-     * the "ctrlKey" value.
-     **/
-    
-    ViewerViewport& that = *reinterpret_cast<ViewerViewport*>(userData);
-    that.isCtrlDown_ = event->ctrlKey;
-    return false;
-  }
-
   
   static EM_BOOL OnWheel(int eventType,
                          const EmscriptenWheelEvent *wheelEvent,
@@ -1839,15 +1826,41 @@
   {
     ViewerViewport& that = *reinterpret_cast<ViewerViewport*>(userData);
 
-    if (that.cursor_.get() != NULL)
+    if (that.frames_.get() != NULL &&
+        that.cursor_.get() != NULL)
     {
+      const bool isCtrl = wheelEvent->mouse.ctrlKey;
+      const bool isShift = wheelEvent->mouse.shiftKey;
+      
+      const size_t previousCursorIndex = that.cursor_->GetCurrentIndex();
+      
       if (wheelEvent->deltaY < 0)
       {
-        that.ChangeFrame(that.isCtrlDown_ ? SeriesCursor::Action_FastMinus : SeriesCursor::Action_Minus, false /* not circular */);
+        that.ChangeFrame(isCtrl ? SeriesCursor::Action_FastMinus :
+                         SeriesCursor::Action_Minus, false /* not circular */);
       }
       else if (wheelEvent->deltaY > 0)
       {
-        that.ChangeFrame(that.isCtrlDown_ ? SeriesCursor::Action_FastPlus : SeriesCursor::Action_Plus, false /* not circular */);
+        that.ChangeFrame(isCtrl ? SeriesCursor::Action_FastPlus :
+                         SeriesCursor::Action_Plus, false /* not circular */);
+      }
+
+      if (that.synchronizationEnabled_)
+      {
+        const size_t currentCursorIndex = that.cursor_->GetCurrentIndex();
+
+        const OrthancStone::CoordinateSystem3D current =
+          that.frames_->GetFrameGeometry(currentCursorIndex);
+      
+        if (isShift &&
+            previousCursorIndex != currentCursorIndex)
+        {
+          const OrthancStone::CoordinateSystem3D previous =
+            that.frames_->GetFrameGeometry(previousCursorIndex);
+          that.synchronizationOffset_ += previous.GetOrigin() - current.GetOrigin();
+        }
+
+        that.observer_->SignalCrosshair(that, current.GetOrigin() + that.synchronizationOffset_);
       }
     }
     
@@ -1876,8 +1889,6 @@
     // has been destroyed. "WebAssemblyViewport::CreateObjectCookie()"
     // provides a more advanced alternative.
     emscripten_set_wheel_callback(viewport_->GetCanvasCssSelector().c_str(), this, true, NULL);
-    emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, NULL);
-    emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, NULL);
   }
 
   static boost::shared_ptr<ViewerViewport> Create(OrthancStone::WebAssemblyLoadersContext& context,
@@ -1919,6 +1930,7 @@
     cineRate_ = DEFAULT_CINE_RATE;
     inverted_ = false;
     serverSideTranscoding_ = false;
+    OrthancStone::LinearAlgebra::AssignVector(synchronizationOffset_, 0, 0, 0);
 
     frames_.reset(frames);
     cursor_.reset(new SeriesCursor(frames_->GetFramesCount()));
@@ -2281,7 +2293,7 @@
           OrthancStone::Vector click = plane.MapSliceToWorldCoordinates(x, y);
           if (viewer_.observer_.get() != NULL)
           {
-            viewer_.observer_->SignalCrosshair(click);
+            viewer_.observer_->SignalCrosshair(viewer_, click);
           }
         }
         
@@ -2384,14 +2396,22 @@
 
       Json::Value preset = Json::objectValue;
       preset["name"] = name;
-      preset["info"] = ("C " + boost::lexical_cast<std::string>(boost::math::iround(c)) +
-                        ", W " + boost::lexical_cast<std::string>(boost::math::iround(w)));
       preset["center"] = c;
       preset["width"] = w;
-
+      preset["info"] =
+        ("C " + boost::lexical_cast<std::string>(static_cast<int>(boost::math::iround<double>(c))) +
+         ", W " + boost::lexical_cast<std::string>(static_cast<int>(boost::math::iround<double>(w))));
+      
       target.append(preset);
     }
   }
+
+
+  void SetSynchronizedBrowsingEnabled(int enabled)
+  {
+    OrthancStone::LinearAlgebra::AssignVector(synchronizationOffset_, 0, 0, 0);
+    synchronizationEnabled_ = enabled;
+  }
 };
 
 
@@ -2510,12 +2530,16 @@
     UpdateReferenceLines();
   }
 
-  virtual void SignalCrosshair(const OrthancStone::Vector& click) ORTHANC_OVERRIDE
+  virtual void SignalCrosshair(const ViewerViewport& viewport,
+                               const OrthancStone::Vector& click) ORTHANC_OVERRIDE
   {
     for (Viewports::const_iterator it = allViewports_.begin(); it != allViewports_.end(); ++it)
     {
-      assert(it->second != NULL);
-      it->second->FocusOnPoint(click);
+      assert(it->second.get() != NULL);
+      if (it->second.get() != &viewport)
+      {
+        it->second->FocusOnPoint(click);
+      }
     }
   }
 
@@ -3084,4 +3108,19 @@
     }
     EXTERN_CATCH_EXCEPTIONS;
   }
+
+
+  EMSCRIPTEN_KEEPALIVE
+  void SetSynchronizedBrowsingEnabled(int enabled)
+  {
+    try
+    {
+      for (Viewports::iterator it = allViewports_.begin(); it != allViewports_.end(); ++it)
+      {
+        assert(it->second != NULL);
+        it->second->SetSynchronizedBrowsingEnabled(enabled);
+      }
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+  }
 }