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