# HG changeset patch # User Sebastien Jodogne # Date 1559130007 -7200 # Node ID 270c31978df1983fa2b88dc2e8e2f58cd6e603d8 # Parent a68cd7ae88388d6abef7c998cb6e59572c72e684 BasicMPR sample diff -r a68cd7ae8838 -r 270c31978df1 Samples/Sdl/Loader.cpp --- a/Samples/Sdl/Loader.cpp Wed May 29 13:39:31 2019 +0200 +++ b/Samples/Sdl/Loader.cpp Wed May 29 13:40:07 2019 +0200 @@ -166,9 +166,9 @@ { printf("Geometry ready\n"); - //plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry(); + plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry(); //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry(); - plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry(); + //plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry(); plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f)); Refresh(); diff -r a68cd7ae8838 -r 270c31978df1 Samples/WebAssembly/BasicMPR.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/WebAssembly/BasicMPR.cpp Wed May 29 13:40:07 2019 +0200 @@ -0,0 +1,880 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 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 "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h" +#include "../../Framework/OpenGL/WebAssemblyOpenGLContext.h" +#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Scene2D/PanSceneTracker.h" +#include "../../Framework/Scene2D/RotateSceneTracker.h" +#include "../../Framework/Scene2D/ZoomSceneTracker.h" +#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h" +#include "../../Framework/Scene2DViewport/ViewportController.h" +#include "../../Framework/StoneInitialization.h" +#include "../../Framework/Volumes/VolumeSceneLayerSource.h" + +#include +#include + + +static const unsigned int FONT_SIZE = 32; + + +namespace OrthancStone +{ + class WebAssemblyViewport : public boost::noncopyable + { + private: + // the construction order is important because compositor_ + // will hold a reference to the scene that belong to the + // controller_ object + OpenGL::WebAssemblyOpenGLContext context_; + boost::shared_ptr controller_; + OpenGLCompositor compositor_; + + void SetupEvents(const std::string& canvas); + + public: + WebAssemblyViewport(MessageBroker& broker, + const std::string& canvas) : + context_(canvas), + controller_(new ViewportController(broker)), + compositor_(context_, *controller_->GetScene()) + { + compositor_.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE, Orthanc::Encoding_Latin1); + SetupEvents(canvas); + } + + Scene2D& GetScene() + { + return *controller_->GetScene(); + } + + const boost::shared_ptr& GetController() + { + return controller_; + } + + void UpdateSize() + { + context_.UpdateSize(); + compositor_.UpdateSize(); + Refresh(); + } + + void Refresh() + { + compositor_.Refresh(); + } + + const std::string& GetCanvasIdentifier() const + { + return context_.GetCanvasIdentifier(); + } + + ScenePoint2D GetPixelCenterCoordinates(int x, int y) const + { + return compositor_.GetPixelCenterCoordinates(x, y); + } + + unsigned int GetCanvasWidth() const + { + return context_.GetCanvasWidth(); + } + + unsigned int GetCanvasHeight() const + { + return context_.GetCanvasHeight(); + } + }; + + class ActiveTracker : public boost::noncopyable + { + private: + boost::shared_ptr tracker_; + std::string canvasIdentifier_; + bool insideCanvas_; + + public: + ActiveTracker(const boost::shared_ptr& tracker, + const WebAssemblyViewport& viewport) : + tracker_(tracker), + canvasIdentifier_(viewport.GetCanvasIdentifier()), + insideCanvas_(true) + { + if (tracker_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + bool IsAlive() const + { + return tracker_->IsAlive(); + } + + void PointerMove(const PointerEvent& event) + { + tracker_->PointerMove(event); + } + + void PointerUp(const PointerEvent& event) + { + tracker_->PointerUp(event); + } + }; +} + +static OrthancStone::PointerEvent* ConvertMouseEvent( + const EmscriptenMouseEvent& source, + OrthancStone::WebAssemblyViewport& viewport) +{ + std::auto_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(); +} + +std::auto_ptr tracker_; + +EM_BOOL OnMouseEvent(int eventType, + const EmscriptenMouseEvent *mouseEvent, + void *userData) +{ + if (mouseEvent != NULL && + userData != NULL) + { + OrthancStone::WebAssemblyViewport& viewport = + *reinterpret_cast(userData); + + switch (eventType) + { + case EMSCRIPTEN_EVENT_CLICK: + { + static unsigned int count = 0; + char buf[64]; + sprintf(buf, "click %d", count++); + + std::auto_ptr layer(new OrthancStone::TextSceneLayer); + layer->SetText(buf); + viewport.GetScene().SetLayer(100, layer.release()); + viewport.Refresh(); + break; + } + + case EMSCRIPTEN_EVENT_MOUSEDOWN: + { + boost::shared_ptr t; + + { + std::auto_ptr event( + ConvertMouseEvent(*mouseEvent, viewport)); + + switch (mouseEvent->button) + { + case 0: // Left button + emscripten_console_log("Creating RotateSceneTracker"); + t.reset(new OrthancStone::RotateSceneTracker( + viewport.GetController(), *event)); + break; + + case 1: // Middle button + emscripten_console_log("Creating PanSceneTracker"); + LOG(INFO) << "Creating PanSceneTracker" ; + t.reset(new OrthancStone::PanSceneTracker( + viewport.GetController(), *event)); + break; + + case 2: // Right button + emscripten_console_log("Creating ZoomSceneTracker"); + t.reset(new OrthancStone::ZoomSceneTracker( + viewport.GetController(), *event, viewport.GetCanvasWidth())); + break; + + default: + break; + } + } + + if (t.get() != NULL) + { + tracker_.reset( + new OrthancStone::ActiveTracker(t, viewport)); + viewport.Refresh(); + } + + break; + } + + case EMSCRIPTEN_EVENT_MOUSEMOVE: + if (tracker_.get() != NULL) + { + std::auto_ptr event( + ConvertMouseEvent(*mouseEvent, viewport)); + tracker_->PointerMove(*event); + viewport.Refresh(); + } + break; + + case EMSCRIPTEN_EVENT_MOUSEUP: + if (tracker_.get() != NULL) + { + std::auto_ptr event( + ConvertMouseEvent(*mouseEvent, viewport)); + tracker_->PointerUp(*event); + viewport.Refresh(); + if (!tracker_->IsAlive()) + tracker_.reset(); + } + break; + + default: + break; + } + } + + return true; +} + + +void OrthancStone::WebAssemblyViewport::SetupEvents(const std::string& canvas) +{ + emscripten_set_mousedown_callback(canvas.c_str(), this, false, OnMouseEvent); + emscripten_set_mousemove_callback(canvas.c_str(), this, false, OnMouseEvent); + emscripten_set_mouseup_callback(canvas.c_str(), this, false, OnMouseEvent); +} + + + + +namespace OrthancStone +{ + class VolumeSlicerViewport : public IObserver + { + private: + OrthancStone::WebAssemblyViewport viewport_; + std::auto_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_); + currentPlane_ = depth / 2; + + planes_.resize(depth); + + for (unsigned int z = 0; z < depth; z++) + { + planes_[z] = geometry.GetProjectionSlice(projection_, z); + } + } + + public: + VolumeSlicerViewport(MessageBroker& broker, + const std::string& canvas, + VolumeProjection projection) : + IObserver(broker), + viewport_(broker, canvas), + projection_(projection), + currentPlane_(0) + { + } + + 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, &VolumeSlicerViewport::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_]); + } + } + }; + + + + + class WebAssemblyOracle : + public IOracle, + public IObservable + { + private: + typedef std::map HttpHeaders; + + class FetchContext : public boost::noncopyable + { + private: + class Emitter : public IMessageEmitter + { + private: + WebAssemblyOracle& oracle_; + + public: + Emitter(WebAssemblyOracle& oracle) : + oracle_(oracle) + { + } + + virtual void EmitMessage(const IObserver& receiver, + const IMessage& message) + { + oracle_.EmitMessage(receiver, message); + } + }; + + Emitter emitter_; + const IObserver& receiver_; + std::auto_ptr command_; + std::string expectedContentType_; + + public: + FetchContext(WebAssemblyOracle& oracle, + const IObserver& receiver, + IOracleCommand* command, + const std::string& expectedContentType) : + emitter_(oracle), + receiver_(receiver), + command_(command), + expectedContentType_(expectedContentType) + { + if (command == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + const std::string& GetExpectedContentType() const + { + return expectedContentType_; + } + + void EmitMessage(const IMessage& message) + { + emitter_.EmitMessage(receiver_, message); + } + + IMessageEmitter& GetEmitter() + { + return emitter_; + } + + const IObserver& GetReceiver() const + { + return receiver_; + } + + IOracleCommand& GetCommand() const + { + return *command_; + } + + template + const T& GetTypedCommand() const + { + return dynamic_cast(*command_); + } + }; + + static void FetchSucceeded(emscripten_fetch_t *fetch) + { + /** + * Firstly, make a local copy of the fetched information, and + * free data associated with the fetch. + **/ + + std::auto_ptr context(reinterpret_cast(fetch->userData)); + + std::string answer; + if (fetch->numBytes > 0) + { + answer.assign(fetch->data, fetch->numBytes); + } + + /** + * TODO - HACK - As of emscripten-1.38.31, the fetch API does + * not contain a way to retrieve the HTTP headers of the + * answer. We make the assumption that the "Content-Type" header + * of the response is the same as the "Accept" header of the + * query. This should be fixed in future versions of emscripten. + * https://github.com/emscripten-core/emscripten/pull/8486 + **/ + + HttpHeaders headers; + if (!context->GetExpectedContentType().empty()) + { + headers["Content-Type"] = context->GetExpectedContentType(); + } + + + emscripten_fetch_close(fetch); + + + /** + * Secondly, use the retrieved data. + **/ + + try + { + if (context.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + else + { + switch (context->GetCommand().GetType()) + { + case IOracleCommand::Type_OrthancRestApi: + { + OrthancRestApiCommand::SuccessMessage message + (context->GetTypedCommand(), headers, answer); + context->EmitMessage(message); + break; + } + + case IOracleCommand::Type_GetOrthancImage: + { + context->GetTypedCommand().ProcessHttpAnswer + (context->GetEmitter(), context->GetReceiver(), answer, headers); + break; + } + + case IOracleCommand::Type_GetOrthancWebViewerJpeg: + { + context->GetTypedCommand().ProcessHttpAnswer + (context->GetEmitter(), context->GetReceiver(), answer); + break; + } + + default: + LOG(ERROR) << "Command type not implemented by the WebAssembly Oracle: " + << context->GetCommand().GetType(); + } + } + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Error while processing a fetch answer in the oracle: " << e.What(); + } + } + + static void FetchFailed(emscripten_fetch_t *fetch) + { + std::auto_ptr context(reinterpret_cast(fetch->userData)); + + LOG(ERROR) << "Fetching " << fetch->url << " failed, HTTP failure status code: " << fetch->status; + + /** + * TODO - The following code leads to an infinite recursion, at + * least with Firefox running on incognito mode => WHY? + **/ + //emscripten_fetch_close(fetch); // Also free data on failure. + } + + + class FetchCommand : public boost::noncopyable + { + private: + WebAssemblyOracle& oracle_; + const IObserver& receiver_; + std::auto_ptr command_; + Orthanc::HttpMethod method_; + std::string uri_; + std::string body_; + HttpHeaders headers_; + unsigned int timeout_; + std::string expectedContentType_; + + public: + FetchCommand(WebAssemblyOracle& oracle, + const IObserver& receiver, + IOracleCommand* command) : + oracle_(oracle), + receiver_(receiver), + command_(command), + method_(Orthanc::HttpMethod_Get), + timeout_(0) + { + if (command == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + void SetMethod(Orthanc::HttpMethod method) + { + method_ = method; + } + + void SetUri(const std::string& uri) + { + uri_ = uri; + } + + void SetBody(std::string& body /* will be swapped */) + { + body_.swap(body); + } + + void SetHttpHeaders(const HttpHeaders& headers) + { + headers_ = headers; + } + + void SetTimeout(unsigned int timeout) + { + timeout_ = timeout; + } + + void Execute() + { + if (command_.get() == NULL) + { + // Cannot call Execute() twice + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + emscripten_fetch_attr_t attr; + emscripten_fetch_attr_init(&attr); + + const char* method; + + switch (method_) + { + case Orthanc::HttpMethod_Get: + method = "GET"; + break; + + case Orthanc::HttpMethod_Post: + method = "POST"; + break; + + case Orthanc::HttpMethod_Delete: + method = "DELETE"; + break; + + case Orthanc::HttpMethod_Put: + method = "PUT"; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + strcpy(attr.requestMethod, method); + + attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY; + attr.onsuccess = FetchSucceeded; + attr.onerror = FetchFailed; + attr.timeoutMSecs = timeout_ * 1000; + + std::vector headers; + headers.reserve(2 * headers_.size() + 1); + + std::string expectedContentType; + + for (HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); ++it) + { + std::string key; + Orthanc::Toolbox::ToLowerCase(key, it->first); + + if (key == "accept") + { + expectedContentType = it->second; + } + + if (key != "accept-encoding") // Web browsers forbid the modification of this HTTP header + { + headers.push_back(it->first.c_str()); + headers.push_back(it->second.c_str()); + } + } + + headers.push_back(NULL); // Termination of the array of HTTP headers + + attr.requestHeaders = &headers[0]; + + if (!body_.empty()) + { + attr.requestDataSize = body_.size(); + attr.requestData = body_.c_str(); + } + + // Must be the last call to prevent memory leak on error + attr.userData = new FetchContext(oracle_, receiver_, command_.release(), expectedContentType); + emscripten_fetch(&attr, uri_.c_str()); + } + }; + + + void Execute(const IObserver& receiver, + OrthancRestApiCommand* command) + { + FetchCommand fetch(*this, receiver, command); + + fetch.SetMethod(command->GetMethod()); + fetch.SetUri(command->GetUri()); + fetch.SetHttpHeaders(command->GetHttpHeaders()); + fetch.SetTimeout(command->GetTimeout()); + + if (command->GetMethod() == Orthanc::HttpMethod_Put || + command->GetMethod() == Orthanc::HttpMethod_Put) + { + std::string body; + command->SwapBody(body); + fetch.SetBody(body); + } + + fetch.Execute(); + } + + + void Execute(const IObserver& receiver, + GetOrthancImageCommand* command) + { + FetchCommand fetch(*this, receiver, command); + + fetch.SetUri(command->GetUri()); + fetch.SetHttpHeaders(command->GetHttpHeaders()); + fetch.SetTimeout(command->GetTimeout()); + + fetch.Execute(); + } + + + void Execute(const IObserver& receiver, + GetOrthancWebViewerJpegCommand* command) + { + FetchCommand fetch(*this, receiver, command); + + fetch.SetUri(command->GetUri()); + fetch.SetHttpHeaders(command->GetHttpHeaders()); + fetch.SetTimeout(command->GetTimeout()); + + fetch.Execute(); + } + + + public: + WebAssemblyOracle(MessageBroker& broker) : + IObservable(broker) + { + } + + virtual void Schedule(const IObserver& receiver, + IOracleCommand* command) + { + std::auto_ptr protection(command); + + if (command == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + switch (command->GetType()) + { + case IOracleCommand::Type_OrthancRestApi: + Execute(receiver, dynamic_cast(protection.release())); + break; + + case IOracleCommand::Type_GetOrthancImage: + Execute(receiver, dynamic_cast(protection.release())); + break; + + case IOracleCommand::Type_GetOrthancWebViewerJpeg: + Execute(receiver, dynamic_cast(protection.release())); + break; + + default: + LOG(ERROR) << "Command type not implemented by the WebAssembly Oracle: " << command->GetType(); + } + } + + virtual void Start() + { + } + + virtual void Stop() + { + } + }; +} + + + + +boost::shared_ptr ct_(new OrthancStone::DicomVolumeImage); + +boost::shared_ptr loader_; + +std::auto_ptr viewport1_; +std::auto_ptr viewport2_; +std::auto_ptr viewport3_; + +OrthancStone::MessageBroker broker_; +OrthancStone::WebAssemblyOracle oracle_(broker_); + + +EM_BOOL OnWindowResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData) +{ + try + { + if (viewport1_.get() != NULL) + { + viewport1_->UpdateSize(); + } + + if (viewport2_.get() != NULL) + { + viewport2_->UpdateSize(); + } + + if (viewport3_.get() != NULL) + { + viewport3_->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 (viewport1_.get() != NULL) + { + viewport1_->Refresh(); + } + + if (viewport2_.get() != NULL) + { + viewport2_->Refresh(); + } + + if (viewport3_.get() != NULL) + { + viewport3_->Refresh(); + } + + return true; + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception in the animation loop, stopping now: " << e.What(); + return false; + } +} + + +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 Initialize() + { + try + { + loader_.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct_, oracle_, oracle_)); + + viewport1_.reset(new OrthancStone::VolumeSlicerViewport(broker_, "mycanvas1", OrthancStone::VolumeProjection_Axial)); + viewport1_->SetSlicer(0, loader_, *loader_, new OrthancStone::GrayscaleStyleConfigurator); + viewport1_->UpdateSize(); + + viewport2_.reset(new OrthancStone::VolumeSlicerViewport(broker_, "mycanvas2", OrthancStone::VolumeProjection_Coronal)); + viewport2_->SetSlicer(0, loader_, *loader_, new OrthancStone::GrayscaleStyleConfigurator); + viewport2_->UpdateSize(); + + viewport3_.reset(new OrthancStone::VolumeSlicerViewport(broker_, "mycanvas3", OrthancStone::VolumeProjection_Sagittal)); + viewport3_->SetSlicer(0, loader_, *loader_, new OrthancStone::GrayscaleStyleConfigurator); + viewport3_->UpdateSize(); + + emscripten_set_resize_callback("#window", NULL, false, OnWindowResize); + + emscripten_request_animation_frame_loop(OnAnimationFrame, NULL); + + oracle_.Start(); + loader_->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception during Initialize(): " << e.What(); + } + } +} diff -r a68cd7ae8838 -r 270c31978df1 Samples/WebAssembly/BasicMPR.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/WebAssembly/BasicMPR.html Wed May 29 13:40:07 2019 +0200 @@ -0,0 +1,69 @@ + + + + + + + + + + + + Stone of Orthanc + + + + + + + + + + + + + diff -r a68cd7ae8838 -r 270c31978df1 Samples/WebAssembly/BasicScene.cpp --- a/Samples/WebAssembly/BasicScene.cpp Wed May 29 13:39:31 2019 +0200 +++ b/Samples/WebAssembly/BasicScene.cpp Wed May 29 13:40:07 2019 +0200 @@ -113,14 +113,14 @@ chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5)); chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5)); chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5)); - layer->AddChain(chain, true); + layer->AddChain(chain, true, 255, 0, 0); chain.clear(); chain.push_back(ScenePoint2D(-5, -5)); chain.push_back(ScenePoint2D(5, -5)); chain.push_back(ScenePoint2D(5, 5)); chain.push_back(ScenePoint2D(-5, 5)); - layer->AddChain(chain, true); + layer->AddChain(chain, true, 0, 255, 0); double dy = 1.01; chain.clear(); @@ -128,9 +128,8 @@ chain.push_back(ScenePoint2D(4, -4 + dy)); chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy)); chain.push_back(ScenePoint2D(4, 2)); - layer->AddChain(chain, false); + layer->AddChain(chain, false, 0, 0, 255); - layer->SetColor(0,255, 255); scene->SetLayer(50, layer.release()); } diff -r a68cd7ae8838 -r 270c31978df1 Samples/WebAssembly/CMakeLists.txt --- a/Samples/WebAssembly/CMakeLists.txt Wed May 29 13:39:31 2019 +0200 +++ b/Samples/WebAssembly/CMakeLists.txt Wed May 29 13:40:07 2019 +0200 @@ -13,6 +13,7 @@ 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 ALLOW_MEMORY_GROWTH=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 EXIT_RUNTIME=1") ##################################################################### @@ -68,20 +69,40 @@ ${ORTHANC_STONE_SOURCES} ) -add_executable(BasicScene - BasicScene.cpp - ) + +if (OFF) + add_executable(BasicScene + BasicScene.cpp + ) + + target_link_libraries(BasicScene OrthancStone) + + install( + TARGETS BasicScene + RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} + ) +endif() -target_link_libraries(BasicScene OrthancStone) + +if (ON) + add_executable(BasicMPR + BasicMPR.cpp + ) -install( - TARGETS BasicScene - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} - ) + target_link_libraries(BasicMPR OrthancStone) + + install( + TARGETS BasicMPR + RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} + ) +endif() + install( FILES + ${CMAKE_CURRENT_BINARY_DIR}/BasicMPR.wasm ${CMAKE_CURRENT_BINARY_DIR}/BasicScene.wasm + ${CMAKE_SOURCE_DIR}/BasicMPR.html ${CMAKE_SOURCE_DIR}/BasicScene.html ${CMAKE_SOURCE_DIR}/Configuration.json ${CMAKE_SOURCE_DIR}/index.html diff -r a68cd7ae8838 -r 270c31978df1 Samples/WebAssembly/NOTES.txt --- a/Samples/WebAssembly/NOTES.txt Wed May 29 13:39:31 2019 +0200 +++ b/Samples/WebAssembly/NOTES.txt Wed May 29 13:40:07 2019 +0200 @@ -1,7 +1,7 @@ $ source ~/Downloads/emsdk/emsdk_env.sh $ cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON .. -DCMAKE_INSTALL_PREFIX=/tmp/stone $ ninja install -$ sudo docker run -p 4242:4242 -p 8042:8042 --rm -v /tmp/stone:/root/stone:ro jodogne/orthanc-plugins:1.5.6 /root/stone/Configuration.json --verbose +$ docker run -p 4242:4242 -p 8042:8042 --rm -v /tmp/stone:/root/stone:ro -v /tmp/stone-db/:/var/lib/orthanc/db/ jodogne/orthanc-plugins:1.5.6 /root/stone/Configuration.json --verbose notes BGO : source ~/apps/emsdk/emsdk_env.sh @@ -11,4 +11,4 @@ cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON .. -DCMAKE_INSTALL_PREFIX=/mnt/c/osi/dev/orthanc-stone/Samples/WebAssembly/installDir ninja install -docker run -p 4242:4242 -p 8042:8042 --rm -v "C:/osi/dev/orthanc-stone/Samples/WebAssembly/installDir:/root/stone:ro" jodogne/orthanc-plugins:1.5.6 /root/stone/Configuration.json --verbose \ No newline at end of file +docker run -p 4242:4242 -p 8042:8042 --rm -v "C:/osi/dev/orthanc-stone/Samples/WebAssembly/installDir:/root/stone:ro" jodogne/orthanc-plugins:1.5.6 /root/stone/Configuration.json --verbose diff -r a68cd7ae8838 -r 270c31978df1 Samples/WebAssembly/index.html --- a/Samples/WebAssembly/index.html Wed May 29 13:39:31 2019 +0200 +++ b/Samples/WebAssembly/index.html Wed May 29 13:40:07 2019 +0200 @@ -10,6 +10,7 @@

Available samples