changeset 1681:f2e8b3ac1dcd

handling multiple windowing presets in the Stone web viewer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 24 Nov 2020 18:08:07 +0100
parents 03afa09cfcf1
children 84fe7089ccaa
files Applications/StoneWebViewer/WebApplication/app.js Applications/StoneWebViewer/WebApplication/index.html Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp
diffstat 3 files changed, 132 insertions(+), 108 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/StoneWebViewer/WebApplication/app.js	Tue Nov 24 16:39:54 2020 +0100
+++ b/Applications/StoneWebViewer/WebApplication/app.js	Tue Nov 24 18:08:07 2020 +0100
@@ -300,6 +300,9 @@
       viewport4Visible: false,
       viewport4Series: {},
 
+      showWindowing: false,
+      windowingPresets: [],
+
       series: [],
       studies: [],
       seriesIndex: {}  // Maps "SeriesInstanceUID" to "index in this.series"
@@ -617,19 +620,13 @@
     },
 
     SetWindowing: function(center, width) {
+      this.showWindowing = false;
       var canvas = this.GetActiveCanvas();
       if (canvas != '') {
         stone.SetWindowing(canvas, center, width);
       }
     },
 
-    SetPresetWindowing: function() {
-      var canvas = this.GetActiveCanvas();
-      if (canvas != '') {
-        stone.SetPresetWindowing(canvas);
-      }
-    },
-
     InvertContrast: function() {
       var canvas = this.GetActiveCanvas();
       if (canvas != '') {
@@ -679,6 +676,27 @@
           
         stone.FocusFirstOsiriXAnnotation('canvas1');
       }
+    },
+
+    ToggleWindowing: function()
+    {
+      if (this.showWindowing)
+      {
+        this.showWindowing = false;
+      }
+      else
+      {
+        stone.LoadWindowingPresets(this.GetActiveCanvas());
+        this.windowingPresets = JSON.parse(stone.GetStringBuffer());
+
+        var p = $('#windowing-popover').last();
+        var top = p.offset().top + p.height() + 10;
+        $('#windowing-content').css('top', top);
+        //$('#windowing-content').css('right', '10');
+        //$('#windowing-content').css('left', 'auto');
+
+        this.showWindowing = true;
+      }
     }
   },
   
@@ -783,17 +801,6 @@
   //app.modalWarning = true;
 
 
-  $('#windowing-popover').popover({
-    container: 'body',
-    content: $('#windowing-content').html(),
-    template: '<div class="popover wvToolbar__windowingPresetConfigPopover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>',
-    placement: 'auto',
-    html: true,
-    sanitize: false,
-    trigger: 'focus'   // Close on click
-  });
-  
-  
   var wasmSource = 'StoneWebViewer.js';
   
   // Option 1: Loading script using plain HTML
--- a/Applications/StoneWebViewer/WebApplication/index.html	Tue Nov 24 16:39:54 2020 +0100
+++ b/Applications/StoneWebViewer/WebApplication/index.html	Tue Nov 24 18:08:07 2020 +0100
@@ -367,11 +367,64 @@
             <div class="ng-scope inline-object">
               <button class="wvButton--underline text-center"
                       data-toggle="tooltip" data-title="Change windowing"
-                      id="windowing-popover">
+                      id="windowing-popover"
+                      v-on:click="ToggleWindowing()">
                 <i class="fa fa-sun"></i>
               </button>
             </div>
 
