comparison Applications/Samples/SingleVolumeApplication.h @ 51:b340879da9bd

reorganization
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 27 Apr 2017 14:50:20 +0200
parents Samples/SingleVolumeApplication.h@28956ed68280
children fcec0ab44054 4cff7b1ed31d
comparison
equal deleted inserted replaced
49:c45f368de5c0 51:b340879da9bd
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 Osimis, 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 #pragma once
23
24 #include "SampleInteractor.h"
25
26 #include "../../Resources/Orthanc/Core/Toolbox.h"
27 #include "../../Framework/Layers/LineMeasureTracker.h"
28 #include "../../Framework/Layers/CircleMeasureTracker.h"
29 #include "../../Resources/Orthanc/Core/Logging.h"
30
31 namespace OrthancStone
32 {
33 namespace Samples
34 {
35 class SingleVolumeApplication : public SampleApplicationBase
36 {
37 private:
38 class Interactor : public SampleInteractor
39 {
40 private:
41 enum MouseMode
42 {
43 MouseMode_None,
44 MouseMode_TrackCoordinates,
45 MouseMode_LineMeasure,
46 MouseMode_CircleMeasure
47 };
48
49 MouseMode mouseMode_;
50
51 void SetMouseMode(MouseMode mode,
52 IStatusBar* statusBar)
53 {
54 if (mouseMode_ == mode)
55 {
56 mouseMode_ = MouseMode_None;
57 }
58 else
59 {
60 mouseMode_ = mode;
61 }
62
63 if (statusBar)
64 {
65 switch (mouseMode_)
66 {
67 case MouseMode_None:
68 statusBar->SetMessage("Disabling the mouse tools");
69 break;
70
71 case MouseMode_TrackCoordinates:
72 statusBar->SetMessage("Tracking the mouse coordinates");
73 break;
74
75 case MouseMode_LineMeasure:
76 statusBar->SetMessage("Mouse clicks will now measure the distances");
77 break;
78
79 case MouseMode_CircleMeasure:
80 statusBar->SetMessage("Mouse clicks will now draw circles");
81 break;
82
83 default:
84 break;
85 }
86 }
87 }
88
89 public:
90 Interactor(VolumeImage& volume,
91 VolumeProjection projection,
92 bool reverse) :
93 SampleInteractor(volume, projection, reverse),
94 mouseMode_(MouseMode_None)
95 {
96 }
97
98 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
99 const SliceGeometry& slice,
100 const ViewportGeometry& view,
101 MouseButton button,
102 double x,
103 double y,
104 IStatusBar* statusBar)
105 {
106 if (button == MouseButton_Left)
107 {
108 switch (mouseMode_)
109 {
110 case MouseMode_LineMeasure:
111 return new LineMeasureTracker(NULL, slice, x, y, 255, 0, 0, 14 /* font size */);
112
113 case MouseMode_CircleMeasure:
114 return new CircleMeasureTracker(NULL, slice, x, y, 255, 0, 0, 14 /* font size */);
115
116 default:
117 break;
118 }
119 }
120
121 return NULL;
122 }
123
124 virtual void MouseOver(CairoContext& context,
125 WorldSceneWidget& widget,
126 const SliceGeometry& slice,
127 const ViewportGeometry& view,
128 double x,
129 double y,
130 IStatusBar* statusBar)
131 {
132 if (mouseMode_ == MouseMode_TrackCoordinates &&
133 statusBar != NULL)
134 {
135 Vector p = slice.MapSliceToWorldCoordinates(x, y);
136
137 char buf[64];
138 sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", p[0] / 10.0, p[1] / 10.0, p[2] / 10.0);
139 statusBar->SetMessage(buf);
140 }
141 }
142
143
144 virtual void KeyPressed(WorldSceneWidget& widget,
145 char key,
146 KeyboardModifiers modifiers,
147 IStatusBar* statusBar)
148 {
149 switch (key)
150 {
151 case 't':
152 SetMouseMode(MouseMode_TrackCoordinates, statusBar);
153 break;
154
155 case 'm':
156 SetMouseMode(MouseMode_LineMeasure, statusBar);
157 break;
158
159 case 'c':
160 SetMouseMode(MouseMode_CircleMeasure, statusBar);
161 break;
162
163 case 'b':
164 {
165 if (statusBar)
166 {
167 statusBar->SetMessage("Setting Hounsfield window to bones");
168 }
169
170 RenderStyle style;
171 style.windowing_ = ImageWindowing_Bone;
172 dynamic_cast<LayeredSceneWidget&>(widget).SetLayerStyle(0, style);
173 break;
174 }
175
176 case 'l':
177 {
178 if (statusBar)
179 {
180 statusBar->SetMessage("Setting Hounsfield window to lung");
181 }
182
183 RenderStyle style;
184 style.windowing_ = ImageWindowing_Lung;
185 dynamic_cast<LayeredSceneWidget&>(widget).SetLayerStyle(0, style);
186 break;
187 }
188
189 case 'd':
190 {
191 if (statusBar)
192 {
193 statusBar->SetMessage("Setting Hounsfield window to what is written in the DICOM file");
194 }
195
196 RenderStyle style;
197 style.windowing_ = ImageWindowing_Default;
198 dynamic_cast<LayeredSceneWidget&>(widget).SetLayerStyle(0, style);
199 break;
200 }
201
202 default:
203 break;
204 }
205 }
206 };
207
208
209 public:
210 virtual void DeclareCommandLineOptions(boost::program_options::options_description& options)
211 {
212 boost::program_options::options_description generic("Sample options");
213 generic.add_options()
214 ("series", boost::program_options::value<std::string>(),
215 "Orthanc ID of the series")
216 ("threads", boost::program_options::value<unsigned int>()->default_value(3),
217 "Number of download threads")
218 ("projection", boost::program_options::value<std::string>()->default_value("axial"),
219 "Projection of interest (can be axial, sagittal or coronal)")
220 ("reverse", boost::program_options::value<bool>()->default_value(false),
221 "Reverse the normal direction of the volume")
222 ;
223
224 options.add(generic);
225 }
226
227 virtual void Initialize(BasicApplicationContext& context,
228 IStatusBar& statusBar,
229 const boost::program_options::variables_map& parameters)
230 {
231 using namespace OrthancStone;
232
233 if (parameters.count("series") != 1)
234 {
235 LOG(ERROR) << "The series ID is missing";
236 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
237 }
238
239 std::string series = parameters["series"].as<std::string>();
240 unsigned int threads = parameters["threads"].as<unsigned int>();
241 bool reverse = parameters["reverse"].as<bool>();
242
243 std::string tmp = parameters["projection"].as<std::string>();
244 Orthanc::Toolbox::ToLowerCase(tmp);
245
246 VolumeProjection projection;
247 if (tmp == "axial")
248 {
249 projection = VolumeProjection_Axial;
250 }
251 else if (tmp == "sagittal")
252 {
253 projection = VolumeProjection_Sagittal;
254 }
255 else if (tmp == "coronal")
256 {
257 projection = VolumeProjection_Coronal;
258 }
259 else
260 {
261 LOG(ERROR) << "Unknown projection: " << tmp;
262 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
263 }
264
265 VolumeImage& volume = context.AddSeriesVolume(series, true /* progressive download */, threads);
266
267 std::auto_ptr<Interactor> interactor(new Interactor(volume, projection, reverse));
268
269 std::auto_ptr<LayeredSceneWidget> widget(new LayeredSceneWidget);
270 widget->AddLayer(new VolumeImage::LayerFactory(volume));
271 widget->SetSlice(interactor->GetCursor().GetCurrentSlice());
272 widget->SetInteractor(*interactor);
273
274 context.AddInteractor(interactor.release());
275 context.SetCentralWidget(widget.release());
276
277 statusBar.SetMessage("Use the keys \"b\", \"l\" and \"d\" to change Hounsfield windowing");
278 statusBar.SetMessage("Use the keys \"t\" to track the (X,Y,Z) mouse coordinates");
279 statusBar.SetMessage("Use the keys \"m\" to measure distances");
280 statusBar.SetMessage("Use the keys \"c\" to draw circles");
281 }
282 };
283 }
284 }