Mercurial > hg > orthanc-stone
comparison Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp @ 1693:eafb10992e73
synchronized browsing
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 26 Nov 2020 13:46:50 +0100 |
parents | 84fe7089ccaa |
children | 7226b68e2742 |
comparison
equal
deleted
inserted
replaced
1692:e787b52d025f | 1693:eafb10992e73 |
---|---|
1031 virtual void SignalFrameUpdated(const ViewerViewport& viewport, | 1031 virtual void SignalFrameUpdated(const ViewerViewport& viewport, |
1032 size_t currentFrame, | 1032 size_t currentFrame, |
1033 size_t countFrames, | 1033 size_t countFrames, |
1034 DisplayedFrameQuality quality) = 0; | 1034 DisplayedFrameQuality quality) = 0; |
1035 | 1035 |
1036 virtual void SignalCrosshair(const OrthancStone::Vector& click) = 0; | 1036 virtual void SignalCrosshair(const ViewerViewport& viewport, |
1037 const OrthancStone::Vector& click) = 0; | |
1037 }; | 1038 }; |
1038 | 1039 |
1039 private: | 1040 private: |
1040 static const int LAYER_TEXTURE = 0; | 1041 static const int LAYER_TEXTURE = 0; |
1041 static const int LAYER_REFERENCE_LINES = 1; | 1042 static const int LAYER_REFERENCE_LINES = 1; |
1395 unsigned int cineRate_; | 1396 unsigned int cineRate_; |
1396 bool inverted_; | 1397 bool inverted_; |
1397 bool flipX_; | 1398 bool flipX_; |
1398 bool flipY_; | 1399 bool flipY_; |
1399 bool fitNextContent_; | 1400 bool fitNextContent_; |
1400 bool isCtrlDown_; | |
1401 std::list<PrefetchItem> prefetchQueue_; | 1401 std::list<PrefetchItem> prefetchQueue_; |
1402 bool serverSideTranscoding_; | 1402 bool serverSideTranscoding_; |
1403 OrthancStone::Vector synchronizationOffset_; | |
1404 bool synchronizationEnabled_; | |
1403 | 1405 |
1404 | 1406 |
1405 bool hasFocusOnInstance_; | 1407 bool hasFocusOnInstance_; |
1406 std::string focusSopInstanceUid_; | 1408 std::string focusSopInstanceUid_; |
1407 size_t focusFrameNumber_; | 1409 size_t focusFrameNumber_; |
1780 bool softwareRendering) : | 1782 bool softwareRendering) : |
1781 context_(context), | 1783 context_(context), |
1782 source_(source), | 1784 source_(source), |
1783 framesCache_(cache), | 1785 framesCache_(cache), |
1784 fitNextContent_(true), | 1786 fitNextContent_(true), |
1785 isCtrlDown_(false), | |
1786 flipX_(false), | 1787 flipX_(false), |
1787 flipY_(false), | 1788 flipY_(false), |
1788 hasFocusOnInstance_(false), | 1789 hasFocusOnInstance_(false), |
1789 focusFrameNumber_(0) | 1790 focusFrameNumber_(0), |
1791 synchronizationOffset_(OrthancStone::LinearAlgebra::CreateVector(0, 0, 0)), | |
1792 synchronizationEnabled_(false) | |
1790 { | 1793 { |
1791 if (!framesCache_) | 1794 if (!framesCache_) |
1792 { | 1795 { |
1793 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | 1796 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); |
1794 } | 1797 } |
1810 Orthanc::EmbeddedResources::GetFileResource(ttf, Orthanc::EmbeddedResources::UBUNTU_FONT); | 1813 Orthanc::EmbeddedResources::GetFileResource(ttf, Orthanc::EmbeddedResources::UBUNTU_FONT); |
1811 lock->GetCompositor().SetFont(0, ttf, 24 /* font size */, Orthanc::Encoding_Latin1); | 1814 lock->GetCompositor().SetFont(0, ttf, 24 /* font size */, Orthanc::Encoding_Latin1); |
1812 } | 1815 } |
1813 | 1816 |
1814 emscripten_set_wheel_callback(viewport_->GetCanvasCssSelector().c_str(), this, true, OnWheel); | 1817 emscripten_set_wheel_callback(viewport_->GetCanvasCssSelector().c_str(), this, true, OnWheel); |
1815 emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, OnKey); | |
1816 emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, OnKey); | |
1817 | 1818 |
1818 SetWindowingPreset(); | 1819 SetWindowingPreset(); |
1819 } | |
1820 | |
1821 static EM_BOOL OnKey(int eventType, | |
1822 const EmscriptenKeyboardEvent *event, | |
1823 void *userData) | |
1824 { | |
1825 /** | |
1826 * WARNING: There is a problem with Firefox 71 that seems to mess | |
1827 * the "ctrlKey" value. | |
1828 **/ | |
1829 | |
1830 ViewerViewport& that = *reinterpret_cast<ViewerViewport*>(userData); | |
1831 that.isCtrlDown_ = event->ctrlKey; | |
1832 return false; | |
1833 } | 1820 } |
1834 | 1821 |
1835 | 1822 |
1836 static EM_BOOL OnWheel(int eventType, | 1823 static EM_BOOL OnWheel(int eventType, |
1837 const EmscriptenWheelEvent *wheelEvent, | 1824 const EmscriptenWheelEvent *wheelEvent, |
1838 void *userData) | 1825 void *userData) |
1839 { | 1826 { |
1840 ViewerViewport& that = *reinterpret_cast<ViewerViewport*>(userData); | 1827 ViewerViewport& that = *reinterpret_cast<ViewerViewport*>(userData); |
1841 | 1828 |
1842 if (that.cursor_.get() != NULL) | 1829 if (that.frames_.get() != NULL && |
1843 { | 1830 that.cursor_.get() != NULL) |
1831 { | |
1832 const bool isCtrl = wheelEvent->mouse.ctrlKey; | |
1833 const bool isShift = wheelEvent->mouse.shiftKey; | |
1834 | |
1835 const size_t previousCursorIndex = that.cursor_->GetCurrentIndex(); | |
1836 | |
1844 if (wheelEvent->deltaY < 0) | 1837 if (wheelEvent->deltaY < 0) |
1845 { | 1838 { |
1846 that.ChangeFrame(that.isCtrlDown_ ? SeriesCursor::Action_FastMinus : SeriesCursor::Action_Minus, false /* not circular */); | 1839 that.ChangeFrame(isCtrl ? SeriesCursor::Action_FastMinus : |
1840 SeriesCursor::Action_Minus, false /* not circular */); | |
1847 } | 1841 } |
1848 else if (wheelEvent->deltaY > 0) | 1842 else if (wheelEvent->deltaY > 0) |
1849 { | 1843 { |
1850 that.ChangeFrame(that.isCtrlDown_ ? SeriesCursor::Action_FastPlus : SeriesCursor::Action_Plus, false /* not circular */); | 1844 that.ChangeFrame(isCtrl ? SeriesCursor::Action_FastPlus : |
1845 SeriesCursor::Action_Plus, false /* not circular */); | |
1846 } | |
1847 | |
1848 if (that.synchronizationEnabled_) | |
1849 { | |
1850 const size_t currentCursorIndex = that.cursor_->GetCurrentIndex(); | |
1851 | |
1852 const OrthancStone::CoordinateSystem3D current = | |
1853 that.frames_->GetFrameGeometry(currentCursorIndex); | |
1854 | |
1855 if (isShift && | |
1856 previousCursorIndex != currentCursorIndex) | |
1857 { | |
1858 const OrthancStone::CoordinateSystem3D previous = | |
1859 that.frames_->GetFrameGeometry(previousCursorIndex); | |
1860 that.synchronizationOffset_ += previous.GetOrigin() - current.GetOrigin(); | |
1861 } | |
1862 | |
1863 that.observer_->SignalCrosshair(that, current.GetOrigin() + that.synchronizationOffset_); | |
1851 } | 1864 } |
1852 } | 1865 } |
1853 | 1866 |
1854 return true; | 1867 return true; |
1855 } | 1868 } |
1874 { | 1887 { |
1875 // Unregister the callbacks to avoid any call with a "void*" that | 1888 // Unregister the callbacks to avoid any call with a "void*" that |
1876 // has been destroyed. "WebAssemblyViewport::CreateObjectCookie()" | 1889 // has been destroyed. "WebAssemblyViewport::CreateObjectCookie()" |
1877 // provides a more advanced alternative. | 1890 // provides a more advanced alternative. |
1878 emscripten_set_wheel_callback(viewport_->GetCanvasCssSelector().c_str(), this, true, NULL); | 1891 emscripten_set_wheel_callback(viewport_->GetCanvasCssSelector().c_str(), this, true, NULL); |
1879 emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, NULL); | |
1880 emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, NULL); | |
1881 } | 1892 } |
1882 | 1893 |
1883 static boost::shared_ptr<ViewerViewport> Create(OrthancStone::WebAssemblyLoadersContext& context, | 1894 static boost::shared_ptr<ViewerViewport> Create(OrthancStone::WebAssemblyLoadersContext& context, |
1884 const OrthancStone::DicomSource& source, | 1895 const OrthancStone::DicomSource& source, |
1885 const std::string& canvas, | 1896 const std::string& canvas, |
1917 flipY_ = false; | 1928 flipY_ = false; |
1918 fitNextContent_ = true; | 1929 fitNextContent_ = true; |
1919 cineRate_ = DEFAULT_CINE_RATE; | 1930 cineRate_ = DEFAULT_CINE_RATE; |
1920 inverted_ = false; | 1931 inverted_ = false; |
1921 serverSideTranscoding_ = false; | 1932 serverSideTranscoding_ = false; |
1933 OrthancStone::LinearAlgebra::AssignVector(synchronizationOffset_, 0, 0, 0); | |
1922 | 1934 |
1923 frames_.reset(frames); | 1935 frames_.reset(frames); |
1924 cursor_.reset(new SeriesCursor(frames_->GetFramesCount())); | 1936 cursor_.reset(new SeriesCursor(frames_->GetFramesCount())); |
1925 | 1937 |
1926 LOG(INFO) << "Number of frames in series: " << frames_->GetFramesCount(); | 1938 LOG(INFO) << "Number of frames in series: " << frames_->GetFramesCount(); |
2279 lock2->GetController().GetCanvasToSceneTransform().Apply(x, y); | 2291 lock2->GetController().GetCanvasToSceneTransform().Apply(x, y); |
2280 | 2292 |
2281 OrthancStone::Vector click = plane.MapSliceToWorldCoordinates(x, y); | 2293 OrthancStone::Vector click = plane.MapSliceToWorldCoordinates(x, y); |
2282 if (viewer_.observer_.get() != NULL) | 2294 if (viewer_.observer_.get() != NULL) |
2283 { | 2295 { |
2284 viewer_.observer_->SignalCrosshair(click); | 2296 viewer_.observer_->SignalCrosshair(viewer_, click); |
2285 } | 2297 } |
2286 } | 2298 } |
2287 | 2299 |
2288 return NULL; // No need for a tracker, this is just a click | 2300 return NULL; // No need for a tracker, this is just a click |
2289 } | 2301 } |
2382 name += " " + boost::lexical_cast<std::string>(i + 1); | 2394 name += " " + boost::lexical_cast<std::string>(i + 1); |
2383 } | 2395 } |
2384 | 2396 |
2385 Json::Value preset = Json::objectValue; | 2397 Json::Value preset = Json::objectValue; |
2386 preset["name"] = name; | 2398 preset["name"] = name; |
2387 preset["info"] = ("C " + boost::lexical_cast<std::string>(boost::math::iround(c)) + | |
2388 ", W " + boost::lexical_cast<std::string>(boost::math::iround(w))); | |
2389 preset["center"] = c; | 2399 preset["center"] = c; |
2390 preset["width"] = w; | 2400 preset["width"] = w; |
2391 | 2401 preset["info"] = |
2402 ("C " + boost::lexical_cast<std::string>(static_cast<int>(boost::math::iround<double>(c))) + | |
2403 ", W " + boost::lexical_cast<std::string>(static_cast<int>(boost::math::iround<double>(w)))); | |
2404 | |
2392 target.append(preset); | 2405 target.append(preset); |
2393 } | 2406 } |
2407 } | |
2408 | |
2409 | |
2410 void SetSynchronizedBrowsingEnabled(int enabled) | |
2411 { | |
2412 OrthancStone::LinearAlgebra::AssignVector(synchronizationOffset_, 0, 0, 0); | |
2413 synchronizationEnabled_ = enabled; | |
2394 } | 2414 } |
2395 }; | 2415 }; |
2396 | 2416 |
2397 | 2417 |
2398 | 2418 |
2508 quality); | 2528 quality); |
2509 | 2529 |
2510 UpdateReferenceLines(); | 2530 UpdateReferenceLines(); |
2511 } | 2531 } |
2512 | 2532 |
2513 virtual void SignalCrosshair(const OrthancStone::Vector& click) ORTHANC_OVERRIDE | 2533 virtual void SignalCrosshair(const ViewerViewport& viewport, |
2534 const OrthancStone::Vector& click) ORTHANC_OVERRIDE | |
2514 { | 2535 { |
2515 for (Viewports::const_iterator it = allViewports_.begin(); it != allViewports_.end(); ++it) | 2536 for (Viewports::const_iterator it = allViewports_.begin(); it != allViewports_.end(); ++it) |
2516 { | 2537 { |
2517 assert(it->second != NULL); | 2538 assert(it->second.get() != NULL); |
2518 it->second->FocusOnPoint(click); | 2539 if (it->second.get() != &viewport) |
2540 { | |
2541 it->second->FocusOnPoint(click); | |
2542 } | |
2519 } | 2543 } |
2520 } | 2544 } |
2521 | 2545 |
2522 virtual void SignalSeriesPdfLoaded(const std::string& studyInstanceUid, | 2546 virtual void SignalSeriesPdfLoaded(const std::string& studyInstanceUid, |
2523 const std::string& seriesInstanceUid, | 2547 const std::string& seriesInstanceUid, |
3082 GetViewport(canvas)->FormatWindowingPresets(v); | 3106 GetViewport(canvas)->FormatWindowingPresets(v); |
3083 stringBuffer_ = v.toStyledString(); | 3107 stringBuffer_ = v.toStyledString(); |
3084 } | 3108 } |
3085 EXTERN_CATCH_EXCEPTIONS; | 3109 EXTERN_CATCH_EXCEPTIONS; |
3086 } | 3110 } |
3111 | |
3112 | |
3113 EMSCRIPTEN_KEEPALIVE | |
3114 void SetSynchronizedBrowsingEnabled(int enabled) | |
3115 { | |
3116 try | |
3117 { | |
3118 for (Viewports::iterator it = allViewports_.begin(); it != allViewports_.end(); ++it) | |
3119 { | |
3120 assert(it->second != NULL); | |
3121 it->second->SetSynchronizedBrowsingEnabled(enabled); | |
3122 } | |
3123 } | |
3124 EXTERN_CATCH_EXCEPTIONS; | |
3125 } | |
3087 } | 3126 } |