# HG changeset patch # User Benjamin Golinvaux # Date 1587974508 -7200 # Node ID 24bcff8ea58f4c1ca37c98207995bb60f75cff18 # Parent ab871499ed3030141727e307b153e43bc5d824ad RtViewer : SDL ok. Preparation for WASM builds ongoing diff -r ab871499ed30 -r 24bcff8ea58f Samples/Sdl/RtViewer/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Sdl/RtViewer/CMakeLists.txt Mon Apr 27 10:01:48 2020 +0200 @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 2.8.10) + +project(RtViewerSdl) + +set(ORTHANC_FRAMEWORK_SOURCE "path") +set(ORTHANC_FRAMEWORK_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../../../orthanc) +set(STONE_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../../) + +include(${STONE_ROOT}/Resources/CMake/OrthancStoneParameters.cmake) +include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake) + +SET(ENABLE_GOOGLE_TEST OFF) +SET(ENABLE_LOCALE ON) # Necessary for text rendering +SET(ENABLE_QT OFF) +SET(ENABLE_SDL ON) +SET(ENABLE_DCMTK ON) # <== +SET(ENABLE_OPENGL ON) # <== +SET(ENABLE_WEB_CLIENT ON) +SET(ORTHANC_SANDBOXED OFF) + +DownloadPackage( + "a24b8136b8f3bb93f166baf97d9328de" + "http://orthanc.osimis.io/ThirdPartyDownloads/ubuntu-font-family-0.83.zip" + "${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83") + +set(ORTHANC_STONE_APPLICATION_RESOURCES + UBUNTU_FONT ${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83/Ubuntu-R.ttf + ) + +include(${STONE_ROOT}/Resources/CMake/OrthancStoneConfiguration.cmake) +include(${STONE_ROOT}/Resources/CMake/Utilities.cmake) + +include_directories(${STONE_ROOT}) + +add_definitions( + -DORTHANC_ENABLE_LOGGING=1 + -DORTHANC_ENABLE_LOGGING_PLUGIN=0 + -DORTHANC_ENABLE_PUGIXML=0 + -DORTHANC_DEFAULT_DICOM_ENCODING=Encoding_Latin1 + ) + +SortFilesInSourceGroups() + +add_executable(RtViewerSdl + ../SdlHelpers.h + RtViewerSdl.cpp + ../../Common/RtViewer.cpp + ../../Common/RtViewer.h + ${ORTHANC_STONE_SOURCES} + ) + + +target_link_libraries(RtViewerSdl ${DCMTK_LIBRARIES}) + diff -r ab871499ed30 -r 24bcff8ea58f Samples/Sdl/RtViewer/RtViewerSdl.cpp diff -r ab871499ed30 -r 24bcff8ea58f Samples/WebAssembly/RtViewer/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/WebAssembly/RtViewer/CMakeLists.txt Mon Apr 27 10:01:48 2020 +0200 @@ -0,0 +1,78 @@ +cmake_minimum_required(VERSION 2.8.3) + +project(RtViewerWasm) + +# Configuration of the Emscripten compiler for WebAssembly target +# --------------------------------------------------------------- +set(USE_WASM ON CACHE BOOL "") +set(ORTHANC_FRAMEWORK_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../../../orthanc CACHE STRING "") +set(STONE_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../../) + +set(EMSCRIPTEN_SET_LLVM_WASM_BACKEND ON CACHE BOOL "") + +set(WASM_FLAGS "-s WASM=1 -s FETCH=1") +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(WASM_FLAGS "${WASM_FLAGS} -s SAFE_HEAP=1") +endif() + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}") + +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ASSERTIONS=1 -s DISABLE_EXCEPTION_CATCHING=0") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=268435456") # 256MB + resize +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1") +add_definitions( + -DDISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1 +) + +# Stone of Orthanc configuration +# --------------------------------------------------------------- +set(ALLOW_DOWNLOADS ON) +set(ORTHANC_FRAMEWORK_SOURCE "path") + +include(${STONE_ROOT}/Resources/CMake/OrthancStoneParameters.cmake) + +SET(ENABLE_DCMTK ON) +SET(ENABLE_GOOGLE_TEST OFF) +SET(ENABLE_LOCALE ON) # Necessary for text rendering +SET(ENABLE_WASM ON) +SET(ORTHANC_SANDBOXED ON) + +# this will set up the build system for Stone of Orthanc and will +# populate the ORTHANC_STONE_SOURCES CMake variable +include(${STONE_ROOT}/Resources/CMake/OrthancStoneConfiguration.cmake) + +include_directories(${STONE_ROOT}) + +# Define the WASM module +# --------------------------------------------------------------- +add_executable(RtViewerWasm + RtViewerWasm.cpp + ../../Common/RtViewer.cpp + ../../Common/RtViewer.h + + ${ORTHANC_STONE_SOURCES} + ) + +# Declare installation files for the module +# --------------------------------------------------------------- +install( + TARGETS CtDoseStructViewerWasm + RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} + ) + +# Declare installation files for the companion files (web scaffolding) +# please note that ${CMAKE_CURRENT_BINARY_DIR}/SingleFrameViewerWasm.js +# (the generated JS loader for the WASM module) is handled by the `install1` +# section above +# --------------------------------------------------------------- +install( + FILES + ${CMAKE_SOURCE_DIR}/CtDoseStructViewerApp.js + ${CMAKE_SOURCE_DIR}/index.html + ${CMAKE_CURRENT_BINARY_DIR}/CtDoseStructViewerWasm.wasm + ${CMAKE_SOURCE_DIR}/WasmWrapper.js + DESTINATION ${CMAKE_INSTALL_PREFIX} + ) diff -r ab871499ed30 -r 24bcff8ea58f Samples/WebAssembly/RtViewer/OSBOLETE.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/WebAssembly/RtViewer/OSBOLETE.cpp Mon Apr 27 10:01:48 2020 +0200 @@ -0,0 +1,559 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + + +class ViewportManager; + +static const unsigned int FONT_SIZE = 32; + +boost::shared_ptr ct_(new OrthancStone::DicomVolumeImage); +boost::shared_ptr loader_; +std::unique_ptr widget1_; +std::unique_ptr widget2_; +std::unique_ptr widget3_; +//OrthancStone::MessageBroker broker_; +//OrthancStone::WebAssemblyOracle oracle_(broker_); +std::unique_ptr tracker_; +static std::map arguments_; +static bool ctrlDown_ = false; + + +#if 0 + +// use the one from WebAssemblyViewport +static OrthancStone::PointerEvent* ConvertMouseEvent( + const EmscriptenMouseEvent& source, + OrthancStone::IViewport& viewport) +{ + + std::unique_ptr target( + new OrthancStone::PointerEvent); + + target->AddPosition(viewport.GetPixelCenterCoordinates( + source.targetX, source.targetY)); + target->SetAltModifier(source.altKey); + target->SetControlModifier(source.ctrlKey); + target->SetShiftModifier(source.shiftKey); + + return target.release(); +} +#endif + + +EM_BOOL OnMouseEvent(int eventType, + const EmscriptenMouseEvent *mouseEvent, + void *userData) +{ + if (mouseEvent != NULL && + userData != NULL) + { + boost::shared_ptr& viewport = + *reinterpret_cast*>(userData); + + std::unique_ptr lock = (*viewport)->Lock(); + ViewportController& controller = lock->GetController(); + Scene2D& scene = controller.GetScene(); + + switch (eventType) + { + case EMSCRIPTEN_EVENT_CLICK: + { + static unsigned int count = 0; + char buf[64]; + sprintf(buf, "click %d", count++); + + std::unique_ptr layer(new OrthancStone::TextSceneLayer); + layer->SetText(buf); + scene.SetLayer(100, layer.release()); + lock->Invalidate(); + break; + } + + case EMSCRIPTEN_EVENT_MOUSEDOWN: + { + boost::shared_ptr t; + + { + std::unique_ptr event( + ConvertMouseEvent(*mouseEvent, controller->GetViewport())); + + switch (mouseEvent->button) + { + case 0: // Left button + emscripten_console_log("Creating RotateSceneTracker"); + t.reset(new OrthancStone::RotateSceneTracker( + viewport, *event)); + break; + + case 1: // Middle button + emscripten_console_log("Creating PanSceneTracker"); + LOG(INFO) << "Creating PanSceneTracker" ; + t.reset(new OrthancStone::PanSceneTracker( + viewport, *event)); + break; + + case 2: // Right button + emscripten_console_log("Creating ZoomSceneTracker"); + t.reset(new OrthancStone::ZoomSceneTracker( + viewport, *event, controller->GetViewport().GetCanvasWidth())); + break; + + default: + break; + } + } + + if (t.get() != NULL) + { + tracker_.reset( + new OrthancStone::ActiveTracker(t, controller->GetViewport().GetCanvasIdentifier())); + controller->GetViewport().Refresh(); + } + + break; + } + + case EMSCRIPTEN_EVENT_MOUSEMOVE: + if (tracker_.get() != NULL) + { + std::unique_ptr event( + ConvertMouseEvent(*mouseEvent, controller->GetViewport())); + tracker_->PointerMove(*event); + controller->GetViewport().Refresh(); + } + break; + + case EMSCRIPTEN_EVENT_MOUSEUP: + if (tracker_.get() != NULL) + { + std::unique_ptr event( + ConvertMouseEvent(*mouseEvent, controller->GetViewport())); + tracker_->PointerUp(*event); + controller->GetViewport().Refresh(); + if (!tracker_->IsAlive()) + tracker_.reset(); + } + break; + + default: + break; + } + } + + return true; +} + + +void SetupEvents(const std::string& canvas, + boost::shared_ptr& viewport) +{ + emscripten_set_mousedown_callback(canvas.c_str(), &viewport, false, OnMouseEvent); + emscripten_set_mousemove_callback(canvas.c_str(), &viewport, false, OnMouseEvent); + emscripten_set_mouseup_callback(canvas.c_str(), &viewport, false, OnMouseEvent); +} + + class ViewportManager : public OrthanStone::ObserverBase + { + private: + OrthancStone::WebAssemblyViewport viewport_; + std::unique_ptr source_; + VolumeProjection projection_; + std::vector planes_; + size_t currentPlane_; + + void Handle(const DicomVolumeImage::GeometryReadyMessage& message) + { + LOG(INFO) << "Geometry is available"; + + const VolumeImageGeometry& geometry = message.GetOrigin().GetGeometry(); + + const unsigned int depth = geometry.GetProjectionDepth(projection_); + + // select an initial cutting plane halfway through the volume + currentPlane_ = depth / 2; + + planes_.resize(depth); + + for (unsigned int z = 0; z < depth; z++) + { + planes_[z] = geometry.GetProjectionSlice(projection_, z); + } + + Refresh(); + + viewport_.FitContent(); + } + + public: + ViewportManager(const std::string& canvas, + VolumeProjection projection) : + projection_(projection), + currentPlane_(0) + { + viewport_ = OrthancStone::WebGLViewport::Create(canvas); + } + + void UpdateSize() + { + viewport_.UpdateSize(); + } + + void SetSlicer(int layerDepth, + const boost::shared_ptr& slicer, + IObservable& loader, + ILayerStyleConfigurator* configurator) + { + if (source_.get() != NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, + "Only one slicer can be registered"); + } + + loader.RegisterObserverCallback( + new Callable + (*this, &ViewportManager::Handle)); + + source_.reset(new VolumeSceneLayerSource(viewport_.GetScene(), layerDepth, slicer)); + + if (configurator != NULL) + { + source_->SetConfigurator(configurator); + } + } + + void Refresh() + { + if (source_.get() != NULL && + currentPlane_ < planes_.size()) + { + source_->Update(planes_[currentPlane_]); + viewport_.Refresh(); + } + } + + size_t GetSlicesCount() const + { + return planes_.size(); + } + + void Scroll(int delta) + { + if (!planes_.empty()) + { + int tmp = static_cast(currentPlane_) + delta; + unsigned int next; + + if (tmp < 0) + { + next = 0; + } + else if (tmp >= static_cast(planes_.size())) + { + next = planes_.size() - 1; + } + else + { + next = static_cast(tmp); + } + + if (next != currentPlane_) + { + currentPlane_ = next; + Refresh(); + } + } + } + }; +} + + +EM_BOOL OnWindowResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData) +{ + try + { + if (widget1_.get() != NULL) + { + widget1_->UpdateSize(); + } + + if (widget2_.get() != NULL) + { + widget2_->UpdateSize(); + } + + if (widget3_.get() != NULL) + { + widget3_->UpdateSize(); + } + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception while updating canvas size: " << e.What(); + } + + return true; +} + +EM_BOOL OnAnimationFrame(double time, void *userData) +{ + try + { + if (widget1_.get() != NULL) + { + widget1_->Refresh(); + } + + if (widget2_.get() != NULL) + { + widget2_->Refresh(); + } + + if (widget3_.get() != NULL) + { + widget3_->Refresh(); + } + + return true; + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception in the animation loop, stopping now: " << e.What(); + return false; + } +} + +EM_BOOL OnMouseWheel(int eventType, + const EmscriptenWheelEvent *wheelEvent, + void *userData) +{ + try + { + if (userData != NULL) + { + int delta = 0; + + if (wheelEvent->deltaY < 0) + { + delta = -1; + } + + if (wheelEvent->deltaY > 0) + { + delta = 1; + } + + OrthancStone::ViewportManager& widget = + *reinterpret_cast(userData); + + if (ctrlDown_) + { + delta *= static_cast(widget.GetSlicesCount() / 10); + } + + widget.Scroll(delta); + } + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception in the wheel event: " << e.What(); + } + + return true; +} + + +EM_BOOL OnKeyDown(int eventType, + const EmscriptenKeyboardEvent *keyEvent, + void *userData) +{ + ctrlDown_ = keyEvent->ctrlKey; + return false; +} + + +EM_BOOL OnKeyUp(int eventType, + const EmscriptenKeyboardEvent *keyEvent, + void *userData) +{ + ctrlDown_ = false; + return false; +} + + + +#if 0 +namespace OrthancStone +{ + class TestSleep : public IObserver + { + private: + WebAssemblyOracle& oracle_; + + void Schedule() + { + oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(2000)); + } + + void Handle(const SleepOracleCommand::TimeoutMessage& message) + { + LOG(INFO) << "TIMEOUT"; + Schedule(); + } + + public: + TestSleep(MessageBroker& broker, + WebAssemblyOracle& oracle) : + IObserver(broker), + oracle_(oracle) + { + oracle.RegisterObserverCallback( + new Callable + (*this, &TestSleep::Handle)); + + LOG(INFO) << "STARTING"; + Schedule(); + } + }; + + //static TestSleep testSleep(broker_, oracle_); +} +#endif + +static bool GetArgument(std::string& value, + const std::string& key) +{ + std::map::const_iterator found = arguments_.find(key); + + if (found == arguments_.end()) + { + return false; + } + else + { + value = found->second; + return true; + } +} + + +extern "C" +{ + int main(int argc, char const *argv[]) + { + OrthancStone::StoneInitialize(); + Orthanc::Logging::EnableInfoLevel(true); + // Orthanc::Logging::EnableTraceLevel(true); + EM_ASM(window.dispatchEvent(new CustomEvent("WebAssemblyLoaded"));); + } + + EMSCRIPTEN_KEEPALIVE + void SetArgument(const char* key, const char* value) + { + // This is called for each GET argument (cf. "app.js") + LOG(INFO) << "Received GET argument: [" << key << "] = [" << value << "]"; + arguments_[key] = value; + } + + EMSCRIPTEN_KEEPALIVE + void Initialize() + { + try + { + oracle_.SetOrthancRoot(".."); + + loader_.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct_, oracle_, oracle_)); + + widget1_.reset(new OrthancStone::ViewportManager("mycanvas1", OrthancStone::VolumeProjection_Axial)); + { + std::unique_ptr style(new OrthancStone::GrayscaleStyleConfigurator); + style->SetLinearInterpolation(true); + style->SetWindowing(OrthancStone::ImageWindowing_Bone); + widget1_->SetSlicer(0, loader_, *loader_, style.release()); + } + widget1_->UpdateSize(); + + widget2_.reset(new OrthancStone::ViewportManager("mycanvas2", OrthancStone::VolumeProjection_Coronal)); + { + std::unique_ptr style(new OrthancStone::GrayscaleStyleConfigurator); + style->SetLinearInterpolation(true); + style->SetWindowing(OrthancStone::ImageWindowing_Bone); + widget2_->SetSlicer(0, loader_, *loader_, style.release()); + } + widget2_->UpdateSize(); + + widget3_.reset(new OrthancStone::ViewportManager("mycanvas3", OrthancStone::VolumeProjection_Sagittal)); + { + std::unique_ptr style(new OrthancStone::GrayscaleStyleConfigurator); + style->SetLinearInterpolation(true); + style->SetWindowing(OrthancStone::ImageWindowing_Bone); + widget3_->SetSlicer(0, loader_, *loader_, style.release()); + } + widget3_->UpdateSize(); + + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnWindowResize); // DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1 !! + + emscripten_set_wheel_callback("#mycanvas1", widget1_.get(), false, OnMouseWheel); + emscripten_set_wheel_callback("#mycanvas2", widget2_.get(), false, OnMouseWheel); + emscripten_set_wheel_callback("#mycanvas3", widget3_.get(), false, OnMouseWheel); + + emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnKeyDown); + emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnKeyUp); + + //emscripten_request_animation_frame_loop(OnAnimationFrame, NULL); + + std::string ct; + if (GetArgument(ct, "ct")) + { + //loader_->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); + loader_->LoadSeries(ct); + } + else + { + LOG(ERROR) << "No Orthanc identifier for the CT series was provided"; + } + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception during Initialize(): " << e.What(); + } + } +} + diff -r ab871499ed30 -r 24bcff8ea58f Samples/WebAssembly/RtViewer/RtViewerWasm.cpp diff -r ab871499ed30 -r 24bcff8ea58f Samples/WebAssembly/RtViewer/RtViewerWasm.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/WebAssembly/RtViewer/RtViewerWasm.html Mon Apr 27 10:01:48 2020 +0200 @@ -0,0 +1,60 @@ + + + + + + + + + + + + Stone of Orthanc + + + + + + + + + + + + diff -r ab871499ed30 -r 24bcff8ea58f Samples/WebAssembly/RtViewer/RtViewerWasmApp.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/WebAssembly/RtViewer/RtViewerWasmApp.js Mon Apr 27 10:01:48 2020 +0200 @@ -0,0 +1,28 @@ +// Check support for WebAssembly +if (!('WebAssembly' in window)) { + alert('Sorry, your browser does not support WebAssembly :('); +} else { + + // Wait for the module to be loaded (the event "WebAssemblyLoaded" + // must be emitted by the "main" function) + window.addEventListener('WebAssemblyLoaded', function() { + + // Loop over the GET arguments + var parameters = window.location.search.substr(1); + if (parameters != null && parameters != '') { + var tokens = parameters.split('&'); + for (var i = 0; i < tokens.length; i++) { + var arg = tokens[i].split('='); + if (arg.length == 2) { + + // Send each GET argument to WebAssembly + Module.ccall('SetArgument', null, [ 'string', 'string' ], + [ arg[0], decodeURIComponent(arg[1]) ]); + } + } + } + + // Inform the WebAssembly module that it can start + Module.ccall('Initialize', null, null, null); + }); +} diff -r ab871499ed30 -r 24bcff8ea58f Samples/WebAssembly/RtViewer/RtViewerWasmWrapper.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/WebAssembly/RtViewer/RtViewerWasmWrapper.js Mon Apr 27 10:01:48 2020 +0200 @@ -0,0 +1,27 @@ + +const Stone = function() { + this._InitializeViewport = undefined; + this._LoadOrthanc = undefined; + this._LoadDicomWeb = undefined; +}; + +Stone.prototype.Setup = function(Module) { + this._InitializeViewport = Module.cwrap('InitializeViewport', null, [ 'string' ]); + this._LoadOrthanc = Module.cwrap('LoadOrthanc', null, [ 'string', 'int' ]); + this._LoadDicomWeb = Module.cwrap('LoadDicomWeb', null, [ 'string', 'string', 'string', 'string', 'int' ]); +}; + +Stone.prototype.InitializeViewport = function(canvasId) { + this._InitializeViewport(canvasId); +}; + +Stone.prototype.LoadOrthanc = function(instance, frame) { + this._LoadOrthanc(instance, frame); +}; + +Stone.prototype.LoadDicomWeb = function(server, studyInstanceUid, seriesInstanceUid, sopInstanceUid, frame) { + this._LoadDicomWeb(server, studyInstanceUid, seriesInstanceUid, sopInstanceUid, frame); +}; + +var stone = new Stone(); +