Mercurial > hg > orthanc-stone
annotate Framework/Widgets/WorldSceneWidget.cpp @ 50:369fe11606fa wasm
integration mainline->wasm
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 27 Apr 2017 11:09:58 +0200 |
parents | 25befef48c35 |
children | c2dc924f1a63 |
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 | |
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 bool WorldSceneWidget::RenderCairo(CairoContext& context) | |
240 { | |
241 ViewportGeometry view; | |
242 | |
243 { | |
244 SharedValue<ViewportGeometry>::Locker locker(view_); | |
245 view = locker.GetValue(); | |
246 } | |
247 | |
248 view.ApplyTransform(context); | |
249 | |
250 return RenderScene(context, view); | |
251 } | |
252 | |
253 | |
254 void WorldSceneWidget::RenderMouseOverCairo(CairoContext& context, | |
255 int x, | |
256 int y) | |
257 { | |
258 ViewportGeometry view = GetView(); | |
259 view.ApplyTransform(context); | |
260 | |
261 double sceneX, sceneY; | |
262 MapMouseToScene(sceneX, sceneY, view, x, y); | |
263 RenderSceneMouseOver(context, view, sceneX, sceneY); | |
264 } | |
265 | |
266 | |
267 void WorldSceneWidget::SetSceneExtent(SharedValue<ViewportGeometry>::Locker& locker) | |
268 { | |
269 double x1, y1, x2, y2; | |
270 GetSceneExtent(x1, y1, x2, y2); | |
271 locker.GetValue().SetSceneExtent(x1, y1, x2, y2); | |
272 } | |
273 | |
274 | |
275 void WorldSceneWidget::SetSize(unsigned int width, | |
276 unsigned int height) | |
277 { | |
278 CairoWidget::SetSize(width, height); | |
279 | |
280 { | |
281 SharedValue<ViewportGeometry>::Locker locker(view_); | |
282 locker.GetValue().SetDisplaySize(width, height); | |
283 | |
284 if (observers_.IsEmpty()) | |
285 { | |
286 // Without a size observer, use the default view | |
287 locker.GetValue().SetDefaultView(); | |
288 } | |
289 else | |
290 { | |
291 // With a size observer, let it decide which view to use | |
292 SizeChangeFunctor functor(locker.GetValue()); | |
293 observers_.Notify(this, functor); | |
294 } | |
295 } | |
296 } | |
297 | |
298 | |
299 void WorldSceneWidget::SetInteractor(IWorldSceneInteractor& interactor) | |
300 { | |
301 if (IsStarted()) | |
302 { | |
303 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
304 } | |
305 | |
306 interactor_ = &interactor; | |
307 } | |
308 | |
309 | |
310 void WorldSceneWidget::Start() | |
311 { | |
312 ViewportGeometry geometry; | |
313 | |
314 { | |
315 SharedValue<ViewportGeometry>::Locker locker(view_); | |
316 SetSceneExtent(locker); | |
317 geometry = locker.GetValue(); | |
318 } | |
319 | |
320 WidgetBase::Start(); | |
321 | |
322 ViewChangeFunctor functor(geometry); | |
323 observers_.Notify(this, functor); | |
324 } | |
325 | |
326 | |
327 void WorldSceneWidget::SetDefaultView() | |
328 { | |
329 ViewportGeometry geometry; | |
330 | |
331 { | |
332 SharedValue<ViewportGeometry>::Locker locker(view_); | |
333 SetSceneExtent(locker); | |
334 locker.GetValue().SetDefaultView(); | |
335 geometry = locker.GetValue(); | |
336 } | |
337 | |
338 NotifyChange(); | |
339 | |
340 ViewChangeFunctor functor(geometry); | |
341 observers_.Notify(this, functor); | |
342 } | |
343 | |
344 | |
345 void WorldSceneWidget::SetView(const ViewportGeometry& view) | |
346 { | |
347 { | |
348 SharedValue<ViewportGeometry>::Locker locker(view_); | |
349 locker.GetValue() = view; | |
350 } | |
351 | |
352 NotifyChange(); | |
353 | |
354 ViewChangeFunctor functor(view); | |
355 observers_.Notify(this, functor); | |
356 } | |
357 | |
358 | |
359 ViewportGeometry WorldSceneWidget::GetView() | |
360 { | |
361 SharedValue<ViewportGeometry>::Locker locker(view_); | |
362 return locker.GetValue(); | |
363 } | |
364 | |
365 | |
366 IMouseTracker* WorldSceneWidget::CreateMouseTracker(MouseButton button, | |
367 int x, | |
368 int y, | |
369 KeyboardModifiers modifiers) | |
370 { | |
371 ViewportGeometry view = GetView(); | |
372 | |
373 double sceneX, sceneY; | |
374 MapMouseToScene(sceneX, sceneY, view, x, y); | |
375 | |
376 std::auto_ptr<IWorldSceneMouseTracker> tracker(CreateMouseSceneTracker(view, button, sceneX, sceneY, modifiers)); | |
377 if (tracker.get() != NULL) | |
378 { | |
379 return new SceneMouseTracker(view, tracker.release()); | |
380 } | |
381 | |
382 switch (button) | |
383 { | |
384 case MouseButton_Middle: | |
385 return new PanMouseTracker(*this, x, y); | |
386 | |
387 case MouseButton_Right: | |
388 return new ZoomMouseTracker(*this, x, y); | |
389 | |
390 default: | |
391 return NULL; | |
392 } | |
393 } | |
394 | |
395 | |
396 void WorldSceneWidget::RenderSceneMouseOver(CairoContext& context, | |
397 const ViewportGeometry& view, | |
398 double x, | |
399 double y) | |
400 { | |
401 if (interactor_) | |
402 { | |
403 interactor_->MouseOver(context, *this, GetSlice(), view, x, y, GetStatusBar()); | |
404 } | |
405 } | |
406 | |
407 IWorldSceneMouseTracker* WorldSceneWidget::CreateMouseSceneTracker(const ViewportGeometry& view, | |
408 MouseButton button, | |
409 double x, | |
410 double y, | |
411 KeyboardModifiers modifiers) | |
412 { | |
413 if (interactor_) | |
414 { | |
415 return interactor_->CreateMouseTracker(*this, GetSlice(), view, button, x, y, GetStatusBar()); | |
416 } | |
417 else | |
418 { | |
419 return NULL; | |
420 } | |
421 } | |
422 | |
423 | |
424 void WorldSceneWidget::MouseWheel(MouseWheelDirection direction, | |
425 int x, | |
426 int y, | |
427 KeyboardModifiers modifiers) | |
428 { | |
429 if (interactor_) | |
430 { | |
431 interactor_->MouseWheel(*this, direction, modifiers, GetStatusBar()); | |
432 } | |
433 } | |
434 | |
435 | |
436 void WorldSceneWidget::KeyPressed(char key, | |
437 KeyboardModifiers modifiers) | |
438 { | |
439 if (interactor_) | |
440 { | |
441 interactor_->KeyPressed(*this, key, modifiers, GetStatusBar()); | |
442 } | |
443 } | |
444 } |