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