Mercurial > hg > orthanc-stone
comparison Samples/WebAssembly/RtViewer/OSBOLETE.cpp @ 1384:24bcff8ea58f
RtViewer : SDL ok. Preparation for WASM builds ongoing
author | Benjamin Golinvaux <bgo@osimis.io> |
---|---|
date | Mon, 27 Apr 2020 10:01:48 +0200 |
parents | |
children | 15173a383a00 |
comparison
equal
deleted
inserted
replaced
1383:ab871499ed30 | 1384:24bcff8ea58f |
---|---|
1 /** | |
2 * Stone of Orthanc | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium | |
6 * | |
7 * This program is free software: you can redistribute it and/or | |
8 * modify it under the terms of the GNU Affero General Public License | |
9 * as published by the Free Software Foundation, either version 3 of | |
10 * the License, or (at your option) any later version. | |
11 * | |
12 * This program is distributed in the hope that it will be useful, but | |
13 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 * Affero General Public License for more details. | |
16 * | |
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/>. | |
19 **/ | |
20 | |
21 #include <Framework/Viewport/WebAssemblyViewport.h> | |
22 #include <Framework/Scene2D/OpenGLCompositor.h> | |
23 #include <Framework/Scene2D/PanSceneTracker.h> | |
24 #include <Framework/Scene2D/RotateSceneTracker.h> | |
25 #include <Framework/Scene2D/ZoomSceneTracker.h> | |
26 #include <Framework/Scene2DViewport/UndoStack.h> | |
27 #include <Framework/Scene2DViewport/ViewportController.h> | |
28 #include <Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h> | |
29 #include <Framework/Oracle/SleepOracleCommand.h> | |
30 #include <Framework/Oracle/WebAssemblyOracle.h> | |
31 #include <Framework/Scene2D/GrayscaleStyleConfigurator.h> | |
32 #include <Framework/StoneInitialization.h> | |
33 #include <Framework/Volumes/VolumeSceneLayerSource.h> | |
34 | |
35 #include <Core/OrthancException.h> | |
36 | |
37 #include <emscripten/html5.h> | |
38 #include <emscripten.h> | |
39 | |
40 #include <boost/make_shared.hpp> | |
41 | |
42 | |
43 class ViewportManager; | |
44 | |
45 static const unsigned int FONT_SIZE = 32; | |
46 | |
47 boost::shared_ptr<OrthancStone::DicomVolumeImage> ct_(new OrthancStone::DicomVolumeImage); | |
48 boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> loader_; | |
49 std::unique_ptr<ViewportManager> widget1_; | |
50 std::unique_ptr<ViewportManager> widget2_; | |
51 std::unique_ptr<ViewportManager> widget3_; | |
52 //OrthancStone::MessageBroker broker_; | |
53 //OrthancStone::WebAssemblyOracle oracle_(broker_); | |
54 std::unique_ptr<OrthancStone::IFlexiblePointerTracker> tracker_; | |
55 static std::map<std::string, std::string> arguments_; | |
56 static bool ctrlDown_ = false; | |
57 | |
58 | |
59 #if 0 | |
60 | |
61 // use the one from WebAssemblyViewport | |
62 static OrthancStone::PointerEvent* ConvertMouseEvent( | |
63 const EmscriptenMouseEvent& source, | |
64 OrthancStone::IViewport& viewport) | |
65 { | |
66 | |
67 std::unique_ptr<OrthancStone::PointerEvent> target( | |
68 new OrthancStone::PointerEvent); | |
69 | |
70 target->AddPosition(viewport.GetPixelCenterCoordinates( | |
71 source.targetX, source.targetY)); | |
72 target->SetAltModifier(source.altKey); | |
73 target->SetControlModifier(source.ctrlKey); | |
74 target->SetShiftModifier(source.shiftKey); | |
75 | |
76 return target.release(); | |
77 } | |
78 #endif | |
79 | |
80 | |
81 EM_BOOL OnMouseEvent(int eventType, | |
82 const EmscriptenMouseEvent *mouseEvent, | |
83 void *userData) | |
84 { | |
85 if (mouseEvent != NULL && | |
86 userData != NULL) | |
87 { | |
88 boost::shared_ptr<OrthancStone::WebGLViewport>& viewport = | |
89 *reinterpret_cast<boost::shared_ptr<OrthancStone::WebGLViewport>*>(userData); | |
90 | |
91 std::unique_ptr<OrthancStone::IViewport::ILock> lock = (*viewport)->Lock(); | |
92 ViewportController& controller = lock->GetController(); | |
93 Scene2D& scene = controller.GetScene(); | |
94 | |
95 switch (eventType) | |
96 { | |
97 case EMSCRIPTEN_EVENT_CLICK: | |
98 { | |
99 static unsigned int count = 0; | |
100 char buf[64]; | |
101 sprintf(buf, "click %d", count++); | |
102 | |
103 std::unique_ptr<OrthancStone::TextSceneLayer> layer(new OrthancStone::TextSceneLayer); | |
104 layer->SetText(buf); | |
105 scene.SetLayer(100, layer.release()); | |
106 lock->Invalidate(); | |
107 break; | |
108 } | |
109 | |
110 case EMSCRIPTEN_EVENT_MOUSEDOWN: | |
111 { | |
112 boost::shared_ptr<OrthancStone::IFlexiblePointerTracker> t; | |
113 | |
114 { | |
115 std::unique_ptr<OrthancStone::PointerEvent> event( | |
116 ConvertMouseEvent(*mouseEvent, controller->GetViewport())); | |
117 | |
118 switch (mouseEvent->button) | |
119 { | |
120 case 0: // Left button | |
121 emscripten_console_log("Creating RotateSceneTracker"); | |
122 t.reset(new OrthancStone::RotateSceneTracker( | |
123 viewport, *event)); | |
124 break; | |
125 | |
126 case 1: // Middle button | |
127 emscripten_console_log("Creating PanSceneTracker"); | |
128 LOG(INFO) << "Creating PanSceneTracker" ; | |
129 t.reset(new OrthancStone::PanSceneTracker( | |
130 viewport, *event)); | |
131 break; | |
132 | |
133 case 2: // Right button | |
134 emscripten_console_log("Creating ZoomSceneTracker"); | |
135 t.reset(new OrthancStone::ZoomSceneTracker( | |
136 viewport, *event, controller->GetViewport().GetCanvasWidth())); | |
137 break; | |
138 | |
139 default: | |
140 break; | |
141 } | |
142 } | |
143 | |
144 if (t.get() != NULL) | |
145 { | |
146 tracker_.reset( | |
147 new OrthancStone::ActiveTracker(t, controller->GetViewport().GetCanvasIdentifier())); | |
148 controller->GetViewport().Refresh(); | |
149 } | |
150 | |
151 break; | |
152 } | |
153 | |
154 case EMSCRIPTEN_EVENT_MOUSEMOVE: | |
155 if (tracker_.get() != NULL) | |
156 { | |
157 std::unique_ptr<OrthancStone::PointerEvent> event( | |
158 ConvertMouseEvent(*mouseEvent, controller->GetViewport())); | |
159 tracker_->PointerMove(*event); | |
160 controller->GetViewport().Refresh(); | |
161 } | |
162 break; | |
163 | |
164 case EMSCRIPTEN_EVENT_MOUSEUP: | |
165 if (tracker_.get() != NULL) | |
166 { | |
167 std::unique_ptr<OrthancStone::PointerEvent> event( | |
168 ConvertMouseEvent(*mouseEvent, controller->GetViewport())); | |
169 tracker_->PointerUp(*event); | |
170 controller->GetViewport().Refresh(); | |
171 if (!tracker_->IsAlive()) | |
172 tracker_.reset(); | |
173 } | |
174 break; | |
175 | |
176 default: | |
177 break; | |
178 } | |
179 } | |
180 | |
181 return true; | |
182 } | |
183 | |
184 | |
185 void SetupEvents(const std::string& canvas, | |
186 boost::shared_ptr<OrthancStone::WebGLViewport>& viewport) | |
187 { | |
188 emscripten_set_mousedown_callback(canvas.c_str(), &viewport, false, OnMouseEvent); | |
189 emscripten_set_mousemove_callback(canvas.c_str(), &viewport, false, OnMouseEvent); | |
190 emscripten_set_mouseup_callback(canvas.c_str(), &viewport, false, OnMouseEvent); | |
191 } | |
192 | |
193 class ViewportManager : public OrthanStone::ObserverBase<ViewportManager> | |
194 { | |
195 private: | |
196 OrthancStone::WebAssemblyViewport viewport_; | |
197 std::unique_ptr<VolumeSceneLayerSource> source_; | |
198 VolumeProjection projection_; | |
199 std::vector<CoordinateSystem3D> planes_; | |
200 size_t currentPlane_; | |
201 | |
202 void Handle(const DicomVolumeImage::GeometryReadyMessage& message) | |
203 { | |
204 LOG(INFO) << "Geometry is available"; | |
205 | |
206 const VolumeImageGeometry& geometry = message.GetOrigin().GetGeometry(); | |
207 | |
208 const unsigned int depth = geometry.GetProjectionDepth(projection_); | |
209 | |
210 // select an initial cutting plane halfway through the volume | |
211 currentPlane_ = depth / 2; | |
212 | |
213 planes_.resize(depth); | |
214 | |
215 for (unsigned int z = 0; z < depth; z++) | |
216 { | |
217 planes_[z] = geometry.GetProjectionSlice(projection_, z); | |
218 } | |
219 | |
220 Refresh(); | |
221 | |
222 viewport_.FitContent(); | |
223 } | |
224 | |
225 public: | |
226 ViewportManager(const std::string& canvas, | |
227 VolumeProjection projection) : | |
228 projection_(projection), | |
229 currentPlane_(0) | |
230 { | |
231 viewport_ = OrthancStone::WebGLViewport::Create(canvas); | |
232 } | |
233 | |
234 void UpdateSize() | |
235 { | |
236 viewport_.UpdateSize(); | |
237 } | |
238 | |
239 void SetSlicer(int layerDepth, | |
240 const boost::shared_ptr<IVolumeSlicer>& slicer, | |
241 IObservable& loader, | |
242 ILayerStyleConfigurator* configurator) | |
243 { | |
244 if (source_.get() != NULL) | |
245 { | |
246 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, | |
247 "Only one slicer can be registered"); | |
248 } | |
249 | |
250 loader.RegisterObserverCallback( | |
251 new Callable<ViewportManager, DicomVolumeImage::GeometryReadyMessage> | |
252 (*this, &ViewportManager::Handle)); | |
253 | |
254 source_.reset(new VolumeSceneLayerSource(viewport_.GetScene(), layerDepth, slicer)); | |
255 | |
256 if (configurator != NULL) | |
257 { | |
258 source_->SetConfigurator(configurator); | |
259 } | |
260 } | |
261 | |
262 void Refresh() | |
263 { | |
264 if (source_.get() != NULL && | |
265 currentPlane_ < planes_.size()) | |
266 { | |
267 source_->Update(planes_[currentPlane_]); | |
268 viewport_.Refresh(); | |
269 } | |
270 } | |
271 | |
272 size_t GetSlicesCount() const | |
273 { | |
274 return planes_.size(); | |
275 } | |
276 | |
277 void Scroll(int delta) | |
278 { | |
279 if (!planes_.empty()) | |
280 { | |
281 int tmp = static_cast<int>(currentPlane_) + delta; | |
282 unsigned int next; | |
283 | |
284 if (tmp < 0) | |
285 { | |
286 next = 0; | |
287 } | |
288 else if (tmp >= static_cast<int>(planes_.size())) | |
289 { | |
290 next = planes_.size() - 1; | |
291 } | |
292 else | |
293 { | |
294 next = static_cast<size_t>(tmp); | |
295 } | |
296 | |
297 if (next != currentPlane_) | |
298 { | |
299 currentPlane_ = next; | |
300 Refresh(); | |
301 } | |
302 } | |
303 } | |
304 }; | |
305 } | |
306 | |
307 | |
308 EM_BOOL OnWindowResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData) | |
309 { | |
310 try | |
311 { | |
312 if (widget1_.get() != NULL) | |
313 { | |
314 widget1_->UpdateSize(); | |
315 } | |
316 | |
317 if (widget2_.get() != NULL) | |
318 { | |
319 widget2_->UpdateSize(); | |
320 } | |
321 | |
322 if (widget3_.get() != NULL) | |
323 { | |
324 widget3_->UpdateSize(); | |
325 } | |
326 } | |
327 catch (Orthanc::OrthancException& e) | |
328 { | |
329 LOG(ERROR) << "Exception while updating canvas size: " << e.What(); | |
330 } | |
331 | |
332 return true; | |
333 } | |
334 | |
335 EM_BOOL OnAnimationFrame(double time, void *userData) | |
336 { | |
337 try | |
338 { | |
339 if (widget1_.get() != NULL) | |
340 { | |
341 widget1_->Refresh(); | |
342 } | |
343 | |
344 if (widget2_.get() != NULL) | |
345 { | |
346 widget2_->Refresh(); | |
347 } | |
348 | |
349 if (widget3_.get() != NULL) | |
350 { | |
351 widget3_->Refresh(); | |
352 } | |
353 | |
354 return true; | |
355 } | |
356 catch (Orthanc::OrthancException& e) | |
357 { | |
358 LOG(ERROR) << "Exception in the animation loop, stopping now: " << e.What(); | |
359 return false; | |
360 } | |
361 } | |
362 | |
363 EM_BOOL OnMouseWheel(int eventType, | |
364 const EmscriptenWheelEvent *wheelEvent, | |
365 void *userData) | |
366 { | |
367 try | |
368 { | |
369 if (userData != NULL) | |
370 { | |
371 int delta = 0; | |
372 | |
373 if (wheelEvent->deltaY < 0) | |
374 { | |
375 delta = -1; | |
376 } | |
377 | |
378 if (wheelEvent->deltaY > 0) | |
379 { | |
380 delta = 1; | |
381 } | |
382 | |
383 OrthancStone::ViewportManager& widget = | |
384 *reinterpret_cast<OrthancStone::ViewportManager*>(userData); | |
385 | |
386 if (ctrlDown_) | |
387 { | |
388 delta *= static_cast<int>(widget.GetSlicesCount() / 10); | |
389 } | |
390 | |
391 widget.Scroll(delta); | |
392 } | |
393 } | |
394 catch (Orthanc::OrthancException& e) | |
395 { | |
396 LOG(ERROR) << "Exception in the wheel event: " << e.What(); | |
397 } | |
398 | |
399 return true; | |
400 } | |
401 | |
402 | |
403 EM_BOOL OnKeyDown(int eventType, | |
404 const EmscriptenKeyboardEvent *keyEvent, | |
405 void *userData) | |
406 { | |
407 ctrlDown_ = keyEvent->ctrlKey; | |
408 return false; | |
409 } | |
410 | |
411 | |
412 EM_BOOL OnKeyUp(int eventType, | |
413 const EmscriptenKeyboardEvent *keyEvent, | |
414 void *userData) | |
415 { | |
416 ctrlDown_ = false; | |
417 return false; | |
418 } | |
419 | |
420 | |
421 | |
422 #if 0 | |
423 namespace OrthancStone | |
424 { | |
425 class TestSleep : public IObserver | |
426 { | |
427 private: | |
428 WebAssemblyOracle& oracle_; | |
429 | |
430 void Schedule() | |
431 { | |
432 oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(2000)); | |
433 } | |
434 | |
435 void Handle(const SleepOracleCommand::TimeoutMessage& message) | |
436 { | |
437 LOG(INFO) << "TIMEOUT"; | |
438 Schedule(); | |
439 } | |
440 | |
441 public: | |
442 TestSleep(MessageBroker& broker, | |
443 WebAssemblyOracle& oracle) : | |
444 IObserver(broker), | |
445 oracle_(oracle) | |
446 { | |
447 oracle.RegisterObserverCallback( | |
448 new Callable<TestSleep, SleepOracleCommand::TimeoutMessage> | |
449 (*this, &TestSleep::Handle)); | |
450 | |
451 LOG(INFO) << "STARTING"; | |
452 Schedule(); | |
453 } | |
454 }; | |
455 | |
456 //static TestSleep testSleep(broker_, oracle_); | |
457 } | |
458 #endif | |
459 | |
460 static bool GetArgument(std::string& value, | |
461 const std::string& key) | |
462 { | |
463 std::map<std::string, std::string>::const_iterator found = arguments_.find(key); | |
464 | |
465 if (found == arguments_.end()) | |
466 { | |
467 return false; | |
468 } | |
469 else | |
470 { | |
471 value = found->second; | |
472 return true; | |
473 } | |
474 } | |
475 | |
476 | |
477 extern "C" | |
478 { | |
479 int main(int argc, char const *argv[]) | |
480 { | |
481 OrthancStone::StoneInitialize(); | |
482 Orthanc::Logging::EnableInfoLevel(true); | |
483 // Orthanc::Logging::EnableTraceLevel(true); | |
484 EM_ASM(window.dispatchEvent(new CustomEvent("WebAssemblyLoaded"));); | |
485 } | |
486 | |
487 EMSCRIPTEN_KEEPALIVE | |
488 void SetArgument(const char* key, const char* value) | |
489 { | |
490 // This is called for each GET argument (cf. "app.js") | |
491 LOG(INFO) << "Received GET argument: [" << key << "] = [" << value << "]"; | |
492 arguments_[key] = value; | |
493 } | |
494 | |
495 EMSCRIPTEN_KEEPALIVE | |
496 void Initialize() | |
497 { | |
498 try | |
499 { | |
500 oracle_.SetOrthancRoot(".."); | |
501 | |
502 loader_.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct_, oracle_, oracle_)); | |
503 | |
504 widget1_.reset(new OrthancStone::ViewportManager("mycanvas1", OrthancStone::VolumeProjection_Axial)); | |
505 { | |
506 std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator); | |
507 style->SetLinearInterpolation(true); | |
508 style->SetWindowing(OrthancStone::ImageWindowing_Bone); | |
509 widget1_->SetSlicer(0, loader_, *loader_, style.release()); | |
510 } | |
511 widget1_->UpdateSize(); | |
512 | |
513 widget2_.reset(new OrthancStone::ViewportManager("mycanvas2", OrthancStone::VolumeProjection_Coronal)); | |
514 { | |
515 std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator); | |
516 style->SetLinearInterpolation(true); | |
517 style->SetWindowing(OrthancStone::ImageWindowing_Bone); | |
518 widget2_->SetSlicer(0, loader_, *loader_, style.release()); | |
519 } | |
520 widget2_->UpdateSize(); | |
521 | |
522 widget3_.reset(new OrthancStone::ViewportManager("mycanvas3", OrthancStone::VolumeProjection_Sagittal)); | |
523 { | |
524 std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator); | |
525 style->SetLinearInterpolation(true); | |
526 style->SetWindowing(OrthancStone::ImageWindowing_Bone); | |
527 widget3_->SetSlicer(0, loader_, *loader_, style.release()); | |
528 } | |
529 widget3_->UpdateSize(); | |
530 | |
531 emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnWindowResize); // DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1 !! | |
532 | |
533 emscripten_set_wheel_callback("#mycanvas1", widget1_.get(), false, OnMouseWheel); | |
534 emscripten_set_wheel_callback("#mycanvas2", widget2_.get(), false, OnMouseWheel); | |
535 emscripten_set_wheel_callback("#mycanvas3", widget3_.get(), false, OnMouseWheel); | |
536 | |
537 emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnKeyDown); | |
538 emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnKeyUp); | |
539 | |
540 //emscripten_request_animation_frame_loop(OnAnimationFrame, NULL); | |
541 | |
542 std::string ct; | |
543 if (GetArgument(ct, "ct")) | |
544 { | |
545 //loader_->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); | |
546 loader_->LoadSeries(ct); | |
547 } | |
548 else | |
549 { | |
550 LOG(ERROR) << "No Orthanc identifier for the CT series was provided"; | |
551 } | |
552 } | |
553 catch (Orthanc::OrthancException& e) | |
554 { | |
555 LOG(ERROR) << "Exception during Initialize(): " << e.What(); | |
556 } | |
557 } | |
558 } | |
559 |