Mercurial > hg > orthanc-stone
comparison Samples/Deprecated/WebAssembly/BasicMPR.cpp @ 1348:eac254fb6791 broker
Moved Application/Samples/* to Application/Samples/Deprecated/* (remaining) + moved Samples/* to Samples/Deprecated/*
author | Benjamin Golinvaux <bgo@osimis.io> |
---|---|
date | Tue, 07 Apr 2020 14:31:28 +0200 |
parents | Samples/WebAssembly/BasicMPR.cpp@8a0a62189f46 |
children | 28eb7106ef44 |
comparison
equal
deleted
inserted
replaced
1347:bfd77672d825 | 1348:eac254fb6791 |
---|---|
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 | |
22 | |
23 #include "dev.h" | |
24 | |
25 #include <emscripten.h> | |
26 | |
27 #include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h" | |
28 #include "../../Framework/Oracle/SleepOracleCommand.h" | |
29 #include "../../Framework/Oracle/WebAssemblyOracle.h" | |
30 #include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h" | |
31 #include "../../Framework/StoneInitialization.h" | |
32 #include "../../Framework/Volumes/VolumeSceneLayerSource.h" | |
33 | |
34 | |
35 namespace OrthancStone | |
36 { | |
37 class VolumeSlicerWidget : public IObserver | |
38 { | |
39 private: | |
40 OrthancStone::WebAssemblyViewport viewport_; | |
41 std::unique_ptr<VolumeSceneLayerSource> source_; | |
42 VolumeProjection projection_; | |
43 std::vector<CoordinateSystem3D> planes_; | |
44 size_t currentPlane_; | |
45 | |
46 void Handle(const DicomVolumeImage::GeometryReadyMessage& message) | |
47 { | |
48 LOG(INFO) << "Geometry is available"; | |
49 | |
50 const VolumeImageGeometry& geometry = message.GetOrigin().GetGeometry(); | |
51 | |
52 const unsigned int depth = geometry.GetProjectionDepth(projection_); | |
53 currentPlane_ = depth / 2; | |
54 | |
55 planes_.resize(depth); | |
56 | |
57 for (unsigned int z = 0; z < depth; z++) | |
58 { | |
59 planes_[z] = geometry.GetProjectionSlice(projection_, z); | |
60 } | |
61 | |
62 Refresh(); | |
63 | |
64 viewport_.FitContent(); | |
65 } | |
66 | |
67 public: | |
68 VolumeSlicerWidget(MessageBroker& broker, | |
69 const std::string& canvas, | |
70 VolumeProjection projection) : | |
71 IObserver(broker), | |
72 viewport_(broker, canvas), | |
73 projection_(projection), | |
74 currentPlane_(0) | |
75 { | |
76 } | |
77 | |
78 void UpdateSize() | |
79 { | |
80 viewport_.UpdateSize(); | |
81 } | |
82 | |
83 void SetSlicer(int layerDepth, | |
84 const boost::shared_ptr<IVolumeSlicer>& slicer, | |
85 IObservable& loader, | |
86 ILayerStyleConfigurator* configurator) | |
87 { | |
88 if (source_.get() != NULL) | |
89 { | |
90 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, | |
91 "Only one slicer can be registered"); | |
92 } | |
93 | |
94 loader.RegisterObserverCallback( | |
95 new Callable<VolumeSlicerWidget, DicomVolumeImage::GeometryReadyMessage> | |
96 (*this, &VolumeSlicerWidget::Handle)); | |
97 | |
98 source_.reset(new VolumeSceneLayerSource(viewport_.GetScene(), layerDepth, slicer)); | |
99 | |
100 if (configurator != NULL) | |
101 { | |
102 source_->SetConfigurator(configurator); | |
103 } | |
104 } | |
105 | |
106 void Refresh() | |
107 { | |
108 if (source_.get() != NULL && | |
109 currentPlane_ < planes_.size()) | |
110 { | |
111 source_->Update(planes_[currentPlane_]); | |
112 viewport_.Refresh(); | |
113 } | |
114 } | |
115 | |
116 size_t GetSlicesCount() const | |
117 { | |
118 return planes_.size(); | |
119 } | |
120 | |
121 void Scroll(int delta) | |
122 { | |
123 if (!planes_.empty()) | |
124 { | |
125 int tmp = static_cast<int>(currentPlane_) + delta; | |
126 unsigned int next; | |
127 | |
128 if (tmp < 0) | |
129 { | |
130 next = 0; | |
131 } | |
132 else if (tmp >= static_cast<int>(planes_.size())) | |
133 { | |
134 next = planes_.size() - 1; | |
135 } | |
136 else | |
137 { | |
138 next = static_cast<size_t>(tmp); | |
139 } | |
140 | |
141 if (next != currentPlane_) | |
142 { | |
143 currentPlane_ = next; | |
144 Refresh(); | |
145 } | |
146 } | |
147 } | |
148 }; | |
149 } | |
150 | |
151 | |
152 | |
153 | |
154 boost::shared_ptr<OrthancStone::DicomVolumeImage> ct_(new OrthancStone::DicomVolumeImage); | |
155 | |
156 boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> loader_; | |
157 | |
158 std::unique_ptr<OrthancStone::VolumeSlicerWidget> widget1_; | |
159 std::unique_ptr<OrthancStone::VolumeSlicerWidget> widget2_; | |
160 std::unique_ptr<OrthancStone::VolumeSlicerWidget> widget3_; | |
161 | |
162 OrthancStone::MessageBroker broker_; | |
163 OrthancStone::WebAssemblyOracle oracle_(broker_); | |
164 | |
165 | |
166 EM_BOOL OnWindowResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData) | |
167 { | |
168 try | |
169 { | |
170 if (widget1_.get() != NULL) | |
171 { | |
172 widget1_->UpdateSize(); | |
173 } | |
174 | |
175 if (widget2_.get() != NULL) | |
176 { | |
177 widget2_->UpdateSize(); | |
178 } | |
179 | |
180 if (widget3_.get() != NULL) | |
181 { | |
182 widget3_->UpdateSize(); | |
183 } | |
184 } | |
185 catch (Orthanc::OrthancException& e) | |
186 { | |
187 LOG(ERROR) << "Exception while updating canvas size: " << e.What(); | |
188 } | |
189 | |
190 return true; | |
191 } | |
192 | |
193 | |
194 | |
195 | |
196 EM_BOOL OnAnimationFrame(double time, void *userData) | |
197 { | |
198 try | |
199 { | |
200 if (widget1_.get() != NULL) | |
201 { | |
202 widget1_->Refresh(); | |
203 } | |
204 | |
205 if (widget2_.get() != NULL) | |
206 { | |
207 widget2_->Refresh(); | |
208 } | |
209 | |
210 if (widget3_.get() != NULL) | |
211 { | |
212 widget3_->Refresh(); | |
213 } | |
214 | |
215 return true; | |
216 } | |
217 catch (Orthanc::OrthancException& e) | |
218 { | |
219 LOG(ERROR) << "Exception in the animation loop, stopping now: " << e.What(); | |
220 return false; | |
221 } | |
222 } | |
223 | |
224 | |
225 static bool ctrlDown_ = false; | |
226 | |
227 | |
228 EM_BOOL OnMouseWheel(int eventType, | |
229 const EmscriptenWheelEvent *wheelEvent, | |
230 void *userData) | |
231 { | |
232 try | |
233 { | |
234 if (userData != NULL) | |
235 { | |
236 int delta = 0; | |
237 | |
238 if (wheelEvent->deltaY < 0) | |
239 { | |
240 delta = -1; | |
241 } | |
242 | |
243 if (wheelEvent->deltaY > 0) | |
244 { | |
245 delta = 1; | |
246 } | |
247 | |
248 OrthancStone::VolumeSlicerWidget& widget = | |
249 *reinterpret_cast<OrthancStone::VolumeSlicerWidget*>(userData); | |
250 | |
251 if (ctrlDown_) | |
252 { | |
253 delta *= static_cast<int>(widget.GetSlicesCount() / 10); | |
254 } | |
255 | |
256 widget.Scroll(delta); | |
257 } | |
258 } | |
259 catch (Orthanc::OrthancException& e) | |
260 { | |
261 LOG(ERROR) << "Exception in the wheel event: " << e.What(); | |
262 } | |
263 | |
264 return true; | |
265 } | |
266 | |
267 | |
268 EM_BOOL OnKeyDown(int eventType, | |
269 const EmscriptenKeyboardEvent *keyEvent, | |
270 void *userData) | |
271 { | |
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; | |
282 return false; | |
283 } | |
284 | |
285 | |
286 | |
287 | |
288 namespace OrthancStone | |
289 { | |
290 class TestSleep : public IObserver | |
291 { | |
292 private: | |
293 WebAssemblyOracle& oracle_; | |
294 | |
295 void Schedule() | |
296 { | |
297 oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(2000)); | |
298 } | |
299 | |
300 void Handle(const SleepOracleCommand::TimeoutMessage& message) | |
301 { | |
302 LOG(INFO) << "TIMEOUT"; | |
303 Schedule(); | |
304 } | |
305 | |
306 public: | |
307 TestSleep(MessageBroker& broker, | |
308 WebAssemblyOracle& oracle) : | |
309 IObserver(broker), | |
310 oracle_(oracle) | |
311 { | |
312 oracle.RegisterObserverCallback( | |
313 new Callable<TestSleep, SleepOracleCommand::TimeoutMessage> | |
314 (*this, &TestSleep::Handle)); | |
315 | |
316 LOG(INFO) << "STARTING"; | |
317 Schedule(); | |
318 } | |
319 }; | |
320 | |
321 //static TestSleep testSleep(broker_, oracle_); | |
322 } | |
323 | |
324 | |
325 | |
326 static std::map<std::string, std::string> arguments_; | |
327 | |
328 static bool GetArgument(std::string& value, | |
329 const std::string& key) | |
330 { | |
331 std::map<std::string, std::string>::const_iterator found = arguments_.find(key); | |
332 | |
333 if (found == arguments_.end()) | |
334 { | |
335 return false; | |
336 } | |
337 else | |
338 { | |
339 value = found->second; | |
340 return true; | |
341 } | |
342 } | |
343 | |
344 | |
345 extern "C" | |
346 { | |
347 int main(int argc, char const *argv[]) | |
348 { | |
349 OrthancStone::StoneInitialize(); | |
350 Orthanc::Logging::EnableInfoLevel(true); | |
351 // Orthanc::Logging::EnableTraceLevel(true); | |
352 EM_ASM(window.dispatchEvent(new CustomEvent("WebAssemblyLoaded"));); | |
353 } | |
354 | |
355 EMSCRIPTEN_KEEPALIVE | |
356 void SetArgument(const char* key, const char* value) | |
357 { | |
358 // This is called for each GET argument (cf. "app.js") | |
359 LOG(INFO) << "Received GET argument: [" << key << "] = [" << value << "]"; | |
360 arguments_[key] = value; | |
361 } | |
362 | |
363 EMSCRIPTEN_KEEPALIVE | |
364 void Initialize() | |
365 { | |
366 try | |
367 { | |
368 oracle_.SetOrthancRoot(".."); | |
369 | |
370 loader_.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct_, oracle_, oracle_)); | |
371 | |
372 widget1_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas1", OrthancStone::VolumeProjection_Axial)); | |
373 { | |
374 std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator); | |
375 style->SetLinearInterpolation(true); | |
376 style->SetWindowing(OrthancStone::ImageWindowing_Bone); | |
377 widget1_->SetSlicer(0, loader_, *loader_, style.release()); | |
378 } | |
379 widget1_->UpdateSize(); | |
380 | |
381 widget2_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas2", OrthancStone::VolumeProjection_Coronal)); | |
382 { | |
383 std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator); | |
384 style->SetLinearInterpolation(true); | |
385 style->SetWindowing(OrthancStone::ImageWindowing_Bone); | |
386 widget2_->SetSlicer(0, loader_, *loader_, style.release()); | |
387 } | |
388 widget2_->UpdateSize(); | |
389 | |
390 widget3_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas3", OrthancStone::VolumeProjection_Sagittal)); | |
391 { | |
392 std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator); | |
393 style->SetLinearInterpolation(true); | |
394 style->SetWindowing(OrthancStone::ImageWindowing_Bone); | |
395 widget3_->SetSlicer(0, loader_, *loader_, style.release()); | |
396 } | |
397 widget3_->UpdateSize(); | |
398 | |
399 emscripten_set_resize_callback("#window", NULL, false, OnWindowResize); | |
400 | |
401 emscripten_set_wheel_callback("mycanvas1", widget1_.get(), false, OnMouseWheel); | |
402 emscripten_set_wheel_callback("mycanvas2", widget2_.get(), false, OnMouseWheel); | |
403 emscripten_set_wheel_callback("mycanvas3", widget3_.get(), false, OnMouseWheel); | |
404 | |
405 emscripten_set_keydown_callback("#window", NULL, false, OnKeyDown); | |
406 emscripten_set_keyup_callback("#window", NULL, false, OnKeyUp); | |
407 | |
408 emscripten_request_animation_frame_loop(OnAnimationFrame, NULL); | |
409 | |
410 | |
411 std::string ct; | |
412 if (GetArgument(ct, "ct")) | |
413 { | |
414 //loader_->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); | |
415 loader_->LoadSeries(ct); | |
416 } | |
417 else | |
418 { | |
419 LOG(ERROR) << "No Orthanc identifier for the CT series was provided"; | |
420 } | |
421 } | |
422 catch (Orthanc::OrthancException& e) | |
423 { | |
424 LOG(ERROR) << "Exception during Initialize(): " << e.What(); | |
425 } | |
426 } | |
427 } |