comparison Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp @ 1593:b782f78aed42

rendering osirix annotations
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 23 Oct 2020 17:39:16 +0200
parents 5887a4f8594b
children 4fb8fdf03314
comparison
equal deleted inserted replaced
1592:0d4b11ba86df 1593:b782f78aed42
17 * You should have received a copy of the GNU Affero General Public License 17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **/ 19 **/
20 20
21 21
22 #include <EmbeddedResources.h>
22 #include <emscripten.h> 23 #include <emscripten.h>
23 24
24 25
25 #define DISPATCH_JAVASCRIPT_EVENT(name) \ 26 #define DISPATCH_JAVASCRIPT_EVENT(name) \
26 EM_ASM( \ 27 EM_ASM( \
70 #include <Oracle/ParseDicomFromWadoCommand.h> 71 #include <Oracle/ParseDicomFromWadoCommand.h>
71 #include <Oracle/ParseDicomSuccessMessage.h> 72 #include <Oracle/ParseDicomSuccessMessage.h>
72 #include <Scene2D/ColorTextureSceneLayer.h> 73 #include <Scene2D/ColorTextureSceneLayer.h>
73 #include <Scene2D/FloatTextureSceneLayer.h> 74 #include <Scene2D/FloatTextureSceneLayer.h>
74 #include <Scene2D/PolylineSceneLayer.h> 75 #include <Scene2D/PolylineSceneLayer.h>
76 #include <Scene2D/TextSceneLayer.h>
75 #include <Scene2DViewport/ViewportController.h> 77 #include <Scene2DViewport/ViewportController.h>
76 #include <StoneException.h> 78 #include <StoneException.h>
77 #include <Toolbox/DicomInstanceParameters.h> 79 #include <Toolbox/DicomInstanceParameters.h>
78 #include <Toolbox/GeometryToolbox.h> 80 #include <Toolbox/GeometryToolbox.h>
81 #include <Toolbox/OsiriX/AngleAnnotation.h>
82 #include <Toolbox/OsiriX/CollectionOfAnnotations.h>
83 #include <Toolbox/OsiriX/LineAnnotation.h>
84 #include <Toolbox/OsiriX/TextAnnotation.h>
79 #include <Toolbox/SortedFrames.h> 85 #include <Toolbox/SortedFrames.h>
80 #include <Viewport/DefaultViewportInteractor.h> 86 #include <Viewport/DefaultViewportInteractor.h>
81 87
82 // WebAssembly includes 88 // WebAssembly includes
83 #include <WebAssemblyCairoViewport.h> 89 #include <WebAssemblyCairoViewport.h>
944 else 950 else
945 { 951 {
946 return false; 952 return false;
947 } 953 }
948 } 954 }
955
956
957 bool ProjectPoint(double& x,
958 double& y,
959 const OrthancStone::Vector& v) const
960 {
961 if (IsValid())
962 {
963 coordinates_.ProjectPoint(x, y, v);
964 return true;
965 }
966 else
967 {
968 return false;
969 }
970 }
949 }; 971 };
950 972
951 973
952 974
953 class ViewerViewport : public OrthancStone::ObserverBase<ViewerViewport> 975 class ViewerViewport : public OrthancStone::ObserverBase<ViewerViewport>
967 }; 989 };
968 990
969 private: 991 private:
970 static const int LAYER_TEXTURE = 0; 992 static const int LAYER_TEXTURE = 0;
971 static const int LAYER_REFERENCE_LINES = 1; 993 static const int LAYER_REFERENCE_LINES = 1;
994 static const int LAYER_ANNOTATIONS = 2;
995 static const int LAYER_TEMP = 3; // TODO - REMOVE
972 996
973 997
974 class ICommand : public Orthanc::IDynamicObject 998 class ICommand : public Orthanc::IDynamicObject
975 { 999 {
976 private: 1000 private:
1050 } 1074 }
1051 1075
1052 GetViewport().DisplayCurrentFrame(); 1076 GetViewport().DisplayCurrentFrame();
1053 } 1077 }
1054 }; 1078 };
1079
1055 1080
1056 class SetLowQualityFrame : public ICommand 1081 class SetLowQualityFrame : public ICommand
1057 { 1082 {
1058 private: 1083 private:
1059 std::string sopInstanceUid_; 1084 std::string sopInstanceUid_;
1280 bool fitNextContent_; 1305 bool fitNextContent_;
1281 bool isCtrlDown_; 1306 bool isCtrlDown_;
1282 FrameGeometry currentFrameGeometry_; 1307 FrameGeometry currentFrameGeometry_;
1283 std::list<PrefetchItem> prefetchQueue_; 1308 std::list<PrefetchItem> prefetchQueue_;
1284 1309
1310 boost::shared_ptr<OrthancStone::OsiriX::CollectionOfAnnotations> annotations_;
1311
1285 void ScheduleNextPrefetch() 1312 void ScheduleNextPrefetch()
1286 { 1313 {
1287 while (!prefetchQueue_.empty()) 1314 while (!prefetchQueue_.empty())
1288 { 1315 {
1289 size_t index = prefetchQueue_.front().GetFrameIndex(); 1316 size_t index = prefetchQueue_.front().GetFrameIndex();
1338 DisplayCurrentFrame(); 1365 DisplayCurrentFrame();
1339 } 1366 }
1340 } 1367 }
1341 } 1368 }
1342 1369
1370
1343 void DisplayCurrentFrame() 1371 void DisplayCurrentFrame()
1344 { 1372 {
1345 DisplayedFrameQuality quality = DisplayedFrameQuality_None; 1373 DisplayedFrameQuality quality = DisplayedFrameQuality_None;
1346 1374
1347 if (cursor_.get() != NULL && 1375 if (cursor_.get() != NULL &&
1399 else 1427 else
1400 { 1428 {
1401 currentFrameGeometry_ = FrameGeometry(); 1429 currentFrameGeometry_ = FrameGeometry();
1402 } 1430 }
1403 } 1431 }
1432
1404 1433
1405 void ClearViewport() 1434 void ClearViewport()
1406 { 1435 {
1407 { 1436 {
1408 std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); 1437 std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock());
1480 double pixelSpacingX, pixelSpacingY; 1509 double pixelSpacingX, pixelSpacingY;
1481 OrthancStone::GeometryToolbox::GetPixelSpacing( 1510 OrthancStone::GeometryToolbox::GetPixelSpacing(
1482 pixelSpacingX, pixelSpacingY, frames_->GetFrameTags(index)); 1511 pixelSpacingX, pixelSpacingY, frames_->GetFrameTags(index));
1483 layer->SetPixelSpacing(pixelSpacingX, pixelSpacingY); 1512 layer->SetPixelSpacing(pixelSpacingX, pixelSpacingY);
1484 1513
1514
1515 /****
1516 * BEGINNING OF EXPERIMENTAL CODE
1517 ****/
1518
1519 std::unique_ptr<OrthancStone::ISceneLayer> annotationsLayer; // TODO - Macro layer
1520 std::unique_ptr<OrthancStone::ISceneLayer> tempLayer; // TODO - Macro layer
1521
1522 if (annotations_)
1523 {
1524 std::set<size_t> a;
1525 annotations_->LookupSopInstanceUid(a, sopInstanceUid);
1526 if (!a.empty())
1527 {
1528 using namespace OrthancStone::OsiriX;
1529
1530 std::unique_ptr<OrthancStone::PolylineSceneLayer> layer(new OrthancStone::PolylineSceneLayer);
1531
1532 for (std::set<size_t>::const_iterator it = a.begin(); it != a.end(); ++it)
1533 {
1534 const Annotation& annotation = annotations_->GetAnnotation(*it);
1535
1536 switch (annotation.GetType())
1537 {
1538 case Annotation::Type_Line:
1539 {
1540 const LineAnnotation& line = dynamic_cast<const LineAnnotation&>(annotation);
1541 double x1, y1, x2, y2;
1542 if (GetCurrentFrameGeometry().ProjectPoint(x1, y1, line.GetPoint1()) &&
1543 GetCurrentFrameGeometry().ProjectPoint(x2, y2, line.GetPoint2()))
1544 {
1545 OrthancStone::PolylineSceneLayer::Chain chain;
1546 chain.push_back(OrthancStone::ScenePoint2D(x1, y1));
1547 chain.push_back(OrthancStone::ScenePoint2D(x2, y2));
1548
1549 // TODO - IsArrow
1550
1551 layer->AddChain(chain, false, 0, 255, 0);
1552 }
1553 break;
1554 }
1555
1556 case Annotation::Type_Angle:
1557 {
1558 const AngleAnnotation& angle = dynamic_cast<const AngleAnnotation&>(annotation);
1559 double x1, y1, x2, y2, x3, y3;
1560 if (GetCurrentFrameGeometry().ProjectPoint(x1, y1, angle.GetA()) &&
1561 GetCurrentFrameGeometry().ProjectPoint(x2, y2, angle.GetCenter()) &&
1562 GetCurrentFrameGeometry().ProjectPoint(x3, y3, angle.GetB()))
1563 {
1564 OrthancStone::PolylineSceneLayer::Chain chain;
1565 chain.push_back(OrthancStone::ScenePoint2D(x1, y1));
1566 chain.push_back(OrthancStone::ScenePoint2D(x2, y2));
1567 chain.push_back(OrthancStone::ScenePoint2D(x3, y3));
1568 layer->AddChain(chain, false, 0, 255, 0);
1569 }
1570 break;
1571 }
1572
1573 case Annotation::Type_Text:
1574 {
1575 const TextAnnotation& text = dynamic_cast<const TextAnnotation&>(annotation);
1576 double x, y;
1577 OrthancStone::LinearAlgebra::Print(text.GetCenter());
1578 if (GetCurrentFrameGeometry().ProjectPoint(x, y, text.GetCenter()))
1579 {
1580 std::unique_ptr<OrthancStone::TextSceneLayer> layer2(new OrthancStone::TextSceneLayer());
1581 layer2->SetPosition(x, y);
1582 layer2->SetText(text.GetText());
1583 layer2->SetAnchor(OrthancStone::BitmapAnchor_Center);
1584 layer2->SetColor(255, 0, 0);
1585 tempLayer.reset(layer2.release());
1586 }
1587 break;
1588 }
1589
1590 default:
1591 LOG(ERROR) << "Annotation type not implemented: " << annotation.GetType();
1592 }
1593 }
1594
1595 annotationsLayer.reset(layer.release());
1596 }
1597 }
1598
1599 /****
1600 * END OF EXPERIMENTAL CODE
1601 ****/
1602
1603
1485 if (layer.get() == NULL) 1604 if (layer.get() == NULL)
1486 { 1605 {
1487 return false; 1606 return false;
1488 } 1607 }
1489 else 1608 else
1491 std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); 1610 std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock());
1492 1611
1493 OrthancStone::Scene2D& scene = lock->GetController().GetScene(); 1612 OrthancStone::Scene2D& scene = lock->GetController().GetScene();
1494 1613
1495 scene.SetLayer(LAYER_TEXTURE, layer.release()); 1614 scene.SetLayer(LAYER_TEXTURE, layer.release());
1615
1616 if (annotationsLayer.get() != NULL)
1617 {
1618 scene.SetLayer(LAYER_ANNOTATIONS, annotationsLayer.release());
1619 }
1620 else
1621 {
1622 scene.DeleteLayer(LAYER_ANNOTATIONS);
1623 }
1624
1625 if (tempLayer.get() != NULL) // TODO - REMOVE
1626 {
1627 scene.SetLayer(LAYER_TEMP, tempLayer.release());
1628 }
1629 else
1630 {
1631 scene.DeleteLayer(LAYER_TEMP);
1632 }
1496 1633
1497 if (fitNextContent_) 1634 if (fitNextContent_)
1498 { 1635 {
1499 lock->RefreshCanvasSize(); 1636 lock->RefreshCanvasSize();
1500 lock->GetCompositor().FitContent(scene); 1637 lock->GetCompositor().FitContent(scene);
1623 } 1760 }
1624 else 1761 else
1625 { 1762 {
1626 LOG(INFO) << "Creating WebGL viewport in canvas: " << canvas; 1763 LOG(INFO) << "Creating WebGL viewport in canvas: " << canvas;
1627 viewport_ = OrthancStone::WebGLViewport::Create(canvas); 1764 viewport_ = OrthancStone::WebGLViewport::Create(canvas);
1765 }
1766
1767 {
1768 std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock());
1769 std::string ttf;
1770 Orthanc::EmbeddedResources::GetFileResource(ttf, Orthanc::EmbeddedResources::UBUNTU_FONT);
1771 lock->GetCompositor().SetFont(0, ttf, 24 /* font size */, Orthanc::Encoding_Latin1);
1628 } 1772 }
1629 1773
1630 emscripten_set_wheel_callback(viewport_->GetCanvasCssSelector().c_str(), this, true, OnWheel); 1774 emscripten_set_wheel_callback(viewport_->GetCanvasCssSelector().c_str(), this, true, OnWheel);
1631 emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, OnKey); 1775 emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, OnKey);
1632 emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, OnKey); 1776 emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, OnKey);
1773 0, source_, uri, new SetDefaultWindowingCommand(GetSharedObserver())); 1917 0, source_, uri, new SetDefaultWindowingCommand(GetSharedObserver()));
1774 } 1918 }
1775 } 1919 }
1776 } 1920 }
1777 1921
1922
1923 void UpdateCurrentFrame()
1924 {
1925 if (cursor_.get() != NULL)
1926 {
1927 unsigned int quality;
1928 DisplayFrame(quality, cursor_->GetCurrentIndex());
1929 }
1930 }
1931
1932
1778 // This method is used when the layout of the HTML page changes, 1933 // This method is used when the layout of the HTML page changes,
1779 // which does not trigger the "emscripten_set_resize_callback()" 1934 // which does not trigger the "emscripten_set_resize_callback()"
1780 void UpdateSize(bool fitContent) 1935 void UpdateSize(bool fitContent)
1781 { 1936 {
1782 std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); 1937 std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock());
1993 2148
1994 void FitForPrint() 2149 void FitForPrint()
1995 { 2150 {
1996 viewport_->FitForPrint(); 2151 viewport_->FitForPrint();
1997 } 2152 }
2153
2154 void SetAnnotations(boost::shared_ptr<OrthancStone::OsiriX::CollectionOfAnnotations> annotations)
2155 {
2156 annotations_ = annotations;
2157 }
2158
1998 }; 2159 };
1999 2160
2000 2161
2001 2162
2002 2163
2003 2164
2004 typedef std::map<std::string, boost::shared_ptr<ViewerViewport> > Viewports; 2165 typedef std::map<std::string, boost::shared_ptr<ViewerViewport> > Viewports;
2005 static Viewports allViewports_; 2166 static Viewports allViewports_;
2006 static bool showReferenceLines_ = true; 2167 static bool showReferenceLines_ = true;
2168 static boost::shared_ptr<OrthancStone::OsiriX::CollectionOfAnnotations> annotations_;
2007 2169
2008 2170
2009 static void UpdateReferenceLines() 2171 static void UpdateReferenceLines()
2010 { 2172 {
2011 if (showReferenceLines_) 2173 if (showReferenceLines_)
2151 std::unique_ptr<OrthancStone::ILoadersContext::ILock> lock(context_->Lock()); 2313 std::unique_ptr<OrthancStone::ILoadersContext::ILock> lock(context_->Lock());
2152 boost::shared_ptr<ViewerViewport> viewport( 2314 boost::shared_ptr<ViewerViewport> viewport(
2153 ViewerViewport::Create(*lock, source_, canvas, cache_, softwareRendering_)); 2315 ViewerViewport::Create(*lock, source_, canvas, cache_, softwareRendering_));
2154 viewport->SetMouseButtonActions(leftButtonAction_, middleButtonAction_, rightButtonAction_); 2316 viewport->SetMouseButtonActions(leftButtonAction_, middleButtonAction_, rightButtonAction_);
2155 viewport->AcquireObserver(new WebAssemblyObserver); 2317 viewport->AcquireObserver(new WebAssemblyObserver);
2318 viewport->SetAnnotations(annotations_);
2156 allViewports_[canvas] = viewport; 2319 allViewports_[canvas] = viewport;
2157 return viewport; 2320 return viewport;
2158 } 2321 }
2159 else 2322 else
2160 { 2323 {
2172 Orthanc::Logging::EnableInfoLevel(true); 2335 Orthanc::Logging::EnableInfoLevel(true);
2173 //Orthanc::Logging::EnableTraceLevel(true); 2336 //Orthanc::Logging::EnableTraceLevel(true);
2174 2337
2175 context_.reset(new OrthancStone::WebAssemblyLoadersContext(1, 4, 1)); 2338 context_.reset(new OrthancStone::WebAssemblyLoadersContext(1, 4, 1));
2176 cache_.reset(new FramesCache); 2339 cache_.reset(new FramesCache);
2340 annotations_.reset(new OrthancStone::OsiriX::CollectionOfAnnotations);
2177 2341
2178 DISPATCH_JAVASCRIPT_EVENT("StoneInitialized"); 2342 DISPATCH_JAVASCRIPT_EVENT("StoneInitialized");
2179 } 2343 }
2180 2344
2181 2345
2536 it->second->FitForPrint(); 2700 it->second->FitForPrint();
2537 } 2701 }
2538 } 2702 }
2539 EXTERN_CATCH_EXCEPTIONS; 2703 EXTERN_CATCH_EXCEPTIONS;
2540 } 2704 }
2705
2706
2707 EMSCRIPTEN_KEEPALIVE
2708 int LoadOsiriXAnnotations(const char* xml,
2709 int clearPreviousAnnotations)
2710 {
2711 try
2712 {
2713 if (clearPreviousAnnotations)
2714 {
2715 annotations_->Clear();
2716 }
2717
2718 annotations_->LoadXml(xml);
2719
2720 for (Viewports::iterator it = allViewports_.begin(); it != allViewports_.end(); ++it)
2721 {
2722 // TODO - Check if the viewport contains one of the SOP
2723 // Instance UID from the loaded annotations => focus on this
2724 // instance
2725
2726 // TODO - If no viewport contains the instance => monitor the
2727 // "ResourcesLoader" as new series get loaded
2728
2729 assert(it->second != NULL);
2730 it->second->UpdateCurrentFrame();
2731 }
2732
2733 LOG(WARNING) << "Loaded " << annotations_->GetSize() << " annotations from OsiriX";
2734 return 1;
2735 }
2736 EXTERN_CATCH_EXCEPTIONS;
2737 return 0;
2738 }
2541 } 2739 }