changeset 1603:595c0952ef7e

focusing on osirix annotations in Stone Web viewer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 28 Oct 2020 18:49:01 +0100
parents b2941196cabf
children 89e43bb4906f
files Applications/StoneWebViewer/WebApplication/app.js Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp
diffstat 2 files changed, 139 insertions(+), 52 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/StoneWebViewer/WebApplication/app.js	Wed Oct 28 16:35:45 2020 +0100
+++ b/Applications/StoneWebViewer/WebApplication/app.js	Wed Oct 28 18:49:01 2020 +0100
@@ -302,6 +302,13 @@
       // It is necessary to use ".toString()" for Microsoft Edge Legacy (*)
       event.dataTransfer.setData('seriesIndex', seriesIndex.toString());
     },
+
+    SetViewportSeriesInstanceUid: function(viewportIndex, seriesInstanceUid) {
+      if (seriesInstanceUid in this.seriesIndex) {
+        this.SetViewportSeries(viewportIndex, this.seriesIndex[seriesInstanceUid]);
+
+      }
+    },
     
     SetViewportSeries: function(viewportIndex, seriesIndex) {
       var series = this.series[seriesIndex];
@@ -507,7 +514,20 @@
     SetMouseButtonActions: function(left, middle, right) {
       this.mouseActionsVisible = false;
       stone.SetMouseButtonActions(left, middle, right);
-    }    
+    },
+
+    LoadOsiriXAnnotations: function(xml, clearPrevious)
+    {
+      if (stone.LoadOsiriXAnnotations(xml, clearPrevious)) {
+        var seriesInstanceUid = stone.GetStringBuffer();
+
+        this.SetViewportLayout('1x1');
+        this.leftVisible = false;
+        this.SetViewportSeriesInstanceUid(1, seriesInstanceUid);
+          
+        stone.FocusFirstOsiriXAnnotation('canvas1');
+      }
+    }
   },
   
   mounted: function() {
@@ -548,31 +568,6 @@
       app.leftMode = 'full';
     }
   }
-
-
-
-  // TODO - TEST
-  axios.get('length.xml')
-    .then(function (response) {
-      stone.LoadOsiriXAnnotations(response.data, false);
-    });
-  
-  axios.get('angle.xml')
-    .then(function (response) {
-      stone.LoadOsiriXAnnotations(response.data, false);
-    });
-  
-  axios.get('arrow.xml')
-    .then(function (response) {
-      stone.LoadOsiriXAnnotations(response.data, false);
-    });
-  
-  axios.get('text.xml')
-    .then(function (response) {
-      stone.LoadOsiriXAnnotations(response.data, false);
-    });
-  
-  
 });
 
 
@@ -711,3 +706,45 @@
 
 // Disable the selection of text using the mouse
 document.onselectstart = new Function ('return false');
+
+
+
+
+
+
+
+//var expectedOrigin = 'http://localhost:8042';
+var expectedOrigin = '';   // TODO - INSECURE - CONFIGURATION
+
+window.addEventListener('message', function(e) {
+  if (expectedOrigin != '' &&
+      e.origin !== expectedOrigin) {
+    alert('Bad origin for the message');
+    return;
+  }
+  
+  if (e.data.type == 'show-osirix-annotations') {
+    app.LoadOsiriXAnnotations(e.data.xml, true /* clear previous annotations */);
+  } else {
+    alert('Unknown message type: ' + e.data.type);
+  }
+});
+
+
+function Test()
+{
+  var s = [ 'length.xml', 'arrow.xml', 'text.xml', 'angle.xml' ];
+
+  for (var i = 0; i < s.length; i++) {
+    axios.get(s[i])
+      .then(function (response) {
+        //var targetOrigin = 'http://localhost:8000';
+        var targetOrigin = '*';  // TODO - INSECURE
+        
+        window.postMessage({
+          'type': 'show-osirix-annotations',
+          'xml': response.data
+        }, targetOrigin);
+      });
+  }
+}
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Wed Oct 28 16:35:45 2020 +0100
+++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Wed Oct 28 18:49:01 2020 +0100
@@ -1310,6 +1310,7 @@
 
   bool         hasFocusOnInstance_;
   std::string  focusSopInstanceUid_;
+  size_t       focusFrameNumber_;
   
   boost::shared_ptr<OrthancStone::OsiriX::CollectionOfAnnotations>  annotations_;
 
