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