Mercurial > hg > orthanc-stone
comparison Applications/Samples/SingleVolumeApplication.h @ 104:eccd64f8e297 wasm
VolumeImageInteractor
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 01 Jun 2017 10:33:49 +0200 |
parents | 474d85e76499 |
children | e0ddd8cad909 |
comparison
equal
deleted
inserted
replaced
103:474d85e76499 | 104:eccd64f8e297 |
---|---|
34 namespace OrthancStone | 34 namespace OrthancStone |
35 { | 35 { |
36 namespace Samples | 36 namespace Samples |
37 { | 37 { |
38 class SingleVolumeApplication : | 38 class SingleVolumeApplication : |
39 public SampleApplicationBase, | 39 public SampleApplicationBase |
40 private ILayerSource::IObserver | |
41 { | 40 { |
42 private: | |
43 class Interactor : public IWorldSceneInteractor | |
44 { | |
45 private: | |
46 SingleVolumeApplication& application_; | |
47 | |
48 public: | |
49 Interactor(SingleVolumeApplication& application) : | |
50 application_(application) | |
51 { | |
52 } | |
53 | |
54 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, | |
55 const ViewportGeometry& view, | |
56 MouseButton button, | |
57 double x, | |
58 double y, | |
59 IStatusBar* statusBar) | |
60 { | |
61 return NULL; | |
62 } | |
63 | |
64 virtual void MouseOver(CairoContext& context, | |
65 WorldSceneWidget& widget, | |
66 const ViewportGeometry& view, | |
67 double x, | |
68 double y, | |
69 IStatusBar* statusBar) | |
70 { | |
71 if (statusBar != NULL) | |
72 { | |
73 Vector p = dynamic_cast<LayerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y); | |
74 | |
75 char buf[64]; | |
76 sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", | |
77 p[0] / 10.0, p[1] / 10.0, p[2] / 10.0); | |
78 statusBar->SetMessage(buf); | |
79 } | |
80 } | |
81 | |
82 virtual void MouseWheel(WorldSceneWidget& widget, | |
83 MouseWheelDirection direction, | |
84 KeyboardModifiers modifiers, | |
85 IStatusBar* statusBar) | |
86 { | |
87 int scale = (modifiers & KeyboardModifiers_Control ? 10 : 1); | |
88 | |
89 switch (direction) | |
90 { | |
91 case MouseWheelDirection_Up: | |
92 application_.OffsetSlice(-scale); | |
93 break; | |
94 | |
95 case MouseWheelDirection_Down: | |
96 application_.OffsetSlice(scale); | |
97 break; | |
98 | |
99 default: | |
100 break; | |
101 } | |
102 } | |
103 | |
104 virtual void KeyPressed(WorldSceneWidget& widget, | |
105 char key, | |
106 KeyboardModifiers modifiers, | |
107 IStatusBar* statusBar) | |
108 { | |
109 switch (key) | |
110 { | |
111 case 's': | |
112 widget.SetDefaultView(); | |
113 break; | |
114 | |
115 default: | |
116 break; | |
117 } | |
118 } | |
119 }; | |
120 | |
121 | |
122 LayerWidget* widget_; | |
123 const OrthancVolumeImage* volume_; | |
124 VolumeProjection projection_; | |
125 std::auto_ptr<VolumeImageGeometry> slices_; | |
126 size_t slice_; | |
127 | |
128 void OffsetSlice(int offset) | |
129 { | |
130 if (slices_.get() != NULL) | |
131 { | |
132 int slice = static_cast<int>(slice_) + offset; | |
133 | |
134 if (slice < 0) | |
135 { | |
136 slice = 0; | |
137 } | |
138 | |
139 if (slice >= static_cast<int>(slices_->GetSliceCount())) | |
140 { | |
141 slice = slices_->GetSliceCount() - 1; | |
142 } | |
143 | |
144 if (slice != static_cast<int>(slice_)) | |
145 { | |
146 SetSlice(slice); | |
147 } | |
148 } | |
149 } | |
150 | |
151 void SetSlice(size_t slice) | |
152 { | |
153 if (slices_.get() != NULL) | |
154 { | |
155 slice_ = slice; | |
156 widget_->SetSlice(slices_->GetSlice(slice_).GetGeometry()); | |
157 } | |
158 } | |
159 | |
160 virtual void NotifyGeometryReady(const ILayerSource& source) | |
161 { | |
162 if (slices_.get() == NULL) | |
163 { | |
164 slices_.reset(new VolumeImageGeometry(*volume_, projection_)); | |
165 SetSlice(slices_->GetSliceCount() / 2); | |
166 | |
167 widget_->SetDefaultView(); | |
168 } | |
169 } | |
170 | |
171 virtual void NotifyGeometryError(const ILayerSource& source) | |
172 { | |
173 } | |
174 | |
175 virtual void NotifyContentChange(const ILayerSource& source) | |
176 { | |
177 } | |
178 | |
179 virtual void NotifySliceChange(const ILayerSource& source, | |
180 const Slice& slice) | |
181 { | |
182 } | |
183 | |
184 virtual void NotifyLayerReady(std::auto_ptr<ILayerRenderer>& layer, | |
185 const ILayerSource& source, | |
186 const Slice& slice, | |
187 bool isError) | |
188 { | |
189 } | |
190 | |
191 #if 0 | |
192 class Interactor : public SampleInteractor | |
193 { | |
194 private: | |
195 enum MouseMode | |
196 { | |
197 MouseMode_None, | |
198 MouseMode_TrackCoordinates, | |
199 MouseMode_LineMeasure, | |
200 MouseMode_CircleMeasure | |
201 }; | |
202 | |
203 MouseMode mouseMode_; | |
204 | |
205 void SetMouseMode(MouseMode mode, | |
206 IStatusBar* statusBar) | |
207 { | |
208 if (mouseMode_ == mode) | |
209 { | |
210 mouseMode_ = MouseMode_None; | |
211 } | |
212 else | |
213 { | |
214 mouseMode_ = mode; | |
215 } | |
216 | |
217 if (statusBar) | |
218 { | |
219 switch (mouseMode_) | |
220 { | |
221 case MouseMode_None: | |
222 statusBar->SetMessage("Disabling the mouse tools"); | |
223 break; | |
224 | |
225 case MouseMode_TrackCoordinates: | |
226 statusBar->SetMessage("Tracking the mouse coordinates"); | |
227 break; | |
228 | |
229 case MouseMode_LineMeasure: | |
230 statusBar->SetMessage("Mouse clicks will now measure the distances"); | |
231 break; | |
232 | |
233 case MouseMode_CircleMeasure: | |
234 statusBar->SetMessage("Mouse clicks will now draw circles"); | |
235 break; | |
236 | |
237 default: | |
238 break; | |
239 } | |
240 } | |
241 } | |
242 | |
243 public: | |
244 Interactor(VolumeImage& volume, | |
245 VolumeProjection projection, | |
246 bool reverse) : | |
247 SampleInteractor(volume, projection, reverse), | |
248 mouseMode_(MouseMode_None) | |
249 { | |
250 } | |
251 | |
252 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, | |
253 const SliceGeometry& slice, | |
254 const ViewportGeometry& view, | |
255 MouseButton button, | |
256 double x, | |
257 double y, | |
258 IStatusBar* statusBar) | |
259 { | |
260 if (button == MouseButton_Left) | |
261 { | |
262 switch (mouseMode_) | |
263 { | |
264 case MouseMode_LineMeasure: | |
265 return new LineMeasureTracker(NULL, slice, x, y, 255, 0, 0, 14 /* font size */); | |
266 | |
267 case MouseMode_CircleMeasure: | |
268 return new CircleMeasureTracker(NULL, slice, x, y, 255, 0, 0, 14 /* font size */); | |
269 | |
270 default: | |
271 break; | |
272 } | |
273 } | |
274 | |
275 return NULL; | |
276 } | |
277 | |
278 virtual void MouseOver(CairoContext& context, | |
279 WorldSceneWidget& widget, | |
280 const SliceGeometry& slice, | |
281 const ViewportGeometry& view, | |
282 double x, | |
283 double y, | |
284 IStatusBar* statusBar) | |
285 { | |
286 if (mouseMode_ == MouseMode_TrackCoordinates && | |
287 statusBar != NULL) | |
288 { | |
289 Vector p = slice.MapSliceToWorldCoordinates(x, y); | |
290 | |
291 char buf[64]; | |
292 sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", p[0] / 10.0, p[1] / 10.0, p[2] / 10.0); | |
293 statusBar->SetMessage(buf); | |
294 } | |
295 } | |
296 | |
297 | |
298 virtual void KeyPressed(WorldSceneWidget& widget, | |
299 char key, | |
300 KeyboardModifiers modifiers, | |
301 IStatusBar* statusBar) | |
302 { | |
303 switch (key) | |
304 { | |
305 case 't': | |
306 SetMouseMode(MouseMode_TrackCoordinates, statusBar); | |
307 break; | |
308 | |
309 case 'm': | |
310 SetMouseMode(MouseMode_LineMeasure, statusBar); | |
311 break; | |
312 | |
313 case 'c': | |
314 SetMouseMode(MouseMode_CircleMeasure, statusBar); | |
315 break; | |
316 | |
317 case 'b': | |
318 { | |
319 if (statusBar) | |
320 { | |
321 statusBar->SetMessage("Setting Hounsfield window to bones"); | |
322 } | |
323 | |
324 RenderStyle style; | |
325 style.windowing_ = ImageWindowing_Bone; | |
326 dynamic_cast<LayeredSceneWidget&>(widget).SetLayerStyle(0, style); | |
327 break; | |
328 } | |
329 | |
330 case 'l': | |
331 { | |
332 if (statusBar) | |
333 { | |
334 statusBar->SetMessage("Setting Hounsfield window to lung"); | |
335 } | |
336 | |
337 RenderStyle style; | |
338 style.windowing_ = ImageWindowing_Lung; | |
339 dynamic_cast<LayeredSceneWidget&>(widget).SetLayerStyle(0, style); | |
340 break; | |
341 } | |
342 | |
343 case 'd': | |
344 { | |
345 if (statusBar) | |
346 { | |
347 statusBar->SetMessage("Setting Hounsfield window to what is written in the DICOM file"); | |
348 } | |
349 | |
350 RenderStyle style; | |
351 style.windowing_ = ImageWindowing_Default; | |
352 dynamic_cast<LayeredSceneWidget&>(widget).SetLayerStyle(0, style); | |
353 break; | |
354 } | |
355 | |
356 default: | |
357 break; | |
358 } | |
359 } | |
360 }; | |
361 #endif | |
362 | |
363 | |
364 public: | 41 public: |
365 SingleVolumeApplication() : | |
366 widget_(NULL), | |
367 volume_(NULL) | |
368 { | |
369 } | |
370 | |
371 virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) | 42 virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) |
372 { | 43 { |
373 boost::program_options::options_description generic("Sample options"); | 44 boost::program_options::options_description generic("Sample options"); |
374 generic.add_options() | 45 generic.add_options() |
375 ("series", boost::program_options::value<std::string>(), | 46 ("series", boost::program_options::value<std::string>(), |
401 unsigned int threads = parameters["threads"].as<unsigned int>(); | 72 unsigned int threads = parameters["threads"].as<unsigned int>(); |
402 bool reverse = parameters["reverse"].as<bool>(); | 73 bool reverse = parameters["reverse"].as<bool>(); |
403 | 74 |
404 std::string tmp = parameters["projection"].as<std::string>(); | 75 std::string tmp = parameters["projection"].as<std::string>(); |
405 Orthanc::Toolbox::ToLowerCase(tmp); | 76 Orthanc::Toolbox::ToLowerCase(tmp); |
406 | 77 |
78 VolumeProjection projection; | |
407 if (tmp == "axial") | 79 if (tmp == "axial") |
408 { | 80 { |
409 projection_ = VolumeProjection_Axial; | 81 projection = VolumeProjection_Axial; |
410 } | 82 } |
411 else if (tmp == "sagittal") | 83 else if (tmp == "sagittal") |
412 { | 84 { |
413 projection_ = VolumeProjection_Sagittal; | 85 projection = VolumeProjection_Sagittal; |
414 } | 86 } |
415 else if (tmp == "coronal") | 87 else if (tmp == "coronal") |
416 { | 88 { |
417 projection_ = VolumeProjection_Coronal; | 89 projection = VolumeProjection_Coronal; |
418 } | 90 } |
419 else | 91 else |
420 { | 92 { |
421 LOG(ERROR) << "Unknown projection: " << tmp; | 93 LOG(ERROR) << "Unknown projection: " << tmp; |
422 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | 94 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); |
423 } | 95 } |
424 | 96 |
425 std::auto_ptr<LayerWidget> widget(new LayerWidget); | 97 std::auto_ptr<LayerWidget> widget(new LayerWidget); |
426 widget_ = widget.get(); | |
427 | 98 |
428 #if 0 | 99 #if 1 |
429 std::auto_ptr<OrthancVolumeImage> volume(new OrthancVolumeImage(context.GetWebService())); | 100 std::auto_ptr<OrthancVolumeImage> volume(new OrthancVolumeImage(context.GetWebService())); |
430 volume->ScheduleLoadSeries(series); | 101 volume->ScheduleLoadSeries(series); |
431 | 102 |
432 volume_ = volume.get(); | |
433 | |
434 { | 103 { |
435 std::auto_ptr<VolumeImageSource> source(new VolumeImageSource(*volume)); | 104 std::auto_ptr<VolumeImageSource> source(new VolumeImageSource(*volume)); |
436 source->Register(*this); | |
437 widget->AddLayer(source.release()); | 105 widget->AddLayer(source.release()); |
438 } | 106 } |
439 | 107 |
108 context.AddInteractor(new VolumeImageInteractor(*volume, *widget, projection)); | |
440 context.AddVolume(volume.release()); | 109 context.AddVolume(volume.release()); |
441 #else | 110 #else |
442 std::auto_ptr<OrthancVolumeImage> ct(new OrthancVolumeImage(context.GetWebService())); | 111 std::auto_ptr<OrthancVolumeImage> ct(new OrthancVolumeImage(context.GetWebService())); |
443 ct->ScheduleLoadSeries("dd069910-4f090474-7d2bba07-e5c10783-f9e4fb1d"); | 112 ct->ScheduleLoadSeries("dd069910-4f090474-7d2bba07-e5c10783-f9e4fb1d"); |
444 | 113 |
445 std::auto_ptr<OrthancVolumeImage> pet(new OrthancVolumeImage(context.GetWebService())); | 114 std::auto_ptr<OrthancVolumeImage> pet(new OrthancVolumeImage(context.GetWebService())); |
446 pet->ScheduleLoadSeries("aabad2e7-80702b5d-e599d26c-4f13398e-38d58a9e"); | 115 pet->ScheduleLoadSeries("aabad2e7-80702b5d-e599d26c-4f13398e-38d58a9e"); |
447 | 116 |
448 volume_ = pet.get(); | 117 context.AddInteractor(new VolumeImageInteractor(*pet, *widget, projection)); |
449 | 118 |
450 { | 119 { |
451 std::auto_ptr<VolumeImageSource> source(new VolumeImageSource(*ct)); | 120 std::auto_ptr<VolumeImageSource> source(new VolumeImageSource(*ct)); |
452 //source->Register(*this); | |
453 widget->AddLayer(source.release()); | 121 widget->AddLayer(source.release()); |
454 } | 122 } |
455 | 123 |
456 { | 124 { |
457 std::auto_ptr<VolumeImageSource> source(new VolumeImageSource(*pet)); | 125 std::auto_ptr<VolumeImageSource> source(new VolumeImageSource(*pet)); |
458 source->Register(*this); | |
459 widget->AddLayer(source.release()); | 126 widget->AddLayer(source.release()); |
460 } | 127 } |
461 | 128 |
462 context.AddVolume(ct.release()); | 129 context.AddVolume(ct.release()); |
463 context.AddVolume(pet.release()); | 130 context.AddVolume(pet.release()); |
464 | 131 |
465 { | 132 { |
466 RenderStyle s; | 133 RenderStyle s; |
467 //s.drawGrid_ = true; | 134 //s.drawGrid_ = true; |
468 s.alpha_ = 1; | 135 s.alpha_ = 1; |
136 s.windowing_ = ImageWindowing_Bone; | |
469 widget->SetLayerStyle(0, s); | 137 widget->SetLayerStyle(0, s); |
470 } | 138 } |
471 | 139 |
472 { | 140 { |
473 RenderStyle s; | 141 RenderStyle s; |
474 //s.drawGrid_ = true; | 142 //s.drawGrid_ = true; |
475 s.SetColor(255, 0, 0); // Draw missing PET layer in red | 143 s.SetColor(255, 0, 0); // Draw missing PET layer in red |
476 s.alpha_ = 0.5; | 144 s.alpha_ = 0.3; |
477 s.applyLut_ = true; | 145 s.applyLut_ = true; |
478 s.lut_ = Orthanc::EmbeddedResources::COLORMAP_JET; | 146 s.lut_ = Orthanc::EmbeddedResources::COLORMAP_JET; |
479 s.interpolation_ = ImageInterpolation_Linear; | 147 s.interpolation_ = ImageInterpolation_Linear; |
480 widget->SetLayerStyle(1, s); | 148 widget->SetLayerStyle(1, s); |
481 } | 149 } |
486 statusBar.SetMessage("Use the keys \"t\" to track the (X,Y,Z) mouse coordinates"); | 154 statusBar.SetMessage("Use the keys \"t\" to track the (X,Y,Z) mouse coordinates"); |
487 statusBar.SetMessage("Use the keys \"m\" to measure distances"); | 155 statusBar.SetMessage("Use the keys \"m\" to measure distances"); |
488 statusBar.SetMessage("Use the keys \"c\" to draw circles"); | 156 statusBar.SetMessage("Use the keys \"c\" to draw circles"); |
489 | 157 |
490 widget->SetTransmitMouseOver(true); | 158 widget->SetTransmitMouseOver(true); |
491 widget->SetInteractor(context.AddInteractor(new Interactor(*this))); | |
492 context.SetCentralWidget(widget.release()); | 159 context.SetCentralWidget(widget.release()); |
493 } | 160 } |
494 }; | 161 }; |
495 } | 162 } |
496 } | 163 } |