+            <div id="windowing-content" v-show="showWindowing"
+                 class="popover wvToolbar__windowingPresetConfigPopover"
+                 style="position: absolute; display: block"
+                 >
+              <div class="arrow"></div>
+              <h3 class="popover-title">Change windowing</h3>
+              <div class="popover-content">
+                
+                <!--p class="wvToolbar__windowingPresetConfigNotice">
+                    Click on the button to toggle the windowing tool or apply a preset to the selected viewport.
+                    </p-->
+
+                <ul class="wvToolbar__windowingPresetList">
+                  <li v-for="preset in windowingPresets" class="wvToolbar__windowingPresetListItem">
+                    <a href="#" v-on:click="SetWindowing(preset.center, preset.width)">
+                      {{ preset.name }} <small>({{ preset.info }})</small>
+                    </a>
+                  </li>
+                  <li class="wvToolbar__windowingPresetListItem">
+                    <a href="#" v-on:click="SetWindowing(-400, 1600)">
+                      CT Lung <small>(C -400, W 1600)</small>
+                    </a>
+                  </li>
+                  <li class="wvToolbar__windowingPresetListItem">
+                    <a href="#" v-on:click="SetWindowing(300, 1500)">
+                      CT Abdomen <small>(C 300, W 1500)</small>
+                    </a>
+                  </li>
+                  <li class="wvToolbar__windowingPresetListItem">
+                    <a href="#" v-on:click="SetWindowing(40, 80)">
+                      CT Bone <small>(C 40, W 80)</small>
+                    </a>
+                  </li>
+                  <li class="wvToolbar__windowingPresetListItem">
+                    <a href="#" v-on:click="SetWindowing(40, 400)">
+                      CT Brain <small>(C 40, W 400)</small>
+                    </a>
+                  </li>
+                  <li class="wvToolbar__windowingPresetListItem">
+                    <a href="#" v-on:click="SetWindowing(-400, 1600)">
+                      CT Chest <small>(C -400, W 1600)</small>
+                    </a>
+                  </li>
+                  <li class="wvToolbar__windowingPresetListItem">
+                    <a href="#" v-on:click="SetWindowing(300, 600)">
+                      CT Angio <small>(C 300, W 600)</small>
+                    </a>
+                  </li>
+                </ul>
+              </div>
+            </div>
+
             <div class="ng-scope inline-object">
               <button class="wvButton--underline text-center"
                       data-toggle="tooltip" data-title="Flip horizontally"
@@ -464,58 +517,11 @@
                         v-bind:active="activeViewport==4"></viewport>
             </div>
           </div>
-
         </div>
-      </div>
+      </div>      
     </div>
 
-
-
-    <script type="text/x-template" id="windowing-content">
-      <p class="wvToolbar__windowingPresetConfigNotice">
-        Click on the button to toggle the windowing tool or apply a preset to the selected viewport.
-      </p>
-
-      <ul class="wvToolbar__windowingPresetList">
-        <li class="wvToolbar__windowingPresetListItem">
-          <a href="#" onclick="app.SetPresetWindowing()">
-            Preset
-          </a>
-        </li>
-        <li class="wvToolbar__windowingPresetListItem">
-          <a href="#" onclick="app.SetWindowing(-400, 1600)">
-            CT Lung <small>(L -400, W 1,600)</small>
-          </a>
-        </li>
-        <li class="wvToolbar__windowingPresetListItem">
-          <a href="#" onclick="app.SetWindowing(300, 1500)">
-            CT Abdomen <small>(L 300, W 1,500)</small>
-          </a>
-        </li>
-        <li class="wvToolbar__windowingPresetListItem">
-          <a href="#" onclick="app.SetWindowing(40, 80)">
-            CT Bone <small>(L 40, W 80)</small>
-          </a>
-        </li>
-        <li class="wvToolbar__windowingPresetListItem">
-          <a href="#" onclick="app.SetWindowing(40, 400)">
-            CT Brain <small>(L 40, W 400)</small>
-          </a>
-        </li>
-        <li class="wvToolbar__windowingPresetListItem">
-          <a href="#" onclick="app.SetWindowing(-400, 1600)">
-            CT Chest <small>(L -400, W 1,600)</small>
-          </a>
-        </li>
-        <li class="wvToolbar__windowingPresetListItem">
-          <a href="#" onclick="app.SetWindowing(300, 600)">
-            CT Angio <small>(L 300, W 600)</small>
-          </a>
-        </li>
-      </ul>
-    </script>
     
-
     <script type="text/x-template" id="viewport-template">
       <div v-bind:id="canvasId + '-container'"
            v-bind:style="{ padding:'2px', 
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Tue Nov 24 16:39:54 2020 +0100
+++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Tue Nov 24 18:08:07 2020 +0100
@@ -94,6 +94,7 @@
 #include <WebGLViewport.h>
 
 
+#include <boost/math/special_functions/round.hpp>
 #include <boost/make_shared.hpp>
 #include <stdio.h>
 
