comparison Framework/Widgets/WorldSceneWidget.cpp @ 0:351ab0da0150

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