@@ -1578,7 +1579,6 @@
               {
                 const TextAnnotation& text = dynamic_cast<const TextAnnotation&>(annotation);
                 double x, y;
-                OrthancStone::LinearAlgebra::Print(text.GetCenter());
                 if (GetCurrentFrameGeometry().ProjectPoint(x, y, text.GetCenter()))
                 {
                   std::unique_ptr<OrthancStone::TextSceneLayer> layer2(new OrthancStone::TextSceneLayer());
@@ -1751,7 +1751,8 @@
     isCtrlDown_(false),
     flipX_(false),
     flipY_(false),
-    hasFocusOnInstance_(false)
+    hasFocusOnInstance_(false),
+    focusFrameNumber_(0)
   {
     if (!cache_)
     {
@@ -1857,20 +1858,6 @@
     dynamic_cast<const ICommand&>(message.GetOrigin().GetPayload()).Handle(message);
   }
 
-  void ApplyScheduledFocus()
-  {
-    size_t instanceIndex;
-    
-    if (hasFocusOnInstance_ &&
-        frames_.get() != NULL &&
-        frames_->LookupSopInstanceUid(instanceIndex, focusSopInstanceUid_))
-    {
-      // TODO
-      
-      hasFocusOnInstance_ = false;
-    }
-  }
-  
 public:
   static boost::shared_ptr<ViewerViewport> Create(OrthancStone::ILoadersContext::ILock& lock,
                                                   const OrthancStone::DicomSource& source,
@@ -1936,10 +1923,12 @@
           0, source_, uri, new SetDefaultWindowingCommand(GetSharedObserver()));
       }
     }
+
+    ApplyScheduledFocus();
   }
 
 
-  void UpdateCurrentFrame()
+  void Redraw()
   {
     if (cursor_.get() != NULL)
     {
@@ -2175,6 +2164,36 @@
     annotations_ = annotations;
   }
 
+  void ScheduleFrameFocus(const std::string& sopInstanceUid,
+                          unsigned int frameNumber)
+  {
+    hasFocusOnInstance_ = true;
+    focusSopInstanceUid_ = sopInstanceUid;
+    focusFrameNumber_ = frameNumber;
+    
+    ApplyScheduledFocus();
+  }
+
+  void ApplyScheduledFocus()
+  {
+    size_t frameIndex;
+    
+    if (hasFocusOnInstance_ &&
+        cursor_.get() != NULL &&
+        frames_.get() != NULL &&
+        frames_->LookupFrame(frameIndex, focusSopInstanceUid_, focusFrameNumber_))
+    {
+      size_t current = cursor_->GetCurrentIndex();
+
+      if (current != frameIndex)
+      {
+        cursor_->SetCurrentIndex(frameIndex);
+        DisplayCurrentFrame();
+      }
+      
+      hasFocusOnInstance_ = false;
+    }
+  }
 };
 
 
@@ -2251,6 +2270,12 @@
       },
       studyInstanceUid.c_str(),
       seriesInstanceUid.c_str());
+
+    for (Viewports::const_iterator it = allViewports_.begin(); it != allViewports_.end(); ++it)
+    {
+      assert(it->second != NULL);
+      it->second->ApplyScheduledFocus();
+    }
   }
 
   virtual void SignalFrameUpdated(const ViewerViewport& viewport,
@@ -2723,6 +2748,8 @@
   }
 
 
+  // Side-effect: "GetStringBuffer()" is filled with the "Series
+  // Instance UID" of the first loaded annotation
   EMSCRIPTEN_KEEPALIVE
   int LoadOsiriXAnnotations(const char* xml,
                             int clearPreviousAnnotations)
@@ -2735,18 +2762,21 @@
       }
       
       annotations_->LoadXml(xml);
-
+      
+      // Force redraw, as the annotations might have changed
       for (Viewports::iterator it = allViewports_.begin(); it != allViewports_.end(); ++it)
       {
-        // TODO - Check if the viewport contains one of the SOP
-        // Instance UID from the loaded annotations => focus on this
-        // instance
-
-        // TODO - If no viewport contains the instance => monitor the
-        // "ResourcesLoader" as new series get loaded
-
         assert(it->second != NULL);
-        it->second->UpdateCurrentFrame();
+        it->second->Redraw();
+      }
+
+      if (annotations_->GetSize() == 0)
+      {
+        stringBuffer_.clear();
+      }
+      else
+      {
+        stringBuffer_ = annotations_->GetAnnotation(0).GetSeriesInstanceUid();
       }
 
       LOG(WARNING) << "Loaded " << annotations_->GetSize() << " annotations from OsiriX";
@@ -2755,4 +2785,24 @@
     EXTERN_CATCH_EXCEPTIONS;
     return 0;
   }
+
+
+  EMSCRIPTEN_KEEPALIVE
+  void FocusFirstOsiriXAnnotation(const char* canvas)
+  {
+    try
+    {
+      if (annotations_->GetSize() != 0)
+      {
+        const OrthancStone::OsiriX::Annotation& annotation = annotations_->GetAnnotation(0);
+        
+        boost::shared_ptr<ViewerViewport> viewport = GetViewport(canvas);
+        viewport->ScheduleFrameFocus(annotation.GetSopInstanceUid(), 0 /* focus on first frame */);
+
+        // Force redraw, as the annotations might already have changed
+        viewport->Redraw();
+      }
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+  }
 }