Mercurial > hg > orthanc-stone
annotate Framework/Widgets/WorldSceneWidget.cpp @ 323:dbfe2e9e5020 am-2
playing with LambdaCallable
author | am@osimis.io |
---|---|
date | Mon, 15 Oct 2018 23:36:43 +0200 |
parents | 8bdc6112bc2e |
children | 8716176ff7f0 |
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. | |
281 | 16 * |
47 | 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 | |
110
53025eecbc95
renamed SliceGeometry as CoordinateSystem3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
109
diff
changeset
|
24 #include <math.h> |
53025eecbc95
renamed SliceGeometry as CoordinateSystem3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
109
diff
changeset
|
25 #include <memory> |
53025eecbc95
renamed SliceGeometry as CoordinateSystem3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
109
diff
changeset
|
26 #include <cassert> |
313 | 27 #include "Core/Logging.h" |
0 | 28 |
29 namespace OrthancStone | |
30 { | |
31 static void MapMouseToScene(double& sceneX, | |
32 double& sceneY, | |
33 const ViewportGeometry& view, | |
34 int mouseX, | |
35 int mouseY) | |
36 { | |
37 // Take the center of the pixel | |
38 double x, y; | |
39 x = static_cast<double>(mouseX) + 0.5; | |
40 y = static_cast<double>(mouseY) + 0.5; | |
41 | |
42 view.MapDisplayToScene(sceneX, sceneY, x, y); | |
43 } | |
44 | |
45 | |
46 struct WorldSceneWidget::SizeChangeFunctor | |
47 { | |
48 ViewportGeometry& view_; | |
49 | |
50 SizeChangeFunctor(ViewportGeometry& view) : | |
51 view_(view) | |
52 { | |
53 } | |
54 | |
55 void operator() (IWorldObserver& observer, | |
56 const WorldSceneWidget& source) | |
57 { | |
58 observer.NotifySizeChange(source, view_); | |
59 } | |
60 }; | |
61 | |
62 | |
281 | 63 // this is an adapter between a IWorldSceneMouseTracker |
64 // that is tracking a mouse in scene coordinates/mm and | |
65 // an IMouseTracker that is tracking a mouse | |
66 // in screen coordinates/pixels. | |
0 | 67 class WorldSceneWidget::SceneMouseTracker : public IMouseTracker |
68 { | |
69 private: | |
70 ViewportGeometry view_; | |
71 std::auto_ptr<IWorldSceneMouseTracker> tracker_; | |
281 | 72 |
0 | 73 public: |
74 SceneMouseTracker(const ViewportGeometry& view, | |
75 IWorldSceneMouseTracker* tracker) : | |
76 view_(view), | |
77 tracker_(tracker) | |
78 { | |
79 assert(tracker != NULL); | |
80 } | |
81 | |
82 virtual void Render(Orthanc::ImageAccessor& target) | |
83 { | |
84 CairoSurface surface(target); | |
281 | 85 CairoContext context(surface); |
0 | 86 view_.ApplyTransform(context); |
87 tracker_->Render(context, view_.GetZoom()); | |
88 } | |
89 | |
281 | 90 virtual void MouseUp() |
0 | 91 { |
92 tracker_->MouseUp(); | |
93 } | |
94 | |
281 | 95 virtual void MouseMove(int x, |
0 | 96 int y) |
97 { | |
98 double sceneX, sceneY; | |
99 MapMouseToScene(sceneX, sceneY, view_, x, y); | |
100 tracker_->MouseMove(sceneX, sceneY); | |
101 } | |
102 }; | |
103 | |
104 | |
281 | 105 WorldSceneWidget::PanMouseTracker::PanMouseTracker(WorldSceneWidget& that, |
106 int x, | |
107 int y) : | |
108 that_(that), | |
109 downX_(x), | |
110 downY_(y) | |
0 | 111 { |
281 | 112 that_.view_.GetPan(previousPanX_, previousPanY_); |
113 } | |
114 | |
115 void WorldSceneWidget::PanMouseTracker::MouseMove(int x, int y) | |
116 { | |
117 that_.view_.SetPan(previousPanX_ + x - downX_, | |
118 previousPanY_ + y - downY_); | |
119 | |
120 that_.observers_.Apply(that_, &IWorldObserver::NotifyViewChange, that_.view_); | |
121 } | |
0 | 122 |
281 | 123 WorldSceneWidget::ZoomMouseTracker::ZoomMouseTracker(WorldSceneWidget& that, |
124 int x, | |
125 int y) : | |
126 that_(that), | |
127 downX_(x), | |
128 downY_(y) | |
129 { | |
130 oldZoom_ = that_.view_.GetZoom(); | |
131 MapMouseToScene(centerX_, centerY_, that_.view_, downX_, downY_); | |
132 } | |
133 | |
134 void WorldSceneWidget::ZoomMouseTracker::MouseMove(int x, | |
135 int y) | |
136 { | |
137 static const double MIN_ZOOM = -4; | |
138 static const double MAX_ZOOM = 4; | |
139 | |
140 if (that_.view_.GetDisplayHeight() <= 3) | |
0 | 141 { |
313 | 142 LOG(WARNING) << "image is too small to zoom (current height = " << that_.view_.GetDisplayHeight() << ")"; |
143 return; | |
0 | 144 } |
145 | |
281 | 146 double dy = (static_cast<double>(y - downY_) / |
147 static_cast<double>(that_.view_.GetDisplayHeight() - 1)); // In the range [-1,1] | |
148 double z; | |
0 | 149 |
281 | 150 // Linear interpolation from [-1, 1] to [MIN_ZOOM, MAX_ZOOM] |
151 if (dy < -1.0) | |
0 | 152 { |
281 | 153 z = MIN_ZOOM; |
0 | 154 } |
281 | 155 else if (dy > 1.0) |
0 | 156 { |
281 | 157 z = MAX_ZOOM; |
0 | 158 } |
281 | 159 else |
0 | 160 { |
281 | 161 z = MIN_ZOOM + (MAX_ZOOM - MIN_ZOOM) * (dy + 1.0) / 2.0; |
0 | 162 } |
163 | |
281 | 164 z = pow(2.0, z); |
0 | 165 |
281 | 166 that_.view_.SetZoom(oldZoom_ * z); |
0 | 167 |
281 | 168 // Correct the pan so that the original click point is kept at |
169 // the same location on the display | |
170 double panX, panY; | |
171 that_.view_.GetPan(panX, panY); | |
0 | 172 |
281 | 173 int tx, ty; |
174 that_.view_.MapSceneToDisplay(tx, ty, centerX_, centerY_); | |
175 that_.view_.SetPan(panX + static_cast<double>(downX_ - tx), | |
176 panY + static_cast<double>(downY_ - ty)); | |
0 | 177 |
281 | 178 that_.observers_.Apply(that_, &IWorldObserver::NotifyViewChange, that_.view_); |
179 } | |
0 | 180 |
181 | |
182 bool WorldSceneWidget::RenderCairo(CairoContext& context) | |
183 { | |
53
c2dc924f1a63
removing threading out of the framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
48
diff
changeset
|
184 view_.ApplyTransform(context); |
c2dc924f1a63
removing threading out of the framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
48
diff
changeset
|
185 return RenderScene(context, view_); |
0 | 186 } |
187 | |
188 | |
189 void WorldSceneWidget::RenderMouseOverCairo(CairoContext& context, | |
190 int x, | |
191 int y) | |
192 { | |
193 ViewportGeometry view = GetView(); | |
194 view.ApplyTransform(context); | |
195 | |
196 double sceneX, sceneY; | |
197 MapMouseToScene(sceneX, sceneY, view, x, y); | |
198 RenderSceneMouseOver(context, view, sceneX, sceneY); | |
199 } | |
200 | |
201 | |
53
c2dc924f1a63
removing threading out of the framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
48
diff
changeset
|
202 void WorldSceneWidget::SetSceneExtent(ViewportGeometry& view) |
0 | 203 { |
109
53bd9277b025
using the Extent class
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
91
diff
changeset
|
204 view.SetSceneExtent(GetSceneExtent()); |
0 | 205 } |
206 | |
207 | |
208 void WorldSceneWidget::SetSize(unsigned int width, | |
209 unsigned int height) | |
210 { | |
211 CairoWidget::SetSize(width, height); | |
212 | |
53
c2dc924f1a63
removing threading out of the framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
48
diff
changeset
|
213 view_.SetDisplaySize(width, height); |
0 | 214 |
53
c2dc924f1a63
removing threading out of the framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
48
diff
changeset
|
215 if (observers_.IsEmpty()) |
c2dc924f1a63
removing threading out of the framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
48
diff
changeset
|
216 { |
66 | 217 // Without a size observer, reset to the default view |
218 // view_.SetDefaultView(); | |
53
c2dc924f1a63
removing threading out of the framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
48
diff
changeset
|
219 } |
c2dc924f1a63
removing threading out of the framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
48
diff
changeset
|
220 else |
c2dc924f1a63
removing threading out of the framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
48
diff
changeset
|
221 { |
c2dc924f1a63
removing threading out of the framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
48
diff
changeset
|
222 // With a size observer, let it decide which view to use |
c2dc924f1a63
removing threading out of the framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
48
diff
changeset
|
223 SizeChangeFunctor functor(view_); |
91 | 224 observers_.Notify(*this, functor); |
0 | 225 } |
226 } | |
227 | |
228 | |
229 void WorldSceneWidget::SetInteractor(IWorldSceneInteractor& interactor) | |
230 { | |
231 interactor_ = &interactor; | |
232 } | |
233 | |
234 | |
235 void WorldSceneWidget::SetDefaultView() | |
236 { | |
53
c2dc924f1a63
removing threading out of the framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
48
diff
changeset
|
237 SetSceneExtent(view_); |
c2dc924f1a63
removing threading out of the framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
48
diff
changeset
|
238 view_.SetDefaultView(); |
0 | 239 |
278 | 240 NotifyContentChanged(); |
0 | 241 |
91 | 242 observers_.Apply(*this, &IWorldObserver::NotifyViewChange, view_); |
0 | 243 } |
244 | |
245 | |
246 void WorldSceneWidget::SetView(const ViewportGeometry& view) | |
247 { | |
53
c2dc924f1a63
removing threading out of the framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
48
diff
changeset
|
248 view_ = view; |
0 | 249 |
278 | 250 NotifyContentChanged(); |
0 | 251 |
91 | 252 observers_.Apply(*this, &IWorldObserver::NotifyViewChange, view_); |
0 | 253 } |
254 | |
255 | |
256 ViewportGeometry WorldSceneWidget::GetView() | |
257 { | |
53
c2dc924f1a63
removing threading out of the framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
48
diff
changeset
|
258 return view_; |
0 | 259 } |
260 | |
261 | |
262 IMouseTracker* WorldSceneWidget::CreateMouseTracker(MouseButton button, | |
263 int x, | |
264 int y, | |
265 KeyboardModifiers modifiers) | |
266 { | |
53
c2dc924f1a63
removing threading out of the framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
48
diff
changeset
|
267 double sceneX, sceneY; |
c2dc924f1a63
removing threading out of the framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
48
diff
changeset
|
268 MapMouseToScene(sceneX, sceneY, view_, x, y); |
0 | 269 |
281 | 270 // asks the Widget Interactor to provide a mouse tracker |
53
c2dc924f1a63
removing threading out of the framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
48
diff
changeset
|
271 std::auto_ptr<IWorldSceneMouseTracker> tracker |
281 | 272 (CreateMouseSceneTracker(view_, button, sceneX, sceneY, modifiers)); |
0 | 273 |
274 if (tracker.get() != NULL) | |
275 { | |
53
c2dc924f1a63
removing threading out of the framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
48
diff
changeset
|
276 return new SceneMouseTracker(view_, tracker.release()); |
0 | 277 } |
281 | 278 //TODO: allow Interactor to create Pan & Zoom |
0 | 279 switch (button) |
280 { | |
281 | 281 case MouseButton_Middle: |
282 return new PanMouseTracker(*this, x, y); | |
0 | 283 |
281 | 284 case MouseButton_Right: |
285 return new ZoomMouseTracker(*this, x, y); | |
0 | 286 |
281 | 287 default: |
288 return NULL; | |
0 | 289 } |
290 } | |
291 | |
292 | |
293 void WorldSceneWidget::RenderSceneMouseOver(CairoContext& context, | |
294 const ViewportGeometry& view, | |
295 double x, | |
296 double y) | |
297 { | |
298 if (interactor_) | |
299 { | |
59 | 300 interactor_->MouseOver(context, *this, view, x, y, GetStatusBar()); |
0 | 301 } |
302 } | |
303 | |
304 IWorldSceneMouseTracker* WorldSceneWidget::CreateMouseSceneTracker(const ViewportGeometry& view, | |
305 MouseButton button, | |
306 double x, | |
307 double y, | |
308 KeyboardModifiers modifiers) | |
309 { | |
310 if (interactor_) | |
311 { | |
281 | 312 return interactor_->CreateMouseTracker(*this, view, button, modifiers, x, y, GetStatusBar()); |
0 | 313 } |
314 else | |
315 { | |
316 return NULL; | |
317 } | |
318 } | |
319 | |
320 | |
321 void WorldSceneWidget::MouseWheel(MouseWheelDirection direction, | |
322 int x, | |
323 int y, | |
281 | 324 KeyboardModifiers modifiers) |
0 | 325 { |
326 if (interactor_) | |
327 { | |
328 interactor_->MouseWheel(*this, direction, modifiers, GetStatusBar()); | |
329 } | |
330 } | |
331 | |
332 | |
333 void WorldSceneWidget::KeyPressed(char key, | |
334 KeyboardModifiers modifiers) | |
335 { | |
336 if (interactor_) | |
337 { | |
338 interactor_->KeyPressed(*this, key, modifiers, GetStatusBar()); | |
339 } | |
340 } | |
341 } |