Mercurial > hg > orthanc-stone
annotate Framework/Widgets/WorldSceneWidget.cpp @ 134:4cff7b1ed31d
upgrade to year 2018
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 02 Jan 2018 09:51:36 +0100 |
parents | 28956ed68280 |
children | fccffbf99ba1 |
rev | line source |
---|---|
0 | 1 /** |
2 * Stone of Orthanc | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
134
4cff7b1ed31d
upgrade to year 2018
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
47
diff
changeset
|
5 * Copyright (C) 2017-2018 Osimis S.A., Belgium |
0 | 6 * |
7 * This program is free software: you can redistribute it and/or | |
47 | 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. | |
0 | 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 | |
47 | 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 | |
0 | 18 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 **/ | |
20 | |
21 | |
22 #include "WorldSceneWidget.h" | |
23 | |
16 | 24 #include "../../Resources/Orthanc/Core/OrthancException.h" |
0 | 25 |
26 namespace OrthancStone | |
27 { | |
28 static void MapMouseToScene(double& sceneX, | |
29 double& sceneY, | |
30 const ViewportGeometry& view, | |
31 int mouseX, | |
32 int mouseY) | |
33 { | |
34 // Take the center of the pixel | |
35 double x, y; | |
36 x = static_cast<double>(mouseX) + 0.5; | |
37 y = static_cast<double>(mouseY) + 0.5; | |
38 | |
39 view.MapDisplayToScene(sceneX, sceneY, x, y); | |
40 } | |
41 | |
42 | |
43 struct WorldSceneWidget::ViewChangeFunctor | |
44 { | |
45 const ViewportGeometry& view_; | |
46 | |
47 ViewChangeFunctor(const ViewportGeometry& view) : | |
48 view_(view) | |
49 { | |
50 } | |
51 | |
52 void operator() (IWorldObserver& observer, | |
53 const WorldSceneWidget& source) | |
54 { | |
55 observer.NotifyViewChange(source, view_); | |
56 } | |
57 }; | |
58 | |
59 | |
60 struct WorldSceneWidget::SizeChangeFunctor | |
61 { | |
62 ViewportGeometry& view_; | |
63 | |
64 SizeChangeFunctor(ViewportGeometry& view) : | |
65 view_(view) | |
66 { | |
67 } | |
68 | |
69 void operator() (IWorldObserver& observer, | |
70 const WorldSceneWidget& source) | |
71 { | |
72 observer.NotifySizeChange(source, view_); | |
73 } | |
74 }; | |
75 | |
76 | |
77 class WorldSceneWidget::SceneMouseTracker : public IMouseTracker | |
78 { | |
79 private: | |
80 ViewportGeometry view_; | |
81 std::auto_ptr<IWorldSceneMouseTracker> tracker_; | |
82 | |
83 public: | |
84 SceneMouseTracker(const ViewportGeometry& view, | |
85 IWorldSceneMouseTracker* tracker) : | |
86 view_(view), | |
87 tracker_(tracker) | |
88 { | |
89 assert(tracker != NULL); | |
90 } | |
91 | |
92 virtual void Render(Orthanc::ImageAccessor& target) | |
93 { | |
94 CairoSurface surface(target); | |
95 CairoContext context(surface); | |
96 view_.ApplyTransform(context); | |
97 tracker_->Render(context, view_.GetZoom()); | |
98 } | |
99 | |
100 virtual void MouseUp() | |
101 { | |
102 tracker_->MouseUp(); | |
103 } | |
104 | |
105 virtual void MouseMove(int x, | |
106 int y) | |
107 { | |
108 double sceneX, sceneY; | |
109 MapMouseToScene(sceneX, sceneY, view_, x, y); | |
110 tracker_->MouseMove(sceneX, sceneY); | |
111 } | |
112 }; | |
113 | |
114 | |
115 class WorldSceneWidget::PanMouseTracker : public IMouseTracker | |
116 { | |
117 private: | |
118 WorldSceneWidget& that_; | |
119 double previousPanX_; | |
120 double previousPanY_; | |
121 double downX_; | |
122 double downY_; | |
123 | |
124 public: | |
125 PanMouseTracker(WorldSceneWidget& that, | |
126 int x, | |
127 int y) : | |
128 that_(that), | |
129 downX_(x), | |
130 downY_(y) | |
131 { | |
132 SharedValue<ViewportGeometry>::Locker locker(that_.view_); | |
133 locker.GetValue().GetPan(previousPanX_, previousPanY_); | |
134 } | |
135 | |
136 virtual void Render(Orthanc::ImageAccessor& surface) | |
137 { | |
138 } | |
139 | |
140 virtual void MouseUp() | |
141 { | |
142 } | |
143 | |
144 virtual void MouseMove(int x, | |
145 int y) | |
146 { | |
147 SharedValue<ViewportGeometry>::Locker locker(that_.view_); | |
148 locker.GetValue().SetPan(previousPanX_ + x - downX_, | |
149 previousPanY_ + y - downY_); | |
150 | |
151 ViewChangeFunctor functor(locker.GetValue()); | |
152 that_.observers_.Notify(&that_, functor); | |
153 } | |
154 }; | |
155 | |
156 | |
157 class WorldSceneWidget::ZoomMouseTracker : public IMouseTracker | |
158 { | |
159 private: | |
160 WorldSceneWidget& that_; | |
161 int downX_; | |
162 int downY_; | |
163 double centerX_; | |
164 double centerY_; | |
165 double oldZoom_; | |
166 | |
167 public: | |
168 ZoomMouseTracker(WorldSceneWidget& that, | |
169 int x, | |
170 int y) : | |
171 that_(that), | |
172 downX_(x), | |
173 downY_(y) | |
174 { | |
175 SharedValue<ViewportGeometry>::Locker locker(that_.view_); | |
176 oldZoom_ = locker.GetValue().GetZoom(); | |
177 MapMouseToScene(centerX_, centerY_, locker.GetValue(), downX_, downY_); | |
178 } | |
179 | |
180 virtual void Render(Orthanc::ImageAccessor& surface) | |
181 { | |
182 } | |
183 | |
184 virtual void MouseUp() | |
185 { | |
186 } | |
187 | |
188 virtual void MouseMove(int x, | |
189 int y) | |
190 { | |
191 static const double MIN_ZOOM = -4; | |
192 static const double MAX_ZOOM = 4; | |
193 | |
194 SharedValue<ViewportGeometry>::Locker locker(that_.view_); | |
195 | |
196 if (locker.GetValue().GetDisplayHeight() <= 3) | |
197 { | |
198 return; // Cannot zoom on such a small image | |
199 } | |
200 | |
201 double dy = (static_cast<double>(y - downY_) / | |
202 static_cast<double>(locker.GetValue().GetDisplayHeight() - 1)); // In the range [-1,1] | |
203 double z; | |
204 | |
205 // Linear interpolation from [-1, 1] to [MIN_ZOOM, MAX_ZOOM] | |
206 if (dy < -1.0) | |
207 { | |
208 z = MIN_ZOOM; | |
209 } | |
210 else if (dy > 1.0) | |
211 { | |
212 z = MAX_ZOOM; | |
213 } | |
214 else | |
215 { | |
216 z = MIN_ZOOM + (MAX_ZOOM - MIN_ZOOM) * (dy + 1.0) / 2.0; | |
217 } | |
218 | |
219 z = pow(2.0, z); | |
220 | |
221 locker.GetValue().SetZoom(oldZoom_ * z); | |
222 | |
223 // Correct the pan so that the original click point is kept at | |
224 // the same location on the display | |
225 double panX, panY; | |
226 locker.GetValue().GetPan(panX, panY); | |
227 | |
228 int tx, ty; | |
229 locker.GetValue().MapSceneToDisplay(tx, ty, centerX_, centerY_); | |
230 locker.GetValue().SetPan(panX + static_cast<double>(downX_ - tx), | |
231 panY + static_cast<double>(downY_ - ty)); | |
232 | |
233 ViewChangeFunctor functor(locker.GetValue()); | |
234 that_.observers_.Notify(&that_, functor); | |
235 } | |
236 }; | |
237 | |
238 | |
239 void WorldSceneWidget::UpdateStep() | |
240 { | |
241 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
242 } | |
243 | |
244 | |
245 bool WorldSceneWidget::RenderCairo(CairoContext& context) | |
246 { | |
247 ViewportGeometry view; | |
248 | |
249 { | |
250 SharedValue<ViewportGeometry>::Locker locker(view_); | |
251 view = locker.GetValue(); | |
252 } | |
253 | |
254 view.ApplyTransform(context); | |
255 | |
256 return RenderScene(context, view); | |
257 } | |
258 | |
259 | |
260 void WorldSceneWidget::RenderMouseOverCairo(CairoContext& context, | |
261 int x, | |
262 int y) | |
263 { | |
264 ViewportGeometry view = GetView(); | |
265 view.ApplyTransform(context); | |
266 | |
267 double sceneX, sceneY; | |
268 MapMouseToScene(sceneX, sceneY, view, x, y); | |
269 RenderSceneMouseOver(context, view, sceneX, sceneY); | |
270 } | |
271 | |
272 | |
273 void WorldSceneWidget::SetSceneExtent(SharedValue<ViewportGeometry>::Locker& locker) | |
274 { | |
275 double x1, y1, x2, y2; | |
276 GetSceneExtent(x1, y1, x2, y2); | |
277 locker.GetValue().SetSceneExtent(x1, y1, x2, y2); | |
278 } | |
279 | |
280 | |
281 void WorldSceneWidget::SetSize(unsigned int width, | |
282 unsigned int height) | |
283 { | |
284 CairoWidget::SetSize(width, height); | |
285 | |
286 { | |
287 SharedValue<ViewportGeometry>::Locker locker(view_); | |
288 locker.GetValue().SetDisplaySize(width, height); | |
289 | |
290 if (observers_.IsEmpty()) | |
291 { | |
292 // Without a size observer, use the default view | |
293 locker.GetValue().SetDefaultView(); | |
294 } | |
295 else | |
296 { | |
297 // With a size observer, let it decide which view to use | |
298 SizeChangeFunctor functor(locker.GetValue()); | |
299 observers_.Notify(this, functor); | |
300 } | |
301 } | |
302 } | |
303 | |
304 | |
305 void WorldSceneWidget::SetInteractor(IWorldSceneInteractor& interactor) | |
306 { | |
307 if (IsStarted()) | |
308 { | |
309 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
310 } | |
311 | |
312 interactor_ = &interactor; | |
313 } | |
314 | |
315 | |
316 void WorldSceneWidget::Start() | |
317 { | |
318 ViewportGeometry geometry; | |
319 | |
320 { | |
321 SharedValue<ViewportGeometry>::Locker locker(view_); | |
322 SetSceneExtent(locker); | |
323 geometry = locker.GetValue(); | |
324 } | |
325 | |
326 WidgetBase::Start(); | |
327 | |
328 ViewChangeFunctor functor(geometry); | |
329 observers_.Notify(this, functor); | |
330 } | |
331 | |
332 | |
333 void WorldSceneWidget::SetDefaultView() | |
334 { | |
335 ViewportGeometry geometry; | |
336 | |
337 { | |
338 SharedValue<ViewportGeometry>::Locker locker(view_); | |
339 SetSceneExtent(locker); | |
340 locker.GetValue().SetDefaultView(); | |
341 geometry = locker.GetValue(); | |
342 } | |
343 | |
344 NotifyChange(); | |
345 | |
346 ViewChangeFunctor functor(geometry); | |
347 observers_.Notify(this, functor); | |
348 } | |
349 | |
350 | |
351 void WorldSceneWidget::SetView(const ViewportGeometry& view) | |
352 { | |
353 { | |
354 SharedValue<ViewportGeometry>::Locker locker(view_); | |
355 locker.GetValue() = view; | |
356 } | |
357 | |
358 NotifyChange(); | |
359 | |
360 ViewChangeFunctor functor(view); | |
361 observers_.Notify(this, functor); | |
362 } | |
363 | |
364 | |
365 ViewportGeometry WorldSceneWidget::GetView() | |
366 { | |
367 SharedValue<ViewportGeometry>::Locker locker(view_); | |
368 return locker.GetValue(); | |
369 } | |
370 | |
371 | |
372 IMouseTracker* WorldSceneWidget::CreateMouseTracker(MouseButton button, | |
373 int x, | |
374 int y, | |
375 KeyboardModifiers modifiers) | |
376 { | |
377 ViewportGeometry view = GetView(); | |
378 | |
379 double sceneX, sceneY; | |
380 MapMouseToScene(sceneX, sceneY, view, x, y); | |
381 | |
382 std::auto_ptr<IWorldSceneMouseTracker> tracker(CreateMouseSceneTracker(view, button, sceneX, sceneY, modifiers)); | |
383 if (tracker.get() != NULL) | |
384 { | |
385 return new SceneMouseTracker(view, tracker.release()); | |
386 } | |
387 | |
388 switch (button) | |
389 { | |
390 case MouseButton_Middle: | |
391 return new PanMouseTracker(*this, x, y); | |
392 | |
393 case MouseButton_Right: | |
394 return new ZoomMouseTracker(*this, x, y); | |
395 | |
396 default: | |
397 return NULL; | |
398 } | |
399 } | |
400 | |
401 | |
402 void WorldSceneWidget::RenderSceneMouseOver(CairoContext& context, | |
403 const ViewportGeometry& view, | |
404 double x, | |
405 double y) | |
406 { | |
407 if (interactor_) | |
408 { | |
409 interactor_->MouseOver(context, *this, GetSlice(), view, x, y, GetStatusBar()); | |
410 } | |
411 } | |
412 | |
413 IWorldSceneMouseTracker* WorldSceneWidget::CreateMouseSceneTracker(const ViewportGeometry& view, | |
414 MouseButton button, | |
415 double x, | |
416 double y, | |
417 KeyboardModifiers modifiers) | |
418 { | |
419 if (interactor_) | |
420 { | |
421 return interactor_->CreateMouseTracker(*this, GetSlice(), view, button, x, y, GetStatusBar()); | |
422 } | |
423 else | |
424 { | |
425 return NULL; | |
426 } | |
427 } | |
428 | |
429 | |
430 void WorldSceneWidget::MouseWheel(MouseWheelDirection direction, | |
431 int x, | |
432 int y, | |
433 KeyboardModifiers modifiers) | |
434 { | |
435 if (interactor_) | |
436 { | |
437 interactor_->MouseWheel(*this, direction, modifiers, GetStatusBar()); | |
438 } | |
439 } | |
440 | |
441 | |
442 void WorldSceneWidget::KeyPressed(char key, | |
443 KeyboardModifiers modifiers) | |
444 { | |
445 if (interactor_) | |
446 { | |
447 interactor_->KeyPressed(*this, key, modifiers, GetStatusBar()); | |
448 } | |
449 } | |
450 } |