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