@@ -1102,27 +1103,25 @@
       {
         OrthancStone::DicomInstanceParameters params(dicom);
 
+        GetViewport().windowingPresetCenters_.resize(params.GetWindowingPresetsCount());
+        GetViewport().windowingPresetWidths_.resize(params.GetWindowingPresetsCount());
+
         for (size_t i = 0; i < params.GetWindowingPresetsCount(); i++)
         {
-          LOG(INFO) << "Preset windowing " << i << "/" << params.GetWindowingPresetsCount()
+          LOG(INFO) << "Preset windowing " << (i + 1) << "/" << params.GetWindowingPresetsCount()
                     << ": " << params.GetWindowingPresetCenter(i)
                     << "," << params.GetWindowingPresetWidth(i);
+
+          GetViewport().windowingPresetCenters_[i] = params.GetWindowingPresetCenter(i);
+          GetViewport().windowingPresetWidths_[i] = params.GetWindowingPresetWidth(i);
         }
 
-        // TODO - WINDOWING
-        if (params.GetWindowingPresetsCount() > 0)
-        {
-          GetViewport().presetWindowingCenter_ = params.GetWindowingPresetCenter(0);
-          GetViewport().presetWindowingWidth_ = params.GetWindowingPresetWidth(0);
-
-          GetViewport().windowingCenter_ = params.GetWindowingPresetCenter(0);
-          GetViewport().windowingWidth_ = params.GetWindowingPresetWidth(0);
-        }
-        else
+        if (params.GetWindowingPresetsCount() == 0)
         {
           LOG(INFO) << "No preset windowing";
-          GetViewport().ResetWindowingPreset();
         }
+
+        GetViewport().SetWindowingPreset();
       }
 
       uint32_t cineRate;
@@ -1367,8 +1366,8 @@
   std::unique_ptr<SeriesCursor>                cursor_;
   float                                        windowingCenter_;
   float                                        windowingWidth_;
-  float                                        presetWindowingCenter_;
-  float                                        presetWindowingWidth_;
+  std::vector<float>                           windowingPresetCenters_;
+  std::vector<float>                           windowingPresetWidths_;
   unsigned int                                 cineRate_;
   bool                                         inverted_;
   bool                                         flipX_;
@@ -1416,18 +1415,6 @@
   }
   
   
-  void ResetWindowingPreset()
-  {
-    presetWindowingCenter_ = 128;
-    presetWindowingWidth_ = 256;
-
-    windowingCenter_ = presetWindowingCenter_;
-    windowingWidth_ = presetWindowingWidth_;
-
-    inverted_ = false;
-  }
-
-
   void ClearViewport()
   {
     {
@@ -1794,7 +1781,7 @@
     emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, OnKey);
     emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, OnKey);
 
-    ResetWindowingPreset();
+    SetWindowingPreset();
   }
 
   static EM_BOOL OnKey(int eventType,
@@ -1896,13 +1883,14 @@
     flipY_ = false;
     fitNextContent_ = true;
     cineRate_ = DEFAULT_CINE_RATE;
-    
+    inverted_ = false;
+
     frames_.reset(frames);
     cursor_.reset(new SeriesCursor(frames_->GetFramesCount()));
 
     LOG(INFO) << "Number of frames in series: " << frames_->GetFramesCount();
 
-    ResetWindowingPreset();
+    SetWindowingPreset();
     ClearViewport();
     prefetchQueue_.clear();
 
@@ -2131,7 +2119,18 @@
 
   void SetWindowingPreset()
   {
-    SetWindowing(presetWindowingCenter_, presetWindowingWidth_);
+    assert(windowingPresetCenters_.size() == windowingPresetWidths_.size());
+    
+    if (windowingPresetCenters_.empty())
+    {
+      windowingCenter_ = 128;
+      windowingWidth_ = 256;
+    }
+    else
+    {
+      windowingCenter_ = windowingPresetCenters_[0];
+      windowingWidth_ = windowingPresetWidths_[0];
+    }
   }
 
   void SetWindowing(float windowingCenter,
@@ -2333,7 +2332,30 @@
 
   void FormatWindowingPresets(Json::Value& target) const
   {
+    assert(windowingPresetCenters_.size() == windowingPresetWidths_.size());
+
     target = Json::arrayValue;
+
+    for (size_t i = 0; i < windowingPresetCenters_.size(); i++)
+    {
+      const float c = windowingPresetCenters_[i];
+      const float w = windowingPresetWidths_[i];
+      
+      std::string name = "Preset";
+      if (windowingPresetCenters_.size() > 1)
+      {
+        name += " " + boost::lexical_cast<std::string>(i + 1);
+      }
+
+      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;
+
+      target.append(preset);
+    }
   }
 };
 
@@ -2825,17 +2847,6 @@
 
 
   EMSCRIPTEN_KEEPALIVE
-  void SetWindowingPreset(const char* canvas)
-  {
-    try
-    {
-      GetViewport(canvas)->SetWindowingPreset();
-    }
-    EXTERN_CATCH_EXCEPTIONS;
-  }  
-
-
-  EMSCRIPTEN_KEEPALIVE
   void SetWindowing(const char* canvas,
                     int center,
                     int width)