Mercurial > hg > orthanc-stone
comparison Samples/Sdl/RtViewer/RtViewerSdl.cpp @ 1404:3e644f6fadd4
Three-viewport is now OK in SDL and Wasm
author | Benjamin Golinvaux <bgo@osimis.io> |
---|---|
date | Wed, 29 Apr 2020 22:06:24 +0200 |
parents | 27e0a00bd3e8 |
children | 5d7ee14dc1eb |
comparison
equal
deleted
inserted
replaced
1395:62dc0d737e7b | 1404:3e644f6fadd4 |
---|---|
16 * | 16 * |
17 * You should have received a copy of the GNU Affero General Public License | 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/>. | 18 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 **/ | 19 **/ |
20 | 20 |
21 #include "RtViewer.h" | 21 #include "RtViewerApp.h" |
22 #include "RtViewerView.h" | |
22 #include "../SdlHelpers.h" | 23 #include "../SdlHelpers.h" |
23 | 24 |
24 // Stone of Orthanc includes | 25 // Stone of Orthanc includes |
25 #include <Framework/Loaders/GenericLoadersContext.h> | 26 #include <Framework/Loaders/GenericLoadersContext.h> |
26 #include <Framework/OpenGL/SdlOpenGLContext.h> | 27 #include <Framework/OpenGL/SdlOpenGLContext.h> |
59 } | 60 } |
60 } | 61 } |
61 | 62 |
62 namespace OrthancStone | 63 namespace OrthancStone |
63 { | 64 { |
64 void RtViewerApp::CreateViewport() | 65 void RtViewerView::EnableGLDebugOutput() |
66 { | |
67 glEnable(GL_DEBUG_OUTPUT); | |
68 glDebugMessageCallback(OpenGLMessageCallback, 0); | |
69 } | |
70 | |
71 boost::shared_ptr<IViewport> RtViewerView::CreateViewport(const std::string& canvasId) | |
65 { | 72 { |
66 // False means we do NOT let Windows treat this as a legacy application that needs to be scaled | 73 // False means we do NOT let Windows treat this as a legacy application that needs to be scaled |
67 viewport_ = SdlOpenGLViewport::Create("CT RTDOSE RTSTRUCT viewer", 1024, 1024, false); | 74 return SdlOpenGLViewport::Create(canvasId, 1024, 1024, false); |
68 } | 75 } |
69 | 76 |
70 void RtViewerApp::ProcessOptions(int argc, char* argv[]) | 77 void RtViewerApp::ProcessOptions(int argc, char* argv[]) |
71 { | 78 { |
72 namespace po = boost::program_options; | 79 namespace po = boost::program_options; |
110 } | 117 } |
111 | 118 |
112 void RtViewerApp::RunSdl(int argc, char* argv[]) | 119 void RtViewerApp::RunSdl(int argc, char* argv[]) |
113 { | 120 { |
114 ProcessOptions(argc, argv); | 121 ProcessOptions(argc, argv); |
115 | |
116 { | |
117 std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); | |
118 ViewportController& controller = lock->GetController(); | |
119 Scene2D& scene = controller.GetScene(); | |
120 ICompositor& compositor = lock->GetCompositor(); | |
121 | |
122 // False means we do NOT let a hi-DPI aware desktop managedr treat this as a legacy application that requires | |
123 // scaling. | |
124 controller.FitContent(compositor.GetCanvasWidth(), compositor.GetCanvasHeight()); | |
125 | |
126 glEnable(GL_DEBUG_OUTPUT); | |
127 glDebugMessageCallback(OpenGLMessageCallback, 0); | |
128 | |
129 compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, | |
130 FONT_SIZE_0, Orthanc::Encoding_Latin1); | |
131 compositor.SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT, | |
132 FONT_SIZE_1, Orthanc::Encoding_Latin1); | |
133 } | |
134 | 122 |
135 /** | 123 /** |
136 Create the shared loaders context | 124 Create the shared loaders context |
137 */ | 125 */ |
138 loadersContext_.reset(new GenericLoadersContext(1, 4, 1)); | 126 loadersContext_.reset(new GenericLoadersContext(1, 4, 1)); |
168 loadersContext->SetOrthancParameters(p); | 156 loadersContext->SetOrthancParameters(p); |
169 } | 157 } |
170 | 158 |
171 loadersContext->StartOracle(); | 159 loadersContext->StartOracle(); |
172 | 160 |
161 CreateLoaders(); | |
162 | |
163 /** | |
164 Create viewports | |
165 */ | |
166 CreateView("RtViewer Axial", VolumeProjection_Axial); | |
167 CreateView("RtViewer Coronal", VolumeProjection_Coronal); | |
168 CreateView("RtViewer Sagittal", VolumeProjection_Sagittal); | |
169 | |
170 for (size_t i = 0; i < views_.size(); ++i) | |
171 { | |
172 views_[i]->PrepareViewport(); | |
173 views_[i]->EnableGLDebugOutput(); | |
174 } | |
175 | |
176 DefaultViewportInteractor interactor; | |
177 | |
173 /** | 178 /** |
174 It is very important that the Oracle (responsible for network I/O) be started before creating and firing the | 179 It is very important that the Oracle (responsible for network I/O) be started before creating and firing the |
175 loaders, for any command scheduled by the loader before the oracle is started will be lost. | 180 loaders, for any command scheduled by the loader before the oracle is started will be lost. |
176 */ | 181 */ |
177 PrepareLoadersAndSlicers(); | 182 StartLoaders(); |
178 | 183 |
179 DefaultViewportInteractor interactor; | 184 |
180 | 185 std::vector<boost::shared_ptr<SdlViewport>> sdlViewports; |
181 boost::shared_ptr<SdlViewport> viewport = boost::dynamic_pointer_cast<SdlViewport>(viewport_); | 186 for(size_t i = 0; i < views_.size(); ++i) |
182 | 187 { |
183 OrthancStoneHelpers::SdlRunLoop(viewport, interactor); | 188 boost::shared_ptr<RtViewerView> view = views_[i]; |
184 | 189 boost::shared_ptr<IViewport> viewport = view->GetViewport(); |
190 boost::shared_ptr<SdlViewport> sdlViewport = | |
191 boost::dynamic_pointer_cast<SdlViewport>(viewport); | |
192 sdlViewports.push_back(sdlViewport); | |
193 } | |
194 OrthancStoneHelpers::SdlRunLoop(sdlViewports, interactor); | |
185 loadersContext->StopOracle(); | 195 loadersContext->StopOracle(); |
186 } | 196 } |
187 | 197 |
188 void RtViewerApp::TakeScreenshot(const std::string& target, | 198 void RtViewerView::TakeScreenshot(const std::string& target, |
189 unsigned int canvasWidth, | 199 unsigned int canvasWidth, |
190 unsigned int canvasHeight) | 200 unsigned int canvasHeight) |
191 { | 201 { |
192 std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); | 202 std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); |
193 ViewportController& controller = lock->GetController(); | 203 ViewportController& controller = lock->GetController(); |
204 Orthanc::ImageProcessing::Convert(png, canvas); | 214 Orthanc::ImageProcessing::Convert(png, canvas); |
205 | 215 |
206 Orthanc::PngWriter writer; | 216 Orthanc::PngWriter writer; |
207 writer.WriteToFile(target, png); | 217 writer.WriteToFile(target, png); |
208 } | 218 } |
209 | |
210 | |
211 #if 0 | |
212 void RtViewerApp::HandleApplicationEvent( | |
213 const SDL_Event& event) | |
214 { | |
215 //DisplayInfoText(); | |
216 | |
217 std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); | |
218 ViewportController& controller = lock->GetController(); | |
219 Scene2D& scene = controller.GetScene(); | |
220 ICompositor& compositor = lock->GetCompositor(); | |
221 | |
222 if (event.type == SDL_MOUSEMOTION) | |
223 { | |
224 int scancodeCount = 0; | |
225 const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); | |
226 | |
227 if (activeTracker_.get() == NULL && | |
228 SDL_SCANCODE_LALT < scancodeCount && | |
229 keyboardState[SDL_SCANCODE_LALT]) | |
230 { | |
231 // The "left-ctrl" key is down, while no tracker is present | |
232 // Let's display the info text | |
233 PointerEvent e; | |
234 e.AddPosition(compositor.GetPixelCenterCoordinates( | |
235 event.button.x, event.button.y)); | |
236 | |
237 DisplayFloatingCtrlInfoText(e); | |
238 } | |
239 else | |
240 { | |
241 HideInfoText(); | |
242 //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)"; | |
243 if (activeTracker_.get() != NULL) | |
244 { | |
245 //LOG(TRACE) << "(activeTracker_.get() != NULL)"; | |
246 PointerEvent e; | |
247 e.AddPosition(compositor.GetPixelCenterCoordinates( | |
248 event.button.x, event.button.y)); | |
249 | |
250 //LOG(TRACE) << "event.button.x = " << event.button.x << " " << | |
251 // "event.button.y = " << event.button.y; | |
252 LOG(TRACE) << "activeTracker_->PointerMove(e); " << | |
253 e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY(); | |
254 | |
255 activeTracker_->PointerMove(e); | |
256 if (!activeTracker_->IsAlive()) | |
257 activeTracker_.reset(); | |
258 } | |
259 } | |
260 } | |
261 else if (event.type == SDL_MOUSEBUTTONUP) | |
262 { | |
263 if (activeTracker_) | |
264 { | |
265 PointerEvent e; | |
266 e.AddPosition(compositor.GetPixelCenterCoordinates(event.button.x, event.button.y)); | |
267 activeTracker_->PointerUp(e); | |
268 if (!activeTracker_->IsAlive()) | |
269 activeTracker_.reset(); | |
270 } | |
271 } | |
272 else if (event.type == SDL_MOUSEBUTTONDOWN) | |
273 { | |
274 PointerEvent e; | |
275 e.AddPosition(compositor.GetPixelCenterCoordinates( | |
276 event.button.x, event.button.y)); | |
277 if (activeTracker_) | |
278 { | |
279 activeTracker_->PointerDown(e); | |
280 if (!activeTracker_->IsAlive()) | |
281 activeTracker_.reset(); | |
282 } | |
283 else | |
284 { | |
285 // we ATTEMPT to create a tracker if need be | |
286 activeTracker_ = CreateSuitableTracker(event, e); | |
287 } | |
288 } | |
289 else if (event.type == SDL_KEYDOWN && | |
290 event.key.repeat == 0 /* Ignore key bounce */) | |
291 { | |
292 switch (event.key.keysym.sym) | |
293 { | |
294 case SDLK_ESCAPE: | |
295 if (activeTracker_) | |
296 { | |
297 activeTracker_->Cancel(); | |
298 if (!activeTracker_->IsAlive()) | |
299 activeTracker_.reset(); | |
300 } | |
301 break; | |
302 | |
303 case SDLK_r: | |
304 UpdateLayers(); | |
305 { | |
306 std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); | |
307 lock->Invalidate(); | |
308 } | |
309 break; | |
310 | |
311 case SDLK_s: | |
312 compositor.FitContent(scene); | |
313 break; | |
314 | |
315 case SDLK_t: | |
316 if (!activeTracker_) | |
317 SelectNextTool(); | |
318 else | |
319 { | |
320 LOG(WARNING) << "You cannot change the active tool when an interaction" | |
321 " is taking place"; | |
322 } | |
323 break; | |
324 | |
325 case SDLK_z: | |
326 LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; | |
327 if (event.key.keysym.mod & KMOD_CTRL) | |
328 { | |
329 if (controller.CanUndo()) | |
330 { | |
331 LOG(TRACE) << "Undoing..."; | |
332 controller.Undo(); | |
333 } | |
334 else | |
335 { | |
336 LOG(WARNING) << "Nothing to undo!!!"; | |
337 } | |
338 } | |
339 break; | |
340 | |
341 case SDLK_y: | |
342 LOG(TRACE) << "SDLK_y has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; | |
343 if (event.key.keysym.mod & KMOD_CTRL) | |
344 { | |
345 if (controller.CanRedo()) | |
346 { | |
347 LOG(TRACE) << "Redoing..."; | |
348 controller.Redo(); | |
349 } | |
350 else | |
351 { | |
352 LOG(WARNING) << "Nothing to redo!!!"; | |
353 } | |
354 } | |
355 break; | |
356 | |
357 case SDLK_c: | |
358 TakeScreenshot( | |
359 "screenshot.png", | |
360 compositor.GetCanvasWidth(), | |
361 compositor.GetCanvasHeight()); | |
362 break; | |
363 | |
364 default: | |
365 break; | |
366 } | |
367 } | |
368 else if (viewport_->IsRefreshEvent(event)) | |
369 { | |
370 // the viewport has been invalidated and requires repaint | |
371 viewport_->Paint(); | |
372 } | |
373 } | |
374 #endif | |
375 | |
376 #if 0 | |
377 void RtViewerApp::RunSdl(int argc, char* argv[]) | |
378 { | |
379 ProcessOptions(argc, argv); | |
380 | |
381 { | |
382 std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); | |
383 ViewportController& controller = lock->GetController(); | |
384 Scene2D& scene = controller.GetScene(); | |
385 ICompositor& compositor = lock->GetCompositor(); | |
386 | |
387 // False means we do NOT let a hi-DPI aware desktop managedr treat this as a legacy application that requires | |
388 // scaling. | |
389 controller.FitContent(compositor.GetCanvasWidth(), compositor.GetCanvasHeight()); | |
390 | |
391 glEnable(GL_DEBUG_OUTPUT); | |
392 glDebugMessageCallback(OpenGLMessageCallback, 0); | |
393 | |
394 compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, | |
395 FONT_SIZE_0, Orthanc::Encoding_Latin1); | |
396 compositor.SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT, | |
397 FONT_SIZE_1, Orthanc::Encoding_Latin1); | |
398 } | |
399 //////// from loader | |
400 | |
401 loadersContext_.reset(new GenericLoadersContext(1, 4, 1)); | |
402 loadersContext_->StartOracle(); | |
403 | |
404 /** | |
405 It is very important that the Oracle (responsible for network I/O) be started before creating and firing the | |
406 loaders, for any command scheduled by the loader before the oracle is started will be lost. | |
407 */ | |
408 PrepareLoadersAndSlicers(); | |
409 | |
410 bool stopApplication = false; | |
411 | |
412 while (!stopApplication) | |
413 { | |
414 SDL_Event event; | |
415 while (!stopApplication && SDL_PollEvent(&event)) | |
416 { | |
417 if (event.type == SDL_QUIT) | |
418 { | |
419 stopApplication = true; | |
420 break; | |
421 } | |
422 else if (event.type == SDL_WINDOWEVENT && | |
423 event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) | |
424 { | |
425 DisableTracker(); | |
426 } | |
427 else if (event.type == SDL_KEYDOWN && | |
428 event.key.repeat == 0 /* Ignore key bounce */) | |
429 { | |
430 switch (event.key.keysym.sym) | |
431 { | |
432 case SDLK_f: | |
433 // TODO: implement GetWindow to be able to do: | |
434 // viewport_->GetWindow().ToggleMaximize(); | |
435 ORTHANC_ASSERT(false, "Please implement GetWindow()"); | |
436 break; | |
437 case SDLK_q: | |
438 stopApplication = true; | |
439 break; | |
440 default: | |
441 break; | |
442 } | |
443 } | |
444 // the code above is rather application-neutral. | |
445 // the following call handles events specific to the application | |
446 HandleApplicationEvent(event); | |
447 } | |
448 SDL_Delay(1); | |
449 } | |
450 loadersContext_->StopOracle(); | |
451 } | |
452 #endif | |
453 | |
454 #if 0 | |
455 boost::shared_ptr<IFlexiblePointerTracker> RtViewerApp::CreateSuitableTracker( | |
456 const SDL_Event& event, | |
457 const PointerEvent& e) | |
458 { | |
459 std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); | |
460 ViewportController& controller = lock->GetController(); | |
461 Scene2D& scene = controller.GetScene(); | |
462 ICompositor& compositor = lock->GetCompositor(); | |
463 | |
464 using namespace Orthanc; | |
465 | |
466 switch (event.button.button) | |
467 { | |
468 case SDL_BUTTON_MIDDLE: | |
469 return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker | |
470 (viewport_, e)); | |
471 | |
472 case SDL_BUTTON_RIGHT: | |
473 return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker | |
474 (viewport_, e, compositor.GetCanvasHeight())); | |
475 | |
476 case SDL_BUTTON_LEFT: | |
477 { | |
478 //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:"; | |
479 // TODO: we need to iterate on the set of measuring tool and perform | |
480 // a hit test to check if a tracker needs to be created for edition. | |
481 // Otherwise, depending upon the active tool, we might want to create | |
482 // a "measuring tool creation" tracker | |
483 | |
484 // TODO: if there are conflicts, we should prefer a tracker that | |
485 // pertains to the type of measuring tool currently selected (TBD?) | |
486 boost::shared_ptr<IFlexiblePointerTracker> hitTestTracker = TrackerHitTest(e); | |
487 | |
488 if (hitTestTracker != NULL) | |
489 { | |
490 //LOG(TRACE) << "hitTestTracker != NULL"; | |
491 return hitTestTracker; | |
492 } | |
493 else | |
494 { | |
495 switch (currentTool_) | |
496 { | |
497 case RtViewerGuiTool_Rotate: | |
498 //LOG(TRACE) << "Creating RotateSceneTracker"; | |
499 return boost::shared_ptr<IFlexiblePointerTracker>(new RotateSceneTracker(viewport_, e)); | |
500 case RtViewerGuiTool_Pan: | |
501 return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker(viewport_, e)); | |
502 case RtViewerGuiTool_Zoom: | |
503 return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker(viewport_, e, compositor.GetCanvasHeight())); | |
504 //case GuiTool_AngleMeasure: | |
505 // return new AngleMeasureTracker(GetScene(), e); | |
506 //case GuiTool_CircleMeasure: | |
507 // return new CircleMeasureTracker(GetScene(), e); | |
508 //case GuiTool_EllipseMeasure: | |
509 // return new EllipseMeasureTracker(GetScene(), e); | |
510 case RtViewerGuiTool_LineMeasure: | |
511 return boost::shared_ptr<IFlexiblePointerTracker>(new CreateLineMeasureTracker(viewport_, e)); | |
512 case RtViewerGuiTool_AngleMeasure: | |
513 return boost::shared_ptr<IFlexiblePointerTracker>(new CreateAngleMeasureTracker(viewport_, e)); | |
514 case RtViewerGuiTool_CircleMeasure: | |
515 LOG(ERROR) << "Not implemented yet!"; | |
516 return boost::shared_ptr<IFlexiblePointerTracker>(); | |
517 case RtViewerGuiTool_EllipseMeasure: | |
518 LOG(ERROR) << "Not implemented yet!"; | |
519 return boost::shared_ptr<IFlexiblePointerTracker>(); | |
520 default: | |
521 throw OrthancException(ErrorCode_InternalError, "Wrong tool!"); | |
522 } | |
523 } | |
524 } | |
525 default: | |
526 return boost::shared_ptr<IFlexiblePointerTracker>(); | |
527 } | |
528 } | |
529 #endif | |
530 | |
531 } | 219 } |
532 | 220 |
533 boost::weak_ptr<OrthancStone::RtViewerApp> g_app; | 221 boost::weak_ptr<OrthancStone::RtViewerApp> g_app; |
534 | 222 |
535 /** | 223 /** |