Mercurial > hg > orthanc-stone
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 } |