Mercurial > hg > orthanc-stone
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 } |