# HG changeset patch # User Sebastien Jodogne # Date 1603907341 -3600 # Node ID 595c0952ef7ece87b57b992554a0cbe90ca7a74d # Parent b2941196cabf8cd80a228816dec54e1c70733e0c focusing on osirix annotations in Stone Web viewer diff -r b2941196cabf -r 595c0952ef7e Applications/StoneWebViewer/WebApplication/app.js --- 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); + }); + } +} diff -r b2941196cabf -r 595c0952ef7e Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp --- 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 annotations_; @@ -1578,7 +1579,6 @@ { const TextAnnotation& text = dynamic_cast(annotation); double x, y; - OrthancStone::LinearAlgebra::Print(text.GetCenter()); if (GetCurrentFrameGeometry().ProjectPoint(x, y, text.GetCenter())) { std::unique_ptr 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(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 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 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; + } }