comparison Samples/WebAssembly/BasicMPR.cpp @ 826:2de01660debe

reorganization
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 29 May 2019 16:48:56 +0200
parents 9a6c7a5dcb76
children d71cf8504159
comparison
equal deleted inserted replaced
825:9a6c7a5dcb76 826:2de01660debe
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **/ 19 **/
20 20
21 21
22 22
23 #include "dev.h"
24
23 #include <emscripten.h> 25 #include <emscripten.h>
24 #include <emscripten/fetch.h>
25 #include <emscripten/html5.h>
26 26
27 #include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h" 27 #include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h"
28 #include "../../Framework/OpenGL/WebAssemblyOpenGLContext.h"
29 #include "../../Framework/Oracle/SleepOracleCommand.h" 28 #include "../../Framework/Oracle/SleepOracleCommand.h"
30 #include "../../Framework/Oracle/WebAssemblyOracle.h" 29 #include "../../Framework/Oracle/WebAssemblyOracle.h"
31 #include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h" 30 #include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h"
32 #include "../../Framework/Scene2D/OpenGLCompositor.h"
33 #include "../../Framework/Scene2D/PanSceneTracker.h"
34 #include "../../Framework/Scene2D/RotateSceneTracker.h"
35 #include "../../Framework/Scene2D/ZoomSceneTracker.h"
36 #include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h"
37 #include "../../Framework/Scene2DViewport/ViewportController.h"
38 #include "../../Framework/StoneInitialization.h" 31 #include "../../Framework/StoneInitialization.h"
39 #include "../../Framework/Volumes/VolumeSceneLayerSource.h" 32 #include "../../Framework/Volumes/VolumeSceneLayerSource.h"
40 33
41 #include <Core/OrthancException.h>
42 #include <Core/Toolbox.h>
43
44
45 static const unsigned int FONT_SIZE = 32;
46
47 34
48 namespace OrthancStone 35 namespace OrthancStone
49 { 36 {
50 class WebAssemblyViewport : public boost::noncopyable 37 class VolumeSlicerWidget : public IObserver
51 {
52 private:
53 // the construction order is important because compositor_
54 // will hold a reference to the scene that belong to the
55 // controller_ object
56 OpenGL::WebAssemblyOpenGLContext context_;
57 boost::shared_ptr<ViewportController> controller_;
58 OpenGLCompositor compositor_;
59
60 void SetupEvents(const std::string& canvas);
61
62 public:
63 WebAssemblyViewport(MessageBroker& broker,
64 const std::string& canvas) :
65 context_(canvas),
66 controller_(new ViewportController(broker)),
67 compositor_(context_, *controller_->GetScene())
68 {
69 compositor_.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
70 FONT_SIZE, Orthanc::Encoding_Latin1);
71 SetupEvents(canvas);
72 }
73
74 Scene2D& GetScene()
75 {
76 return *controller_->GetScene();
77 }
78
79 const boost::shared_ptr<ViewportController>& GetController()
80 {
81 return controller_;
82 }
83
84 void UpdateSize()
85 {
86 context_.UpdateSize();
87 compositor_.UpdateSize();
88 Refresh();
89 }
90
91 void Refresh()
92 {
93 compositor_.Refresh();
94 }
95
96 void FitContent()
97 {
98 GetScene().FitContent(context_.GetCanvasWidth(), context_.GetCanvasHeight());
99 }
100
101 const std::string& GetCanvasIdentifier() const
102 {
103 return context_.GetCanvasIdentifier();
104 }
105
106 ScenePoint2D GetPixelCenterCoordinates(int x, int y) const
107 {
108 return compositor_.GetPixelCenterCoordinates(x, y);
109 }
110
111 unsigned int GetCanvasWidth() const
112 {
113 return context_.GetCanvasWidth();
114 }
115
116 unsigned int GetCanvasHeight() const
117 {
118 return context_.GetCanvasHeight();
119 }
120 };
121
122 class ActiveTracker : public boost::noncopyable
123 {
124 private:
125 boost::shared_ptr<IFlexiblePointerTracker> tracker_;
126 std::string canvasIdentifier_;
127 bool insideCanvas_;
128
129 public:
130 ActiveTracker(const boost::shared_ptr<IFlexiblePointerTracker>& tracker,
131 const WebAssemblyViewport& viewport) :
132 tracker_(tracker),
133 canvasIdentifier_(viewport.GetCanvasIdentifier()),
134 insideCanvas_(true)
135 {
136 if (tracker_.get() == NULL)
137 {
138 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
139 }
140 }
141
142 bool IsAlive() const
143 {
144 return tracker_->IsAlive();
145 }
146
147 void PointerMove(const PointerEvent& event)
148 {
149 tracker_->PointerMove(event);
150 }
151
152 void PointerUp(const PointerEvent& event)
153 {
154 tracker_->PointerUp(event);
155 }
156 };
157 }
158
159 static OrthancStone::PointerEvent* ConvertMouseEvent(
160 const EmscriptenMouseEvent& source,
161 OrthancStone::WebAssemblyViewport& viewport)
162 {
163 std::auto_ptr<OrthancStone::PointerEvent> target(
164 new OrthancStone::PointerEvent);
165
166 target->AddPosition(viewport.GetPixelCenterCoordinates(
167 source.targetX, source.targetY));
168 target->SetAltModifier(source.altKey);
169 target->SetControlModifier(source.ctrlKey);
170 target->SetShiftModifier(source.shiftKey);
171
172 return target.release();
173 }
174
175 std::auto_ptr<OrthancStone::ActiveTracker> tracker_;
176
177 EM_BOOL OnMouseEvent(int eventType,
178 const EmscriptenMouseEvent *mouseEvent,
179 void *userData)
180 {
181 if (mouseEvent != NULL &&
182 userData != NULL)
183 {
184 OrthancStone::WebAssemblyViewport& viewport =
185 *reinterpret_cast<OrthancStone::WebAssemblyViewport*>(userData);
186
187 switch (eventType)
188 {
189 case EMSCRIPTEN_EVENT_CLICK:
190 {
191 static unsigned int count = 0;
192 char buf[64];
193 sprintf(buf, "click %d", count++);
194
195 std::auto_ptr<OrthancStone::TextSceneLayer> layer(new OrthancStone::TextSceneLayer);
196 layer->SetText(buf);
197 viewport.GetScene().SetLayer(100, layer.release());
198 viewport.Refresh();
199 break;
200 }
201
202 case EMSCRIPTEN_EVENT_MOUSEDOWN:
203 {
204 boost::shared_ptr<OrthancStone::IFlexiblePointerTracker> t;
205
206 {
207 std::auto_ptr<OrthancStone::PointerEvent> event(
208 ConvertMouseEvent(*mouseEvent, viewport));
209
210 switch (mouseEvent->button)
211 {
212 case 0: // Left button
213 emscripten_console_log("Creating RotateSceneTracker");
214 t.reset(new OrthancStone::RotateSceneTracker(
215 viewport.GetController(), *event));
216 break;
217
218 case 1: // Middle button
219 emscripten_console_log("Creating PanSceneTracker");
220 LOG(INFO) << "Creating PanSceneTracker" ;
221 t.reset(new OrthancStone::PanSceneTracker(
222 viewport.GetController(), *event));
223 break;
224
225 case 2: // Right button
226 emscripten_console_log("Creating ZoomSceneTracker");
227 t.reset(new OrthancStone::ZoomSceneTracker(
228 viewport.GetController(), *event, viewport.GetCanvasWidth()));
229 break;
230
231 default:
232 break;
233 }
234 }
235
236 if (t.get() != NULL)
237 {
238 tracker_.reset(
239 new OrthancStone::ActiveTracker(t, viewport));
240 viewport.Refresh();
241 }
242
243 break;
244 }
245
246 case EMSCRIPTEN_EVENT_MOUSEMOVE:
247 if (tracker_.get() != NULL)
248 {
249 std::auto_ptr<OrthancStone::PointerEvent> event(
250 ConvertMouseEvent(*mouseEvent, viewport));
251 tracker_->PointerMove(*event);
252 viewport.Refresh();
253 }
254 break;
255
256 case EMSCRIPTEN_EVENT_MOUSEUP:
257 if (tracker_.get() != NULL)
258 {
259 std::auto_ptr<OrthancStone::PointerEvent> event(
260 ConvertMouseEvent(*mouseEvent, viewport));
261 tracker_->PointerUp(*event);
262 viewport.Refresh();
263 if (!tracker_->IsAlive())
264 tracker_.reset();
265 }
266 break;
267
268 default:
269 break;
270 }
271 }
272
273 return true;
274 }
275
276
277 void OrthancStone::WebAssemblyViewport::SetupEvents(const std::string& canvas)
278 {
279 emscripten_set_mousedown_callback(canvas.c_str(), this, false, OnMouseEvent);
280 emscripten_set_mousemove_callback(canvas.c_str(), this, false, OnMouseEvent);
281 emscripten_set_mouseup_callback(canvas.c_str(), this, false, OnMouseEvent);
282 }
283
284
285
286
287 namespace OrthancStone
288 {
289 class VolumeSlicerViewport : public IObserver
290 { 38 {
291 private: 39 private:
292 OrthancStone::WebAssemblyViewport viewport_; 40 OrthancStone::WebAssemblyViewport viewport_;
293 std::auto_ptr<VolumeSceneLayerSource> source_; 41 std::auto_ptr<VolumeSceneLayerSource> source_;
294 VolumeProjection projection_; 42 VolumeProjection projection_;
315 63
316 viewport_.FitContent(); 64 viewport_.FitContent();
317 } 65 }
318 66
319 public: 67 public:
320 VolumeSlicerViewport(MessageBroker& broker, 68 VolumeSlicerWidget(MessageBroker& broker,
321 const std::string& canvas, 69 const std::string& canvas,
322 VolumeProjection projection) : 70 VolumeProjection projection) :
323 IObserver(broker), 71 IObserver(broker),
324 viewport_(broker, canvas), 72 viewport_(broker, canvas),
325 projection_(projection), 73 projection_(projection),
342 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, 90 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls,
343 "Only one slicer can be registered"); 91 "Only one slicer can be registered");
344 } 92 }
345 93
346 loader.RegisterObserverCallback( 94 loader.RegisterObserverCallback(
347 new Callable<VolumeSlicerViewport, DicomVolumeImage::GeometryReadyMessage> 95 new Callable<VolumeSlicerWidget, DicomVolumeImage::GeometryReadyMessage>
348 (*this, &VolumeSlicerViewport::Handle)); 96 (*this, &VolumeSlicerWidget::Handle));
349 97
350 source_.reset(new VolumeSceneLayerSource(viewport_.GetScene(), layerDepth, slicer)); 98 source_.reset(new VolumeSceneLayerSource(viewport_.GetScene(), layerDepth, slicer));
351 99
352 if (configurator != NULL) 100 if (configurator != NULL)
353 { 101 {
361 currentPlane_ < planes_.size()) 109 currentPlane_ < planes_.size())
362 { 110 {
363 source_->Update(planes_[currentPlane_]); 111 source_->Update(planes_[currentPlane_]);
364 viewport_.Refresh(); 112 viewport_.Refresh();
365 } 113 }
114 }
115
116 size_t GetSlicesCount() const
117 {
118 return planes_.size();
366 } 119 }
367 120
368 void Scroll(int delta) 121 void Scroll(int delta)
369 { 122 {
370 if (!planes_.empty()) 123 if (!planes_.empty())
400 153
401 boost::shared_ptr<OrthancStone::DicomVolumeImage> ct_(new OrthancStone::DicomVolumeImage); 154 boost::shared_ptr<OrthancStone::DicomVolumeImage> ct_(new OrthancStone::DicomVolumeImage);
402 155
403 boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> loader_; 156 boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> loader_;
404 157
405 std::auto_ptr<OrthancStone::VolumeSlicerViewport> viewport1_; 158 std::auto_ptr<OrthancStone::VolumeSlicerWidget> widget1_;
406 std::auto_ptr<OrthancStone::VolumeSlicerViewport> viewport2_; 159 std::auto_ptr<OrthancStone::VolumeSlicerWidget> widget2_;
407 std::auto_ptr<OrthancStone::VolumeSlicerViewport> viewport3_; 160 std::auto_ptr<OrthancStone::VolumeSlicerWidget> widget3_;
408 161
409 OrthancStone::MessageBroker broker_; 162 OrthancStone::MessageBroker broker_;
410 OrthancStone::WebAssemblyOracle oracle_(broker_); 163 OrthancStone::WebAssemblyOracle oracle_(broker_);
411 164
412 165
413 EM_BOOL OnWindowResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData) 166 EM_BOOL OnWindowResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
414 { 167 {
415 try 168 try
416 { 169 {
417 if (viewport1_.get() != NULL) 170 if (widget1_.get() != NULL)
418 { 171 {
419 viewport1_->UpdateSize(); 172 widget1_->UpdateSize();
420 } 173 }
421 174
422 if (viewport2_.get() != NULL) 175 if (widget2_.get() != NULL)
423 { 176 {
424 viewport2_->UpdateSize(); 177 widget2_->UpdateSize();
425 } 178 }
426 179
427 if (viewport3_.get() != NULL) 180 if (widget3_.get() != NULL)
428 { 181 {
429 viewport3_->UpdateSize(); 182 widget3_->UpdateSize();
430 } 183 }
431 } 184 }
432 catch (Orthanc::OrthancException& e) 185 catch (Orthanc::OrthancException& e)
433 { 186 {
434 LOG(ERROR) << "Exception while updating canvas size: " << e.What(); 187 LOG(ERROR) << "Exception while updating canvas size: " << e.What();
442 195
443 EM_BOOL OnAnimationFrame(double time, void *userData) 196 EM_BOOL OnAnimationFrame(double time, void *userData)
444 { 197 {
445 try 198 try
446 { 199 {
447 if (viewport1_.get() != NULL) 200 if (widget1_.get() != NULL)
448 { 201 {
449 viewport1_->Refresh(); 202 widget1_->Refresh();
450 } 203 }
451 204
452 if (viewport2_.get() != NULL) 205 if (widget2_.get() != NULL)
453 { 206 {
454 viewport2_->Refresh(); 207 widget2_->Refresh();
455 } 208 }
456 209
457 if (viewport3_.get() != NULL) 210 if (widget3_.get() != NULL)
458 { 211 {
459 viewport3_->Refresh(); 212 widget3_->Refresh();
460 } 213 }
461 214
462 return true; 215 return true;
463 } 216 }
464 catch (Orthanc::OrthancException& e) 217 catch (Orthanc::OrthancException& e)
490 if (wheelEvent->deltaY > 0) 243 if (wheelEvent->deltaY > 0)
491 { 244 {
492 delta = 1; 245 delta = 1;
493 } 246 }
494 247
248 OrthancStone::VolumeSlicerWidget& widget =
249 *reinterpret_cast<OrthancStone::VolumeSlicerWidget*>(userData);
250
495 if (ctrlDown_) 251 if (ctrlDown_)
496 { 252 {
497 delta *= 10; 253 delta *= static_cast<int>(widget.GetSlicesCount() / 10);
498 } 254 }
499 255
500 reinterpret_cast<OrthancStone::VolumeSlicerViewport*>(userData)->Scroll(delta); 256 widget.Scroll(delta);
501 } 257 }
502 } 258 }
503 catch (Orthanc::OrthancException& e) 259 catch (Orthanc::OrthancException& e)
504 { 260 {
505 LOG(ERROR) << "Exception in the wheel event: " << e.What(); 261 LOG(ERROR) << "Exception in the wheel event: " << e.What();
507 263
508 return true; 264 return true;
509 } 265 }
510 266
511 267
512 EM_BOOL OnKey(int eventType, 268 EM_BOOL OnKeyDown(int eventType,
513 const EmscriptenKeyboardEvent *keyEvent, 269 const EmscriptenKeyboardEvent *keyEvent,
514 void *userData) 270 void *userData)
515 { 271 {
516 ctrlDown_ = keyEvent->ctrlKey; 272 ctrlDown_ = keyEvent->ctrlKey;
273 return false;
274 }
275
276
277 EM_BOOL OnKeyUp(int eventType,
278 const EmscriptenKeyboardEvent *keyEvent,
279 void *userData)
280 {
281 ctrlDown_ = false;
517 return false; 282 return false;
518 } 283 }
519 284
520 285
521 286
572 { 337 {
573 try 338 try
574 { 339 {
575 loader_.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct_, oracle_, oracle_)); 340 loader_.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct_, oracle_, oracle_));
576 341
577 viewport1_.reset(new OrthancStone::VolumeSlicerViewport(broker_, "mycanvas1", OrthancStone::VolumeProjection_Axial)); 342 widget1_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas1", OrthancStone::VolumeProjection_Axial));
578 viewport1_->SetSlicer(0, loader_, *loader_, new OrthancStone::GrayscaleStyleConfigurator); 343 widget1_->SetSlicer(0, loader_, *loader_, new OrthancStone::GrayscaleStyleConfigurator);
579 viewport1_->UpdateSize(); 344 widget1_->UpdateSize();
580 345
581 viewport2_.reset(new OrthancStone::VolumeSlicerViewport(broker_, "mycanvas2", OrthancStone::VolumeProjection_Coronal)); 346 widget2_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas2", OrthancStone::VolumeProjection_Coronal));
582 viewport2_->SetSlicer(0, loader_, *loader_, new OrthancStone::GrayscaleStyleConfigurator); 347 widget2_->SetSlicer(0, loader_, *loader_, new OrthancStone::GrayscaleStyleConfigurator);
583 viewport2_->UpdateSize(); 348 widget2_->UpdateSize();
584 349
585 viewport3_.reset(new OrthancStone::VolumeSlicerViewport(broker_, "mycanvas3", OrthancStone::VolumeProjection_Sagittal)); 350 widget3_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas3", OrthancStone::VolumeProjection_Sagittal));
586 viewport3_->SetSlicer(0, loader_, *loader_, new OrthancStone::GrayscaleStyleConfigurator); 351 widget3_->SetSlicer(0, loader_, *loader_, new OrthancStone::GrayscaleStyleConfigurator);
587 viewport3_->UpdateSize(); 352 widget3_->UpdateSize();
588 353
589 emscripten_set_resize_callback("#window", NULL, false, OnWindowResize); 354 emscripten_set_resize_callback("#window", NULL, false, OnWindowResize);
590 355
591 emscripten_set_wheel_callback("mycanvas1", viewport1_.get(), false, OnMouseWheel); 356 emscripten_set_wheel_callback("mycanvas1", widget1_.get(), false, OnMouseWheel);
592 emscripten_set_wheel_callback("mycanvas2", viewport2_.get(), false, OnMouseWheel); 357 emscripten_set_wheel_callback("mycanvas2", widget2_.get(), false, OnMouseWheel);
593 emscripten_set_wheel_callback("mycanvas3", viewport3_.get(), false, OnMouseWheel); 358 emscripten_set_wheel_callback("mycanvas3", widget3_.get(), false, OnMouseWheel);
594 359
595 emscripten_set_keydown_callback("#window", NULL, false, OnKey); 360 emscripten_set_keydown_callback("#window", NULL, false, OnKeyDown);
596 emscripten_set_keyup_callback("#window", NULL, false, OnKey); 361 emscripten_set_keyup_callback("#window", NULL, false, OnKeyUp);
597 362
598 emscripten_request_animation_frame_loop(OnAnimationFrame, NULL); 363 emscripten_request_animation_frame_loop(OnAnimationFrame, NULL);
599 364
600 loader_->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); 365 loader_->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");
601 } 366 }