comparison Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp @ 2003:963f28eb40cb deep-learning

integration default->deep-learning
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 02 Nov 2022 15:14:56 +0100
parents 2034ae383cfd e943a84da9ac
children 37d6805b80ee
comparison
equal deleted inserted replaced
1964:2034ae383cfd 2003:963f28eb40cb
94 #include "../../../OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyCairoViewport.h" 94 #include "../../../OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyCairoViewport.h"
95 #include "../../../OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyLoadersContext.h" 95 #include "../../../OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyLoadersContext.h"
96 #include "../../../OrthancStone/Sources/Platforms/WebAssembly/WebGLViewport.h" 96 #include "../../../OrthancStone/Sources/Platforms/WebAssembly/WebGLViewport.h"
97 97
98 98
99 #include <algorithm>
100 #include <boost/make_shared.hpp>
101 #include <boost/math/constants/constants.hpp>
99 #include <boost/math/special_functions/round.hpp> 102 #include <boost/math/special_functions/round.hpp>
100 #include <boost/make_shared.hpp>
101 #include <stdio.h> 103 #include <stdio.h>
102 #include <algorithm> 104
105 static const double PI = boost::math::constants::pi<double>();
103 106
104 #if !defined(STONE_WEB_VIEWER_EXPORT) 107 #if !defined(STONE_WEB_VIEWER_EXPORT)
105 // We are not running ParseWebAssemblyExports.py, but we're compiling the wasm 108 // We are not running ParseWebAssemblyExports.py, but we're compiling the wasm
106 # define STONE_WEB_VIEWER_EXPORT 109 # define STONE_WEB_VIEWER_EXPORT
107 #endif 110 #endif
136 WebViewerAction_Windowing, 139 WebViewerAction_Windowing,
137 WebViewerAction_Zoom, 140 WebViewerAction_Zoom,
138 WebViewerAction_Pan, 141 WebViewerAction_Pan,
139 WebViewerAction_Rotate, 142 WebViewerAction_Rotate,
140 WebViewerAction_Crosshair, 143 WebViewerAction_Crosshair,
144 WebViewerAction_MagnifyingGlass, // New in 2.4
141 145
142 WebViewerAction_CreateAngle, 146 WebViewerAction_CreateAngle,
143 WebViewerAction_CreateCircle, 147 WebViewerAction_CreateCircle,
144 WebViewerAction_CreateSegment, 148 WebViewerAction_CreateLength,
145 WebViewerAction_RemoveMeasure 149 WebViewerAction_RemoveMeasure,
150 WebViewerAction_CreatePixelProbe, // New in 2.4
151 WebViewerAction_CreateEllipseProbe, // New in 2.4
152 WebViewerAction_CreateRectangleProbe, // New in 2.4
153 WebViewerAction_CreateTextAnnotation // New in 2.4
146 }; 154 };
147 155
148 156
149 157
150 static OrthancStone::MouseAction ConvertWebViewerAction(int action) 158 static OrthancStone::MouseAction ConvertWebViewerAction(int action)
161 return OrthancStone::MouseAction_Pan; 169 return OrthancStone::MouseAction_Pan;
162 170
163 case WebViewerAction_Rotate: 171 case WebViewerAction_Rotate:
164 return OrthancStone::MouseAction_Rotate; 172 return OrthancStone::MouseAction_Rotate;
165 173
174 case WebViewerAction_MagnifyingGlass:
175 return OrthancStone::MouseAction_MagnifyingGlass;
176
166 case WebViewerAction_None: 177 case WebViewerAction_None:
167 case WebViewerAction_Crosshair: 178 case WebViewerAction_Crosshair:
168 case WebViewerAction_CreateAngle: 179 case WebViewerAction_CreateAngle:
169 case WebViewerAction_CreateCircle: 180 case WebViewerAction_CreateCircle:
170 case WebViewerAction_CreateSegment: 181 case WebViewerAction_CreateLength:
171 case WebViewerAction_RemoveMeasure: 182 case WebViewerAction_RemoveMeasure:
183 case WebViewerAction_CreatePixelProbe:
184 case WebViewerAction_CreateEllipseProbe:
185 case WebViewerAction_CreateRectangleProbe:
186 case WebViewerAction_CreateTextAnnotation:
172 return OrthancStone::MouseAction_None; 187 return OrthancStone::MouseAction_None;
173 188
174 default: 189 default:
175 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); 190 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
176 } 191 }
1196 return ((framesCount_ == 0 && frame == 0) || 1211 return ((framesCount_ == 0 && frame == 0) ||
1197 (framesCount_ > 0 && frame >= 0 && frame < framesCount_)); 1212 (framesCount_ > 0 && frame >= 0 && frame < framesCount_));
1198 } 1213 }
1199 1214
1200 public: 1215 public:
1201 explicit SeriesCursor(size_t framesCount) : 1216 explicit SeriesCursor(size_t framesCount,
1217 bool startAtMiddle /* Whether to start at the middle frame */) :
1202 framesCount_(framesCount), 1218 framesCount_(framesCount),
1203 currentFrame_(framesCount / 2), // Start at the middle frame 1219 currentFrame_(startAtMiddle ? framesCount / 2 : 0),
1204 isCircularPrefetch_(false), 1220 isCircularPrefetch_(false),
1205 lastAction_(Action_None) 1221 lastAction_(Action_None)
1206 { 1222 {
1207 SetFastDelta(framesCount / 20); 1223 SetFastDelta(framesCount / 20);
1208 UpdatePrefetch(); 1224 UpdatePrefetch();
1579 size_t frame) = 0; 1595 size_t frame) = 0;
1580 1596
1581 virtual void SignalStoneAnnotationAdded(const ViewerViewport& viewport) = 0; 1597 virtual void SignalStoneAnnotationAdded(const ViewerViewport& viewport) = 0;
1582 1598
1583 virtual void SignalStoneAnnotationRemoved(const ViewerViewport& viewport) = 0; 1599 virtual void SignalStoneAnnotationRemoved(const ViewerViewport& viewport) = 0;
1600
1601 virtual void SignalStoneTextAnnotationRequired(const ViewerViewport& viewport,
1602 const OrthancStone::ScenePoint2D& pointedPosition,
1603 const OrthancStone::ScenePoint2D& labelPosition) = 0;
1584 }; 1604 };
1585 1605
1586 private: 1606 private:
1587 static const int LAYER_TEXTURE = 0; 1607 static const int LAYER_TEXTURE = 0;
1588 static const int LAYER_OVERLAY = 1; 1608 static const int LAYER_OVERLAY = 1;
1589 static const int LAYER_ORIENTATION_MARKERS = 2; 1609 static const int LAYER_DEEP_LEARNING = 2;
1590 static const int LAYER_REFERENCE_LINES = 3; 1610 static const int LAYER_ORIENTATION_MARKERS = 3;
1591 static const int LAYER_ANNOTATIONS_OSIRIX = 4; 1611 static const int LAYER_REFERENCE_LINES = 4;
1592 static const int LAYER_ANNOTATIONS_STONE = 5; 1612 static const int LAYER_ANNOTATIONS_OSIRIX = 5;
1593 static const int LAYER_DEEP_LEARNING = 6; 1613 static const int LAYER_ANNOTATIONS_STONE = 6;
1594 1614
1595 1615
1596 class ICommand : public Orthanc::IDynamicObject 1616 class ICommand : public Orthanc::IDynamicObject
1597 { 1617 {
1598 private: 1618 private:
1983 float windowingWidth_; 2003 float windowingWidth_;
1984 std::vector<float> windowingPresetCenters_; 2004 std::vector<float> windowingPresetCenters_;
1985 std::vector<float> windowingPresetWidths_; 2005 std::vector<float> windowingPresetWidths_;
1986 unsigned int cineRate_; 2006 unsigned int cineRate_;
1987 bool inverted_; 2007 bool inverted_;
1988 bool flipX_;
1989 bool flipY_;
1990 bool fitNextContent_; 2008 bool fitNextContent_;
1991 std::list<PrefetchItem> prefetchQueue_; 2009 std::list<PrefetchItem> prefetchQueue_;
1992 bool serverSideTranscoding_; 2010 bool serverSideTranscoding_;
1993 OrthancStone::Vector synchronizationOffset_; 2011 OrthancStone::Vector synchronizationOffset_;
1994 bool synchronizationEnabled_; 2012 bool synchronizationEnabled_;
2006 2024
2007 // The coordinates of Stone annotations are expressed in 2D 2025 // The coordinates of Stone annotations are expressed in 2D
2008 // coordinates of the current texture, with (0,0) corresponding to 2026 // coordinates of the current texture, with (0,0) corresponding to
2009 // the center of the top-left pixel 2027 // the center of the top-left pixel
2010 boost::shared_ptr<OrthancStone::AnnotationsSceneLayer> stoneAnnotations_; 2028 boost::shared_ptr<OrthancStone::AnnotationsSceneLayer> stoneAnnotations_;
2029
2030 bool linearInterpolation_;
2011 2031
2012 boost::shared_ptr<Orthanc::ImageAccessor> deepLearningMask_; 2032 boost::shared_ptr<Orthanc::ImageAccessor> deepLearningMask_;
2013 std::string deepLearningSopInstanceUid_; 2033 std::string deepLearningSopInstanceUid_;
2014 unsigned int deepLearningFrameNumber_; 2034 unsigned int deepLearningFrameNumber_;
2015
2016 2035
2017 void ScheduleNextPrefetch() 2036 void ScheduleNextPrefetch()
2018 { 2037 {
2019 while (!prefetchQueue_.empty()) 2038 while (!prefetchQueue_.empty())
2020 { 2039 {
2148 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); 2167 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
2149 } 2168 }
2150 2169
2151 assert(layer.get() != NULL); 2170 assert(layer.get() != NULL);
2152 2171
2153 layer->SetLinearInterpolation(true); 2172 layer->SetLinearInterpolation(linearInterpolation_);
2154 layer->SetFlipX(flipX_);
2155 layer->SetFlipY(flipY_);
2156 2173
2157 double pixelSpacingX, pixelSpacingY; 2174 double pixelSpacingX, pixelSpacingY;
2158 2175
2159 if (instance.HasPixelSpacing()) 2176 if (instance.HasPixelSpacing())
2160 { 2177 {
2204 { 2221 {
2205 OverlaysRegistry::Accessor accessor(OverlaysRegistry::GetInstance(), instance.GetSopInstanceUid()); 2222 OverlaysRegistry::Accessor accessor(OverlaysRegistry::GetInstance(), instance.GetSopInstanceUid());
2206 if (accessor.IsValid()) 2223 if (accessor.IsValid())
2207 { 2224 {
2208 overlay.reset(accessor.CreateTexture()); 2225 overlay.reset(accessor.CreateTexture());
2209 overlay->SetFlipX(flipX_); 2226 overlay->SetLinearInterpolation(false);
2210 overlay->SetFlipY(flipY_);
2211 } 2227 }
2212 } 2228 }
2213 2229
2214 std::unique_ptr<OrthancStone::MacroSceneLayer> annotationsOsiriX; 2230 std::unique_ptr<OrthancStone::MacroSceneLayer> annotationsOsiriX;
2215 2231
2250 } 2266 }
2251 2267
2252 deepLearningLayer.reset(new OrthancStone::LookupTableTextureSceneLayer(*deepLearningMask_)); 2268 deepLearningLayer.reset(new OrthancStone::LookupTableTextureSceneLayer(*deepLearningMask_));
2253 deepLearningLayer->SetLookupTable(lut); 2269 deepLearningLayer->SetLookupTable(lut);
2254 deepLearningLayer->SetPixelSpacing(pixelSpacingX, pixelSpacingY); 2270 deepLearningLayer->SetPixelSpacing(pixelSpacingX, pixelSpacingY);
2255 deepLearningLayer->SetFlipX(flipX_);
2256 deepLearningLayer->SetFlipY(flipY_);
2257 } 2271 }
2258 2272
2259 StoneAnnotationsRegistry::GetInstance().Load(*stoneAnnotations_, instance.GetSopInstanceUid(), frameIndex); 2273 StoneAnnotationsRegistry::GetInstance().Load(*stoneAnnotations_, instance.GetSopInstanceUid(), frameIndex);
2260 2274
2261 // Orientation markers, new in Stone Web viewer 2.4 2275 // Orientation markers, new in Stone Web viewer 2.4
2521 { 2535 {
2522 dynamic_cast<OrthancStone::FloatTextureSceneLayer&>( 2536 dynamic_cast<OrthancStone::FloatTextureSceneLayer&>(
2523 lock->GetController().GetScene().GetLayer(LAYER_TEXTURE)). 2537 lock->GetController().GetScene().GetLayer(LAYER_TEXTURE)).
2524 SetCustomWindowing(windowingCenter_, windowingWidth_); 2538 SetCustomWindowing(windowingCenter_, windowingWidth_);
2525 } 2539 }
2526
2527 {
2528 OrthancStone::TextureBaseSceneLayer& layer =
2529 dynamic_cast<OrthancStone::TextureBaseSceneLayer&>(
2530 lock->GetController().GetScene().GetLayer(LAYER_TEXTURE));
2531
2532 layer.SetFlipX(flipX_);
2533 layer.SetFlipY(flipY_);
2534 }
2535
2536 if (lock->GetController().GetScene().HasLayer(LAYER_OVERLAY))
2537 {
2538 OrthancStone::TextureBaseSceneLayer& layer =
2539 dynamic_cast<OrthancStone::TextureBaseSceneLayer&>(
2540 lock->GetController().GetScene().GetLayer(LAYER_OVERLAY));
2541
2542 layer.SetFlipX(flipX_);
2543 layer.SetFlipY(flipY_);
2544 }
2545 2540
2546 lock->Invalidate(); 2541 lock->Invalidate();
2547 } 2542 }
2548 } 2543 }
2549 2544
2550 2545
2551 ViewerViewport(OrthancStone::WebAssemblyLoadersContext& context, 2546 ViewerViewport(OrthancStone::WebAssemblyLoadersContext& context,
2552 const OrthancStone::DicomSource& source, 2547 const OrthancStone::DicomSource& source,
2553 const std::string& canvas, 2548 const std::string& canvas,
2554 boost::shared_ptr<FramesCache> cache, 2549 boost::shared_ptr<FramesCache> cache,
2555 bool softwareRendering) : 2550 bool softwareRendering,
2551 bool linearInterpolation) :
2556 context_(context), 2552 context_(context),
2557 source_(source), 2553 source_(source),
2558 framesCache_(cache), 2554 framesCache_(cache),
2559 fitNextContent_(true), 2555 fitNextContent_(true),
2560 flipX_(false),
2561 flipY_(false),
2562 hasFocusOnInstance_(false), 2556 hasFocusOnInstance_(false),
2563 focusFrameNumber_(0), 2557 focusFrameNumber_(0),
2564 synchronizationOffset_(OrthancStone::LinearAlgebra::CreateVector(0, 0, 0)), 2558 synchronizationOffset_(OrthancStone::LinearAlgebra::CreateVector(0, 0, 0)),
2565 synchronizationEnabled_(false), 2559 synchronizationEnabled_(false),
2566 centralPhysicalWidth_(1), 2560 centralPhysicalWidth_(1),
2567 centralPhysicalHeight_(1), 2561 centralPhysicalHeight_(1),
2568 centralPixelSpacingX_(1), 2562 centralPixelSpacingX_(1),
2569 centralPixelSpacingY_(1) 2563 centralPixelSpacingY_(1),
2564 linearInterpolation_(linearInterpolation)
2570 { 2565 {
2571 if (!framesCache_) 2566 if (!framesCache_)
2572 { 2567 {
2573 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); 2568 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
2574 } 2569 }
2594 emscripten_set_wheel_callback(viewport_->GetCanvasCssSelector().c_str(), this, true, OnWheel); 2589 emscripten_set_wheel_callback(viewport_->GetCanvasCssSelector().c_str(), this, true, OnWheel);
2595 2590
2596 SetWindowingPreset(); 2591 SetWindowingPreset();
2597 2592
2598 stoneAnnotations_.reset(new OrthancStone::AnnotationsSceneLayer(LAYER_ANNOTATIONS_STONE)); 2593 stoneAnnotations_.reset(new OrthancStone::AnnotationsSceneLayer(LAYER_ANNOTATIONS_STONE));
2594 stoneAnnotations_->SetProbedLayer(LAYER_TEXTURE);
2599 } 2595 }
2600 2596
2601 2597
2602 void Handle(const OrthancStone::ViewportController::GrayscaleWindowingChanged& message) 2598 void Handle(const OrthancStone::ViewportController::GrayscaleWindowingChanged& message)
2603 { 2599 {
2731 { 2727 {
2732 observer_->SignalStoneAnnotationRemoved(*this); 2728 observer_->SignalStoneAnnotationRemoved(*this);
2733 } 2729 }
2734 } 2730 }
2735 2731
2732 void Handle(const OrthancStone::AnnotationsSceneLayer::TextAnnotationRequiredMessage& message)
2733 {
2734 if (observer_.get() != NULL)
2735 {
2736 observer_->SignalStoneTextAnnotationRequired(*this, message.GetPointedPosition(), message.GetLabelPosition());
2737 }
2738 }
2739
2736 public: 2740 public:
2737 virtual ~ViewerViewport() 2741 virtual ~ViewerViewport()
2738 { 2742 {
2739 // Unregister the callbacks to avoid any call with a "void*" that 2743 // Unregister the callbacks to avoid any call with a "void*" that
2740 // has been destroyed. "WebAssemblyViewport::CreateObjectCookie()" 2744 // has been destroyed. "WebAssemblyViewport::CreateObjectCookie()"
2744 2748
2745 static boost::shared_ptr<ViewerViewport> Create(OrthancStone::WebAssemblyLoadersContext& context, 2749 static boost::shared_ptr<ViewerViewport> Create(OrthancStone::WebAssemblyLoadersContext& context,
2746 const OrthancStone::DicomSource& source, 2750 const OrthancStone::DicomSource& source,
2747 const std::string& canvas, 2751 const std::string& canvas,
2748 boost::shared_ptr<FramesCache> cache, 2752 boost::shared_ptr<FramesCache> cache,
2749 bool softwareRendering) 2753 bool softwareRendering,
2754 bool linearInterpolation)
2750 { 2755 {
2751 boost::shared_ptr<ViewerViewport> viewport( 2756 boost::shared_ptr<ViewerViewport> viewport(
2752 new ViewerViewport(context, source, canvas, cache, softwareRendering)); 2757 new ViewerViewport(context, source, canvas, cache, softwareRendering, linearInterpolation));
2753 2758
2754 { 2759 {
2755 std::unique_ptr<OrthancStone::ILoadersContext::ILock> lock(context.Lock()); 2760 std::unique_ptr<OrthancStone::ILoadersContext::ILock> lock(context.Lock());
2756 2761
2757 viewport->loader_ = OrthancStone::DicomResourcesLoader::Create(*lock); 2762 viewport->loader_ = OrthancStone::DicomResourcesLoader::Create(*lock);
2770 viewport->Register<OrthancStone::AnnotationsSceneLayer::AnnotationAddedMessage>( 2775 viewport->Register<OrthancStone::AnnotationsSceneLayer::AnnotationAddedMessage>(
2771 *viewport->stoneAnnotations_, &ViewerViewport::Handle); 2776 *viewport->stoneAnnotations_, &ViewerViewport::Handle);
2772 2777
2773 viewport->Register<OrthancStone::AnnotationsSceneLayer::AnnotationRemovedMessage>( 2778 viewport->Register<OrthancStone::AnnotationsSceneLayer::AnnotationRemovedMessage>(
2774 *viewport->stoneAnnotations_, &ViewerViewport::Handle); 2779 *viewport->stoneAnnotations_, &ViewerViewport::Handle);
2780
2781 viewport->Register<OrthancStone::AnnotationsSceneLayer::TextAnnotationRequiredMessage>(
2782 *viewport->stoneAnnotations_, &ViewerViewport::Handle);
2775 } 2783 }
2776 2784
2777 { 2785 {
2778 std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport->viewport_->Lock()); 2786 std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport->viewport_->Lock());
2779 viewport->Register<OrthancStone::ViewportController::GrayscaleWindowingChanged>(lock->GetController(), &ViewerViewport::Handle); 2787 viewport->Register<OrthancStone::ViewportController::GrayscaleWindowingChanged>(lock->GetController(), &ViewerViewport::Handle);
2788 if (frames == NULL) 2796 if (frames == NULL)
2789 { 2797 {
2790 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); 2798 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
2791 } 2799 }
2792 2800
2793 flipX_ = false;
2794 flipY_ = false;
2795 fitNextContent_ = true; 2801 fitNextContent_ = true;
2796 cineRate_ = DEFAULT_CINE_RATE; 2802 cineRate_ = DEFAULT_CINE_RATE;
2797 inverted_ = false; 2803 inverted_ = false;
2798 serverSideTranscoding_ = false; 2804 serverSideTranscoding_ = false;
2799 OrthancStone::LinearAlgebra::AssignVector(synchronizationOffset_, 0, 0, 0); 2805 OrthancStone::LinearAlgebra::AssignVector(synchronizationOffset_, 0, 0, 0);
2800 2806
2801 frames_.reset(frames); 2807 frames_.reset(frames);
2802 cursor_.reset(new SeriesCursor(frames_->GetFramesCount())); 2808 cursor_.reset(new SeriesCursor(frames_->GetFramesCount(), false));
2809
2810 if (frames_->GetFramesCount() != 0)
2811 {
2812 const OrthancStone::DicomInstanceParameters& firstInstance = frames_->GetInstanceOfFrame(0);
2813 std::string modality;
2814 if (firstInstance.GetTags().LookupStringValue(modality, Orthanc::DICOM_TAG_MODALITY, false))
2815 {
2816 if (modality == "MR" ||
2817 modality == "CT" ||
2818 modality == "NM" ||
2819 modality == "OPT" ||
2820 modality == "PT" ||
2821 modality == "RTDOSE" ||
2822 modality == "XA")
2823 {
2824 // For series that might correspond to 3D images, use their
2825 // central frame as the first frame to be displayed
2826 cursor_.reset(new SeriesCursor(frames_->GetFramesCount(), true));
2827 }
2828 }
2829 }
2803 2830
2804 LOG(INFO) << "Number of frames in series: " << frames_->GetFramesCount(); 2831 LOG(INFO) << "Number of frames in series: " << frames_->GetFramesCount();
2805 2832
2806 SetWindowingPreset(); 2833 SetWindowingPreset();
2807 ClearViewport(); 2834 ClearViewport();
3100 } 3127 }
3101 } 3128 }
3102 3129
3103 void FlipX() 3130 void FlipX()
3104 { 3131 {
3105 flipX_ = !flipX_; 3132 {
3106 UpdateCurrentTextureParameters(); 3133 std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock());
3134 lock->GetController().GetScene().FlipViewportX(
3135 lock->GetCompositor().GetCanvasWidth(), lock->GetCompositor().GetCanvasHeight());
3136 lock->Invalidate();
3137 }
3107 } 3138 }
3108 3139
3109 void FlipY() 3140 void FlipY()
3110 { 3141 {
3111 flipY_ = !flipY_; 3142 {
3112 UpdateCurrentTextureParameters(); 3143 std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock());
3144 lock->GetController().GetScene().FlipViewportY(
3145 lock->GetCompositor().GetCanvasWidth(), lock->GetCompositor().GetCanvasHeight());
3146 lock->Invalidate();
3147 }
3148 }
3149
3150 void RotateLeft()
3151 {
3152 {
3153 std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock());
3154 lock->GetController().GetScene().RotateViewport(
3155 -PI / 2.0, lock->GetCompositor().GetCanvasWidth(), lock->GetCompositor().GetCanvasHeight());
3156 lock->Invalidate();
3157 }
3158 }
3159
3160 void RotateRight()
3161 {
3162 {
3163 std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock());
3164 lock->GetController().GetScene().RotateViewport(
3165 PI / 2.0, lock->GetCompositor().GetCanvasWidth(), lock->GetCompositor().GetCanvasHeight());
3166 lock->Invalidate();
3167 }
3113 } 3168 }
3114 3169
3115 void Invert() 3170 void Invert()
3116 { 3171 {
3117 inverted_ = !inverted_; 3172 inverted_ = !inverted_;
3222 3277
3223 case WebViewerAction_CreateCircle: 3278 case WebViewerAction_CreateCircle:
3224 viewer_.stoneAnnotations_->SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Circle); 3279 viewer_.stoneAnnotations_->SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Circle);
3225 break; 3280 break;
3226 3281
3227 case WebViewerAction_CreateSegment: 3282 case WebViewerAction_CreateLength:
3228 viewer_.stoneAnnotations_->SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Segment); 3283 viewer_.stoneAnnotations_->SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Length);
3229 break; 3284 break;
3230 3285
3231 case WebViewerAction_RemoveMeasure: 3286 case WebViewerAction_RemoveMeasure:
3232 viewer_.stoneAnnotations_->SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Remove); 3287 viewer_.stoneAnnotations_->SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Remove);
3288 break;
3289
3290 case WebViewerAction_CreatePixelProbe:
3291 viewer_.stoneAnnotations_->SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_PixelProbe);
3292 break;
3293
3294 case WebViewerAction_CreateEllipseProbe:
3295 viewer_.stoneAnnotations_->SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_EllipseProbe);
3296 break;
3297
3298 case WebViewerAction_CreateRectangleProbe:
3299 viewer_.stoneAnnotations_->SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_RectangleProbe);
3300 break;
3301
3302 case WebViewerAction_CreateTextAnnotation:
3303 viewer_.stoneAnnotations_->SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_TextAnnotation);
3233 break; 3304 break;
3234 3305
3235 default: 3306 default:
3236 viewer_.stoneAnnotations_->SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Edit); 3307 viewer_.stoneAnnotations_->SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Edit);
3237 break; 3308 break;
3400 stoneAnnotations_->Render(lock->GetController().GetScene()); 3471 stoneAnnotations_->Render(lock->GetController().GetScene());
3401 lock->Invalidate(); 3472 lock->Invalidate();
3402 } 3473 }
3403 } 3474 }
3404 } 3475 }
3476 }
3477
3478
3479 void SetLinearInterpolation(bool linearInterpolation)
3480 {
3481 if (linearInterpolation_ != linearInterpolation)
3482 {
3483 linearInterpolation_ = linearInterpolation;
3484 Redraw();
3485 }
3486 }
3487
3488
3489 void AddTextAnnotation(const std::string& label,
3490 const OrthancStone::ScenePoint2D& pointedPosition,
3491 const OrthancStone::ScenePoint2D& labelPosition)
3492 {
3493 stoneAnnotations_->AddTextAnnotation(label, pointedPosition, labelPosition);
3494 Redraw();
3405 } 3495 }
3406 3496
3407 3497
3408 bool GetCurrentFrame(std::string& sopInstanceUid /* out */, 3498 bool GetCurrentFrame(std::string& sopInstanceUid /* out */,
3409 unsigned int& frameNumber /* out */) const 3499 unsigned int& frameNumber /* out */) const
3698 { "canvasId" : UTF8ToString($0) }); 3788 { "canvasId" : UTF8ToString($0) });
3699 window.dispatchEvent(customEvent); 3789 window.dispatchEvent(customEvent);
3700 }, 3790 },
3701 viewport.GetCanvasId().c_str()); 3791 viewport.GetCanvasId().c_str());
3702 } 3792 }
3793
3794 virtual void SignalStoneTextAnnotationRequired(const ViewerViewport& viewport,
3795 const OrthancStone::ScenePoint2D& pointedPosition,
3796 const OrthancStone::ScenePoint2D& labelPosition) ORTHANC_OVERRIDE
3797 {
3798 EM_ASM({
3799 const customEvent = document.createEvent("CustomEvent");
3800 customEvent.initCustomEvent("TextAnnotationRequired", false, false,
3801 { "canvasId" : UTF8ToString($0),
3802 "pointedX" : $1,
3803 "pointedY" : $2,
3804 "labelX" : $3,
3805 "labelY" : $4 });
3806 window.dispatchEvent(customEvent);
3807 },
3808 viewport.GetCanvasId().c_str(),
3809 pointedPosition.GetX(),
3810 pointedPosition.GetY(),
3811 labelPosition.GetX(),
3812 labelPosition.GetY() );
3813 }
3703 }; 3814 };
3704 3815
3705 3816
3706 3817
3707 static OrthancStone::DicomSource source_; 3818 static OrthancStone::DicomSource source_;
3708 static boost::shared_ptr<FramesCache> framesCache_; 3819 static boost::shared_ptr<FramesCache> framesCache_;
3709 static boost::shared_ptr<OrthancStone::WebAssemblyLoadersContext> context_; 3820 static boost::shared_ptr<OrthancStone::WebAssemblyLoadersContext> context_;
3710 static std::string stringBuffer_; 3821 static std::string stringBuffer_;
3711 static bool softwareRendering_ = false; 3822 static bool softwareRendering_ = false;
3823 static bool linearInterpolation_ = true;
3712 static WebViewerAction leftButtonAction_ = WebViewerAction_Windowing; 3824 static WebViewerAction leftButtonAction_ = WebViewerAction_Windowing;
3713 static WebViewerAction middleButtonAction_ = WebViewerAction_Pan; 3825 static WebViewerAction middleButtonAction_ = WebViewerAction_Pan;
3714 static WebViewerAction rightButtonAction_ = WebViewerAction_Zoom; 3826 static WebViewerAction rightButtonAction_ = WebViewerAction_Zoom;
3715 3827
3716 3828
3753 { 3865 {
3754 Viewports::iterator found = allViewports_.find(canvas); 3866 Viewports::iterator found = allViewports_.find(canvas);
3755 if (found == allViewports_.end()) 3867 if (found == allViewports_.end())
3756 { 3868 {
3757 boost::shared_ptr<ViewerViewport> viewport( 3869 boost::shared_ptr<ViewerViewport> viewport(
3758 ViewerViewport::Create(*context_, source_, canvas, framesCache_, softwareRendering_)); 3870 ViewerViewport::Create(*context_, source_, canvas, framesCache_, softwareRendering_, linearInterpolation_));
3759 viewport->SetMouseButtonActions(leftButtonAction_, middleButtonAction_, rightButtonAction_); 3871 viewport->SetMouseButtonActions(leftButtonAction_, middleButtonAction_, rightButtonAction_);
3760 viewport->AcquireObserver(new WebAssemblyObserver); 3872 viewport->AcquireObserver(new WebAssemblyObserver);
3761 viewport->SetOsiriXAnnotations(osiriXAnnotations_); 3873 viewport->SetOsiriXAnnotations(osiriXAnnotations_);
3762 allViewports_[canvas] = viewport; 3874 allViewports_[canvas] = viewport;
3763 return viewport; 3875 return viewport;
4492 EXTERN_CATCH_EXCEPTIONS; 4604 EXTERN_CATCH_EXCEPTIONS;
4493 } 4605 }
4494 4606
4495 4607
4496 EMSCRIPTEN_KEEPALIVE 4608 EMSCRIPTEN_KEEPALIVE
4609 void RotateLeft(const char* canvas)
4610 {
4611 try
4612 {
4613 GetViewport(canvas)->RotateLeft();
4614 }
4615 EXTERN_CATCH_EXCEPTIONS;
4616 }
4617
4618
4619 EMSCRIPTEN_KEEPALIVE
4620 void RotateRight(const char* canvas)
4621 {
4622 try
4623 {
4624 GetViewport(canvas)->RotateRight();
4625 }
4626 EXTERN_CATCH_EXCEPTIONS;
4627 }
4628
4629
4630 EMSCRIPTEN_KEEPALIVE
4497 void SetSoftwareRendering(int softwareRendering) 4631 void SetSoftwareRendering(int softwareRendering)
4498 { 4632 {
4499 softwareRendering_ = softwareRendering; 4633 softwareRendering_ = softwareRendering;
4500 } 4634 }
4501 4635
4502 4636
4503 EMSCRIPTEN_KEEPALIVE 4637 EMSCRIPTEN_KEEPALIVE
4504 int IsSoftwareRendering() 4638 int IsSoftwareRendering()
4505 { 4639 {
4506 return softwareRendering_; 4640 return softwareRendering_;
4641 }
4642
4643
4644 EMSCRIPTEN_KEEPALIVE
4645 void SetLinearInterpolation(int linearInterpolation)
4646 {
4647 linearInterpolation_ = linearInterpolation;
4648
4649 try
4650 {
4651 for (Viewports::iterator it = allViewports_.begin(); it != allViewports_.end(); ++it)
4652 {
4653 assert(it->second != NULL);
4654 it->second->SetLinearInterpolation(linearInterpolation);
4655 }
4656 }
4657 EXTERN_CATCH_EXCEPTIONS;
4507 } 4658 }
4508 4659
4509 4660
4510 EMSCRIPTEN_KEEPALIVE 4661 EMSCRIPTEN_KEEPALIVE
4511 void SetMouseButtonActions(int leftAction, 4662 void SetMouseButtonActions(int leftAction,
4716 } 4867 }
4717 } 4868 }
4718 EXTERN_CATCH_EXCEPTIONS; 4869 EXTERN_CATCH_EXCEPTIONS;
4719 return false; 4870 return false;
4720 } 4871 }
4872
4873
4874 EMSCRIPTEN_KEEPALIVE
4875 void AddTextAnnotation(const char* canvas,
4876 const char* label,
4877 double pointedX,
4878 double pointedY,
4879 double labelX,
4880 double labelY)
4881 {
4882 try
4883 {
4884 GetViewport(canvas)->AddTextAnnotation(label, OrthancStone::ScenePoint2D(pointedX, pointedY),
4885 OrthancStone::ScenePoint2D(labelX, labelY));
4886 }
4887 EXTERN_CATCH_EXCEPTIONS;
4888 }
4721 } 4889 }