Mercurial > hg > orthanc-stone
comparison Samples/Sdl/FusionMprSdl.cpp @ 827:2fd96a637a59
Added FusioMpr sample + small dumb changes
author | Benjamin Golinvaux <bgo@osimis.io> |
---|---|
date | Wed, 29 May 2019 13:44:55 +0200 |
parents | |
children | 28f99af358fa |
comparison
equal
deleted
inserted
replaced
818:e42b491f1fb2 | 827:2fd96a637a59 |
---|---|
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-2019 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 "FusionMprSdl.h" | |
22 | |
23 #include "../../Applications/Sdl/SdlOpenGLWindow.h" | |
24 | |
25 #include "../../Framework/StoneInitialization.h" | |
26 | |
27 #include "../../Framework/Messages/IMessageEmitter.h" | |
28 | |
29 #include "../../Framework/Scene2D/CairoCompositor.h" | |
30 #include "../../Framework/Scene2D/ColorTextureSceneLayer.h" | |
31 #include "../../Framework/Scene2D/OpenGLCompositor.h" | |
32 #include "../../Framework/Scene2D/PanSceneTracker.h" | |
33 #include "../../Framework/Scene2D/ZoomSceneTracker.h" | |
34 #include "../../Framework/Scene2D/RotateSceneTracker.h" | |
35 | |
36 #include "../../Framework/Scene2DViewport/CreateLineMeasureTracker.h" | |
37 #include "../../Framework/Scene2DViewport/CreateAngleMeasureTracker.h" | |
38 #include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h" | |
39 #include "../../Framework/Scene2DViewport/MeasureTool.h" | |
40 #include "../../Framework/Scene2DViewport/PredeclaredTypes.h" | |
41 | |
42 #include "../../Framework/Volumes/VolumeSceneLayerSource.h" | |
43 | |
44 #include <Core/Images/Image.h> | |
45 #include <Core/Images/ImageProcessing.h> | |
46 #include <Core/Images/PngWriter.h> | |
47 #include <Core/Logging.h> | |
48 #include <Core/OrthancException.h> | |
49 | |
50 #include <boost/shared_ptr.hpp> | |
51 #include <boost/weak_ptr.hpp> | |
52 #include <boost/make_shared.hpp> | |
53 #include <boost/thread.hpp> | |
54 | |
55 #include <stdio.h> | |
56 #include "../../Framework/Oracle/GetOrthancWebViewerJpegCommand.h" | |
57 #include "../../Framework/Oracle/ThreadedOracle.h" | |
58 #include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h" | |
59 #include "../../Framework/Loaders/OrthancMultiframeVolumeLoader.h" | |
60 #include "../../Framework/Loaders/DicomStructureSetLoader.h" | |
61 #include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h" | |
62 #include "../../Framework/Scene2D/LookupTableStyleConfigurator.h" | |
63 #include "../../Framework/Volumes/DicomVolumeImageMPRSlicer.h" | |
64 #include "Core/SystemToolbox.h" | |
65 | |
66 namespace OrthancStone | |
67 { | |
68 | |
69 class NativeApplicationContext : public IMessageEmitter | |
70 { | |
71 private: | |
72 boost::shared_mutex mutex_; | |
73 MessageBroker broker_; | |
74 IObservable oracleObservable_; | |
75 | |
76 public: | |
77 NativeApplicationContext() : | |
78 oracleObservable_(broker_) | |
79 { | |
80 } | |
81 | |
82 | |
83 virtual void EmitMessage(const IObserver& observer, | |
84 const IMessage& message) ORTHANC_OVERRIDE | |
85 { | |
86 try | |
87 { | |
88 boost::unique_lock<boost::shared_mutex> lock(mutex_); | |
89 oracleObservable_.EmitMessage(observer, message); | |
90 } | |
91 catch (Orthanc::OrthancException& e) | |
92 { | |
93 LOG(ERROR) << "Exception while emitting a message: " << e.What(); | |
94 } | |
95 } | |
96 | |
97 | |
98 class ReaderLock : public boost::noncopyable | |
99 { | |
100 private: | |
101 NativeApplicationContext& that_; | |
102 boost::shared_lock<boost::shared_mutex> lock_; | |
103 | |
104 public: | |
105 ReaderLock(NativeApplicationContext& that) : | |
106 that_(that), | |
107 lock_(that.mutex_) | |
108 { | |
109 } | |
110 }; | |
111 | |
112 | |
113 class WriterLock : public boost::noncopyable | |
114 { | |
115 private: | |
116 NativeApplicationContext& that_; | |
117 boost::unique_lock<boost::shared_mutex> lock_; | |
118 | |
119 public: | |
120 WriterLock(NativeApplicationContext& that) : | |
121 that_(that), | |
122 lock_(that.mutex_) | |
123 { | |
124 } | |
125 | |
126 MessageBroker& GetBroker() | |
127 { | |
128 return that_.broker_; | |
129 } | |
130 | |
131 IObservable& GetOracleObservable() | |
132 { | |
133 return that_.oracleObservable_; | |
134 } | |
135 }; | |
136 }; | |
137 | |
138 const char* FusionMprMeasureToolToString(size_t i) | |
139 { | |
140 static const char* descs[] = { | |
141 "FusionMprGuiTool_Rotate", | |
142 "FusionMprGuiTool_Pan", | |
143 "FusionMprGuiTool_Zoom", | |
144 "FusionMprGuiTool_LineMeasure", | |
145 "FusionMprGuiTool_CircleMeasure", | |
146 "FusionMprGuiTool_AngleMeasure", | |
147 "FusionMprGuiTool_EllipseMeasure", | |
148 "FusionMprGuiTool_LAST" | |
149 }; | |
150 if (i >= FusionMprGuiTool_LAST) | |
151 { | |
152 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index"); | |
153 } | |
154 return descs[i]; | |
155 } | |
156 | |
157 boost::shared_ptr<Scene2D> FusionMprSdlApp::GetScene() | |
158 { | |
159 return controller_->GetScene(); | |
160 } | |
161 | |
162 boost::shared_ptr<const Scene2D> FusionMprSdlApp::GetScene() const | |
163 { | |
164 return controller_->GetScene(); | |
165 } | |
166 | |
167 void FusionMprSdlApp::SelectNextTool() | |
168 { | |
169 currentTool_ = static_cast<FusionMprGuiTool>(currentTool_ + 1); | |
170 if (currentTool_ == FusionMprGuiTool_LAST) | |
171 currentTool_ = static_cast<FusionMprGuiTool>(0);; | |
172 printf("Current tool is now: %s\n", FusionMprMeasureToolToString(currentTool_)); | |
173 } | |
174 | |
175 void FusionMprSdlApp::DisplayInfoText() | |
176 { | |
177 // do not try to use stuff too early! | |
178 if (compositor_.get() == NULL) | |
179 return; | |
180 | |
181 std::stringstream msg; | |
182 | |
183 for (std::map<std::string, std::string>::const_iterator kv = infoTextMap_.begin(); | |
184 kv != infoTextMap_.end(); ++kv) | |
185 { | |
186 msg << kv->first << " : " << kv->second << std::endl; | |
187 } | |
188 std::string msgS = msg.str(); | |
189 | |
190 TextSceneLayer* layerP = NULL; | |
191 if (GetScene()->HasLayer(FIXED_INFOTEXT_LAYER_ZINDEX)) | |
192 { | |
193 TextSceneLayer& layer = dynamic_cast<TextSceneLayer&>( | |
194 GetScene()->GetLayer(FIXED_INFOTEXT_LAYER_ZINDEX)); | |
195 layerP = &layer; | |
196 } | |
197 else | |
198 { | |
199 std::auto_ptr<TextSceneLayer> layer(new TextSceneLayer); | |
200 layerP = layer.get(); | |
201 layer->SetColor(0, 255, 0); | |
202 layer->SetFontIndex(1); | |
203 layer->SetBorder(20); | |
204 layer->SetAnchor(BitmapAnchor_TopLeft); | |
205 //layer->SetPosition(0,0); | |
206 GetScene()->SetLayer(FIXED_INFOTEXT_LAYER_ZINDEX, layer.release()); | |
207 } | |
208 // position the fixed info text in the upper right corner | |
209 layerP->SetText(msgS.c_str()); | |
210 double cX = compositor_->GetCanvasWidth() * (-0.5); | |
211 double cY = compositor_->GetCanvasHeight() * (-0.5); | |
212 GetScene()->GetCanvasToSceneTransform().Apply(cX,cY); | |
213 layerP->SetPosition(cX, cY); | |
214 } | |
215 | |
216 void FusionMprSdlApp::DisplayFloatingCtrlInfoText(const PointerEvent& e) | |
217 { | |
218 ScenePoint2D p = e.GetMainPosition().Apply(GetScene()->GetCanvasToSceneTransform()); | |
219 | |
220 char buf[128]; | |
221 sprintf(buf, "S:(%0.02f,%0.02f) C:(%0.02f,%0.02f)", | |
222 p.GetX(), p.GetY(), | |
223 e.GetMainPosition().GetX(), e.GetMainPosition().GetY()); | |
224 | |
225 if (GetScene()->HasLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)) | |
226 { | |
227 TextSceneLayer& layer = | |
228 dynamic_cast<TextSceneLayer&>(GetScene()->GetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)); | |
229 layer.SetText(buf); | |
230 layer.SetPosition(p.GetX(), p.GetY()); | |
231 } | |
232 else | |
233 { | |
234 std::auto_ptr<TextSceneLayer> layer(new TextSceneLayer); | |
235 layer->SetColor(0, 255, 0); | |
236 layer->SetText(buf); | |
237 layer->SetBorder(20); | |
238 layer->SetAnchor(BitmapAnchor_BottomCenter); | |
239 layer->SetPosition(p.GetX(), p.GetY()); | |
240 GetScene()->SetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX, layer.release()); | |
241 } | |
242 } | |
243 | |
244 void FusionMprSdlApp::HideInfoText() | |
245 { | |
246 GetScene()->DeleteLayer(FLOATING_INFOTEXT_LAYER_ZINDEX); | |
247 } | |
248 | |
249 void FusionMprSdlApp::HandleApplicationEvent( | |
250 const SDL_Event & event) | |
251 { | |
252 DisplayInfoText(); | |
253 | |
254 if (event.type == SDL_MOUSEMOTION) | |
255 { | |
256 int scancodeCount = 0; | |
257 const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); | |
258 | |
259 if (activeTracker_.get() == NULL && | |
260 SDL_SCANCODE_LALT < scancodeCount && | |
261 keyboardState[SDL_SCANCODE_LALT]) | |
262 { | |
263 // The "left-ctrl" key is down, while no tracker is present | |
264 // Let's display the info text | |
265 PointerEvent e; | |
266 e.AddPosition(compositor_->GetPixelCenterCoordinates( | |
267 event.button.x, event.button.y)); | |
268 | |
269 DisplayFloatingCtrlInfoText(e); | |
270 } | |
271 else | |
272 { | |
273 HideInfoText(); | |
274 //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)"; | |
275 if (activeTracker_.get() != NULL) | |
276 { | |
277 //LOG(TRACE) << "(activeTracker_.get() != NULL)"; | |
278 PointerEvent e; | |
279 e.AddPosition(compositor_->GetPixelCenterCoordinates( | |
280 event.button.x, event.button.y)); | |
281 | |
282 //LOG(TRACE) << "event.button.x = " << event.button.x << " " << | |
283 // "event.button.y = " << event.button.y; | |
284 LOG(TRACE) << "activeTracker_->PointerMove(e); " << | |
285 e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY(); | |
286 | |
287 activeTracker_->PointerMove(e); | |
288 if (!activeTracker_->IsAlive()) | |
289 activeTracker_.reset(); | |
290 } | |
291 } | |
292 } | |
293 else if (event.type == SDL_MOUSEBUTTONUP) | |
294 { | |
295 if (activeTracker_) | |
296 { | |
297 PointerEvent e; | |
298 e.AddPosition(compositor_->GetPixelCenterCoordinates(event.button.x, event.button.y)); | |
299 activeTracker_->PointerUp(e); | |
300 if (!activeTracker_->IsAlive()) | |
301 activeTracker_.reset(); | |
302 } | |
303 } | |
304 else if (event.type == SDL_MOUSEBUTTONDOWN) | |
305 { | |
306 PointerEvent e; | |
307 e.AddPosition(compositor_->GetPixelCenterCoordinates( | |
308 event.button.x, event.button.y)); | |
309 if (activeTracker_) | |
310 { | |
311 activeTracker_->PointerDown(e); | |
312 if (!activeTracker_->IsAlive()) | |
313 activeTracker_.reset(); | |
314 } | |
315 else | |
316 { | |
317 // we ATTEMPT to create a tracker if need be | |
318 activeTracker_ = CreateSuitableTracker(event, e); | |
319 } | |
320 } | |
321 else if (event.type == SDL_KEYDOWN && | |
322 event.key.repeat == 0 /* Ignore key bounce */) | |
323 { | |
324 switch (event.key.keysym.sym) | |
325 { | |
326 case SDLK_ESCAPE: | |
327 if (activeTracker_) | |
328 { | |
329 activeTracker_->Cancel(); | |
330 if (!activeTracker_->IsAlive()) | |
331 activeTracker_.reset(); | |
332 } | |
333 break; | |
334 | |
335 case SDLK_t: | |
336 if (!activeTracker_) | |
337 SelectNextTool(); | |
338 else | |
339 { | |
340 LOG(WARNING) << "You cannot change the active tool when an interaction" | |
341 " is taking place"; | |
342 } | |
343 break; | |
344 case SDLK_s: | |
345 controller_->FitContent(compositor_->GetCanvasWidth(), | |
346 compositor_->GetCanvasHeight()); | |
347 break; | |
348 | |
349 case SDLK_z: | |
350 LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; | |
351 if (event.key.keysym.mod & KMOD_CTRL) | |
352 { | |
353 if (controller_->CanUndo()) | |
354 { | |
355 LOG(TRACE) << "Undoing..."; | |
356 controller_->Undo(); | |
357 } | |
358 else | |
359 { | |
360 LOG(WARNING) << "Nothing to undo!!!"; | |
361 } | |
362 } | |
363 break; | |
364 | |
365 case SDLK_y: | |
366 LOG(TRACE) << "SDLK_y has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; | |
367 if (event.key.keysym.mod & KMOD_CTRL) | |
368 { | |
369 if (controller_->CanRedo()) | |
370 { | |
371 LOG(TRACE) << "Redoing..."; | |
372 controller_->Redo(); | |
373 } | |
374 else | |
375 { | |
376 LOG(WARNING) << "Nothing to redo!!!"; | |
377 } | |
378 } | |
379 break; | |
380 | |
381 case SDLK_c: | |
382 TakeScreenshot( | |
383 "screenshot.png", | |
384 compositor_->GetCanvasWidth(), | |
385 compositor_->GetCanvasHeight()); | |
386 break; | |
387 | |
388 default: | |
389 break; | |
390 } | |
391 } | |
392 } | |
393 | |
394 | |
395 void FusionMprSdlApp::OnSceneTransformChanged( | |
396 const ViewportController::SceneTransformChanged& message) | |
397 { | |
398 DisplayInfoText(); | |
399 } | |
400 | |
401 boost::shared_ptr<IFlexiblePointerTracker> FusionMprSdlApp::CreateSuitableTracker( | |
402 const SDL_Event & event, | |
403 const PointerEvent & e) | |
404 { | |
405 using namespace Orthanc; | |
406 | |
407 switch (event.button.button) | |
408 { | |
409 case SDL_BUTTON_MIDDLE: | |
410 return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker | |
411 (controller_, e)); | |
412 | |
413 case SDL_BUTTON_RIGHT: | |
414 return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker | |
415 (controller_, e, compositor_->GetCanvasHeight())); | |
416 | |
417 case SDL_BUTTON_LEFT: | |
418 { | |
419 //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:"; | |
420 // TODO: we need to iterate on the set of measuring tool and perform | |
421 // a hit test to check if a tracker needs to be created for edition. | |
422 // Otherwise, depending upon the active tool, we might want to create | |
423 // a "measuring tool creation" tracker | |
424 | |
425 // TODO: if there are conflicts, we should prefer a tracker that | |
426 // pertains to the type of measuring tool currently selected (TBD?) | |
427 boost::shared_ptr<IFlexiblePointerTracker> hitTestTracker = TrackerHitTest(e); | |
428 | |
429 if (hitTestTracker != NULL) | |
430 { | |
431 //LOG(TRACE) << "hitTestTracker != NULL"; | |
432 return hitTestTracker; | |
433 } | |
434 else | |
435 { | |
436 switch (currentTool_) | |
437 { | |
438 case FusionMprGuiTool_Rotate: | |
439 //LOG(TRACE) << "Creating RotateSceneTracker"; | |
440 return boost::shared_ptr<IFlexiblePointerTracker>(new RotateSceneTracker( | |
441 controller_, e)); | |
442 case FusionMprGuiTool_Pan: | |
443 return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker( | |
444 controller_, e)); | |
445 case FusionMprGuiTool_Zoom: | |
446 return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker( | |
447 controller_, e, compositor_->GetCanvasHeight())); | |
448 //case GuiTool_AngleMeasure: | |
449 // return new AngleMeasureTracker(GetScene(), e); | |
450 //case GuiTool_CircleMeasure: | |
451 // return new CircleMeasureTracker(GetScene(), e); | |
452 //case GuiTool_EllipseMeasure: | |
453 // return new EllipseMeasureTracker(GetScene(), e); | |
454 case FusionMprGuiTool_LineMeasure: | |
455 return boost::shared_ptr<IFlexiblePointerTracker>(new CreateLineMeasureTracker( | |
456 IObserver::GetBroker(), controller_, e)); | |
457 case FusionMprGuiTool_AngleMeasure: | |
458 return boost::shared_ptr<IFlexiblePointerTracker>(new CreateAngleMeasureTracker( | |
459 IObserver::GetBroker(), controller_, e)); | |
460 case FusionMprGuiTool_CircleMeasure: | |
461 LOG(ERROR) << "Not implemented yet!"; | |
462 return boost::shared_ptr<IFlexiblePointerTracker>(); | |
463 case FusionMprGuiTool_EllipseMeasure: | |
464 LOG(ERROR) << "Not implemented yet!"; | |
465 return boost::shared_ptr<IFlexiblePointerTracker>(); | |
466 default: | |
467 throw OrthancException(ErrorCode_InternalError, "Wrong tool!"); | |
468 } | |
469 } | |
470 } | |
471 default: | |
472 return boost::shared_ptr<IFlexiblePointerTracker>(); | |
473 } | |
474 } | |
475 | |
476 | |
477 FusionMprSdlApp::FusionMprSdlApp(MessageBroker& broker) : IObserver(broker) | |
478 , currentTool_(FusionMprGuiTool_Rotate) | |
479 { | |
480 controller_ = boost::shared_ptr<ViewportController>(new ViewportController(broker)); | |
481 | |
482 controller_->RegisterObserverCallback( | |
483 new Callable<FusionMprSdlApp, ViewportController::SceneTransformChanged> | |
484 (*this, &FusionMprSdlApp::OnSceneTransformChanged)); | |
485 | |
486 TEXTURE_2x2_1_ZINDEX = 1; | |
487 TEXTURE_1x1_ZINDEX = 2; | |
488 TEXTURE_2x2_2_ZINDEX = 3; | |
489 LINESET_1_ZINDEX = 4; | |
490 LINESET_2_ZINDEX = 5; | |
491 FLOATING_INFOTEXT_LAYER_ZINDEX = 6; | |
492 FIXED_INFOTEXT_LAYER_ZINDEX = 7; | |
493 } | |
494 | |
495 void FusionMprSdlApp::PrepareScene() | |
496 { | |
497 // Texture of 2x2 size | |
498 { | |
499 Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); | |
500 | |
501 uint8_t* p = reinterpret_cast<uint8_t*>(i.GetRow(0)); | |
502 p[0] = 255; | |
503 p[1] = 0; | |
504 p[2] = 0; | |
505 | |
506 p[3] = 0; | |
507 p[4] = 255; | |
508 p[5] = 0; | |
509 | |
510 p = reinterpret_cast<uint8_t*>(i.GetRow(1)); | |
511 p[0] = 0; | |
512 p[1] = 0; | |
513 p[2] = 255; | |
514 | |
515 p[3] = 255; | |
516 p[4] = 0; | |
517 p[5] = 0; | |
518 | |
519 GetScene()->SetLayer(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i)); | |
520 } | |
521 } | |
522 | |
523 void FusionMprSdlApp::DisableTracker() | |
524 { | |
525 if (activeTracker_) | |
526 { | |
527 activeTracker_->Cancel(); | |
528 activeTracker_.reset(); | |
529 } | |
530 } | |
531 | |
532 void FusionMprSdlApp::TakeScreenshot(const std::string& target, | |
533 unsigned int canvasWidth, | |
534 unsigned int canvasHeight) | |
535 { | |
536 CairoCompositor compositor(*GetScene(), canvasWidth, canvasHeight); | |
537 compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_0, Orthanc::Encoding_Latin1); | |
538 compositor.Refresh(); | |
539 | |
540 Orthanc::ImageAccessor canvas; | |
541 compositor.GetCanvas().GetReadOnlyAccessor(canvas); | |
542 | |
543 Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false); | |
544 Orthanc::ImageProcessing::Convert(png, canvas); | |
545 | |
546 Orthanc::PngWriter writer; | |
547 writer.WriteToFile(target, png); | |
548 } | |
549 | |
550 | |
551 boost::shared_ptr<IFlexiblePointerTracker> FusionMprSdlApp::TrackerHitTest(const PointerEvent & e) | |
552 { | |
553 // std::vector<boost::shared_ptr<MeasureTool>> measureTools_; | |
554 return boost::shared_ptr<IFlexiblePointerTracker>(); | |
555 } | |
556 | |
557 static void GLAPIENTRY | |
558 OpenGLMessageCallback(GLenum source, | |
559 GLenum type, | |
560 GLuint id, | |
561 GLenum severity, | |
562 GLsizei length, | |
563 const GLchar* message, | |
564 const void* userParam) | |
565 { | |
566 if (severity != GL_DEBUG_SEVERITY_NOTIFICATION) | |
567 { | |
568 fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", | |
569 (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), | |
570 type, severity, message); | |
571 } | |
572 } | |
573 | |
574 static bool g_stopApplication = false; | |
575 | |
576 | |
577 void FusionMprSdlApp::Handle(const DicomVolumeImage::GeometryReadyMessage& message) | |
578 { | |
579 printf("Geometry ready\n"); | |
580 | |
581 //plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry(); | |
582 //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry(); | |
583 plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry(); | |
584 plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f)); | |
585 | |
586 //Refresh(); | |
587 } | |
588 | |
589 | |
590 void FusionMprSdlApp::Handle(const OracleCommandExceptionMessage& message) | |
591 { | |
592 printf("EXCEPTION: [%s] on command type %d\n", message.GetException().What(), message.GetCommand().GetType()); | |
593 | |
594 switch (message.GetCommand().GetType()) | |
595 { | |
596 case IOracleCommand::Type_GetOrthancWebViewerJpeg: | |
597 printf("URI: [%s]\n", dynamic_cast<const GetOrthancWebViewerJpegCommand&> | |
598 (message.GetCommand()).GetUri().c_str()); | |
599 break; | |
600 | |
601 default: | |
602 break; | |
603 } | |
604 } | |
605 | |
606 void FusionMprSdlApp::SetVolume1(int depth, | |
607 const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, | |
608 OrthancStone::ILayerStyleConfigurator* style) | |
609 { | |
610 source1_.reset(new OrthancStone::VolumeSceneLayerSource(*controller_->GetScene(), depth, volume)); | |
611 | |
612 if (style != NULL) | |
613 { | |
614 source1_->SetConfigurator(style); | |
615 } | |
616 } | |
617 | |
618 void FusionMprSdlApp::SetVolume2(int depth, | |
619 const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, | |
620 OrthancStone::ILayerStyleConfigurator* style) | |
621 { | |
622 source2_.reset(new OrthancStone::VolumeSceneLayerSource(*controller_->GetScene(), depth, volume)); | |
623 | |
624 if (style != NULL) | |
625 { | |
626 source2_->SetConfigurator(style); | |
627 } | |
628 } | |
629 | |
630 void FusionMprSdlApp::SetStructureSet(int depth, | |
631 const boost::shared_ptr<OrthancStone::DicomStructureSetLoader>& volume) | |
632 { | |
633 source3_.reset(new OrthancStone::VolumeSceneLayerSource(*controller_->GetScene(), depth, volume)); | |
634 } | |
635 | |
636 void FusionMprSdlApp::Run() | |
637 { | |
638 // False means we do NOT let Windows treat this as a legacy application | |
639 // that needs to be scaled | |
640 SdlOpenGLWindow window("Hello", 1024, 1024, false); | |
641 | |
642 controller_->FitContent(window.GetCanvasWidth(), window.GetCanvasHeight()); | |
643 | |
644 glEnable(GL_DEBUG_OUTPUT); | |
645 glDebugMessageCallback(OpenGLMessageCallback, 0); | |
646 | |
647 compositor_.reset(new OpenGLCompositor(window, *GetScene())); | |
648 | |
649 compositor_->SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, | |
650 FONT_SIZE_0, Orthanc::Encoding_Latin1); | |
651 compositor_->SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT, | |
652 FONT_SIZE_1, Orthanc::Encoding_Latin1); | |
653 | |
654 | |
655 //////// from loader | |
656 | |
657 // from main | |
658 NativeApplicationContext context; | |
659 ThreadedOracle oracle(context); | |
660 | |
661 | |
662 | |
663 { | |
664 Orthanc::WebServiceParameters p; | |
665 //p.SetUrl("http://localhost:8043/"); | |
666 p.SetCredentials("orthanc", "orthanc"); | |
667 oracle.SetOrthancParameters(p); | |
668 } | |
669 | |
670 //////// from Run | |
671 | |
672 boost::shared_ptr<DicomVolumeImage> ct(new DicomVolumeImage); | |
673 boost::shared_ptr<DicomVolumeImage> dose(new DicomVolumeImage); | |
674 | |
675 | |
676 boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader> ctLoader; | |
677 boost::shared_ptr<OrthancMultiframeVolumeLoader> doseLoader; | |
678 boost::shared_ptr<DicomStructureSetLoader> rtstructLoader; | |
679 | |
680 { | |
681 NativeApplicationContext::WriterLock lock(context); | |
682 //toto.reset(new Toto(oracle, lock.GetOracleObservable())); | |
683 oracle_ = &oracle; | |
684 IObservable* oracleObservable = &lock.GetOracleObservable(); | |
685 | |
686 | |
687 //oracleObservable->RegisterObserverCallback | |
688 //(new Callable | |
689 // <FusionMprSdlApp, SleepOracleCommand::TimeoutMessage>(*this, &FusionMprSdlApp::Handle)); | |
690 | |
691 | |
692 //oracleObservable->RegisterObserverCallback | |
693 //(new Callable | |
694 // <Toto, GetOrthancImageCommand::SuccessMessage>(*this, &FusionMprSdlApp::Handle)); | |
695 | |
696 //oracleObservable->RegisterObserverCallback | |
697 //(new Callable | |
698 // <FusionMprSdlApp, GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &ToFusionMprSdlAppto::Handle)); | |
699 | |
700 oracleObservable->RegisterObserverCallback | |
701 (new Callable | |
702 <FusionMprSdlApp, OracleCommandExceptionMessage>(*this, &FusionMprSdlApp::Handle)); | |
703 | |
704 | |
705 ctLoader.reset(new OrthancSeriesVolumeProgressiveLoader(ct, oracle, lock.GetOracleObservable())); | |
706 doseLoader.reset(new OrthancMultiframeVolumeLoader(dose, oracle, lock.GetOracleObservable())); | |
707 rtstructLoader.reset(new DicomStructureSetLoader(oracle, lock.GetOracleObservable())); | |
708 } | |
709 | |
710 | |
711 //toto->SetReferenceLoader(*ctLoader); | |
712 doseLoader->RegisterObserverCallback | |
713 (new Callable | |
714 <FusionMprSdlApp, DicomVolumeImage::GeometryReadyMessage>(*this, &FusionMprSdlApp::Handle)); | |
715 | |
716 this->SetVolume1(0, ctLoader, new GrayscaleStyleConfigurator); | |
717 | |
718 { | |
719 std::auto_ptr<LookupTableStyleConfigurator> config(new LookupTableStyleConfigurator); | |
720 config->SetLookupTable(Orthanc::EmbeddedResources::COLORMAP_HOT); | |
721 | |
722 boost::shared_ptr<DicomVolumeImageMPRSlicer> tmp(new DicomVolumeImageMPRSlicer(dose)); | |
723 this->SetVolume2(1, tmp, config.release()); | |
724 } | |
725 | |
726 this->SetStructureSet(2, rtstructLoader); | |
727 | |
728 #if 0 | |
729 // BGO data | |
730 ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // CT | |
731 doseLoader->LoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // RT-DOSE | |
732 //rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT | |
733 #else | |
734 //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT | |
735 //doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE | |
736 //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6"); // RT-STRUCT | |
737 | |
738 // 2017-05-16 | |
739 ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // CT | |
740 doseLoader->LoadInstance("eac822ef-a395f94e-e8121fe0-8411fef8-1f7bffad"); // RT-DOSE | |
741 rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT | |
742 #endif | |
743 | |
744 oracle.Start(); | |
745 | |
746 //// END from loader | |
747 | |
748 while (!g_stopApplication) | |
749 { | |
750 compositor_->Refresh(); | |
751 | |
752 //////// from loader | |
753 if (source1_.get() != NULL) | |
754 { | |
755 source1_->Update(plane_); | |
756 } | |
757 | |
758 if (source2_.get() != NULL) | |
759 { | |
760 source2_->Update(plane_); | |
761 } | |
762 | |
763 if (source3_.get() != NULL) | |
764 { | |
765 source3_->Update(plane_); | |
766 } | |
767 //// END from loader | |
768 | |
769 SDL_Event event; | |
770 while (!g_stopApplication && SDL_PollEvent(&event)) | |
771 { | |
772 if (event.type == SDL_QUIT) | |
773 { | |
774 g_stopApplication = true; | |
775 break; | |
776 } | |
777 else if (event.type == SDL_WINDOWEVENT && | |
778 event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) | |
779 { | |
780 DisableTracker(); // was: tracker.reset(NULL); | |
781 compositor_->UpdateSize(); | |
782 } | |
783 else if (event.type == SDL_KEYDOWN && | |
784 event.key.repeat == 0 /* Ignore key bounce */) | |
785 { | |
786 switch (event.key.keysym.sym) | |
787 { | |
788 case SDLK_f: | |
789 window.GetWindow().ToggleMaximize(); | |
790 break; | |
791 | |
792 case SDLK_q: | |
793 g_stopApplication = true; | |
794 break; | |
795 default: | |
796 break; | |
797 } | |
798 } | |
799 HandleApplicationEvent(event); | |
800 } | |
801 SDL_Delay(1); | |
802 } | |
803 | |
804 // the following is paramount because the compositor holds a reference | |
805 // to the scene and we do not want this reference to become dangling | |
806 compositor_.reset(NULL); | |
807 | |
808 //// from loader | |
809 | |
810 Orthanc::SystemToolbox::ServerBarrier(); | |
811 | |
812 /** | |
813 * WARNING => The oracle must be stopped BEFORE the objects using | |
814 * it are destroyed!!! This forces to wait for the completion of | |
815 * the running callback methods. Otherwise, the callbacks methods | |
816 * might still be running while their parent object is destroyed, | |
817 * resulting in crashes. This is very visible if adding a sleep(), | |
818 * as in (*). | |
819 **/ | |
820 | |
821 oracle.Stop(); | |
822 //// END from loader | |
823 } | |
824 | |
825 void FusionMprSdlApp::SetInfoDisplayMessage( | |
826 std::string key, std::string value) | |
827 { | |
828 if (value == "") | |
829 infoTextMap_.erase(key); | |
830 else | |
831 infoTextMap_[key] = value; | |
832 DisplayInfoText(); | |
833 } | |
834 | |
835 } | |
836 | |
837 | |
838 boost::weak_ptr<OrthancStone::FusionMprSdlApp> g_app; | |
839 | |
840 void FusionMprSdl_SetInfoDisplayMessage(std::string key, std::string value) | |
841 { | |
842 boost::shared_ptr<OrthancStone::FusionMprSdlApp> app = g_app.lock(); | |
843 if (app) | |
844 { | |
845 app->SetInfoDisplayMessage(key, value); | |
846 } | |
847 } | |
848 | |
849 /** | |
850 * IMPORTANT: The full arguments to "main()" are needed for SDL on | |
851 * Windows. Otherwise, one gets the linking error "undefined reference | |
852 * to `SDL_main'". https://wiki.libsdl.org/FAQWindows | |
853 **/ | |
854 int main(int argc, char* argv[]) | |
855 { | |
856 using namespace OrthancStone; | |
857 | |
858 StoneInitialize(); | |
859 Orthanc::Logging::EnableInfoLevel(true); | |
860 // Orthanc::Logging::EnableTraceLevel(true); | |
861 | |
862 try | |
863 { | |
864 MessageBroker broker; | |
865 boost::shared_ptr<FusionMprSdlApp> app(new FusionMprSdlApp(broker)); | |
866 g_app = app; | |
867 app->PrepareScene(); | |
868 app->Run(); | |
869 } | |
870 catch (Orthanc::OrthancException& e) | |
871 { | |
872 LOG(ERROR) << "EXCEPTION: " << e.What(); | |
873 } | |
874 | |
875 StoneFinalize(); | |
876 | |
877 return 0; | |
878 } | |
879 | |
880 |