Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkCameraController.cpp
Go to the documentation of this file.
1 /*===================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center,
6 Division of Medical and Biological Informatics.
7 All rights reserved.
8 
9 This software is distributed WITHOUT ANY WARRANTY; without
10 even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE.
12 
13 See LICENSE.txt or http://www.mitk.org for details.
14 
15 ===================================================================*/
16 
17 #include "mitkCameraController.h"
18 #include "mitkRenderingManager.h"
19 #include "mitkVtkPropRenderer.h"
20 #include "vtkCommand.h"
21 #include <vtkRenderWindowInteractor.h>
22 
23 #include "vtkCamera.h"
24 #include "vtkRenderer.h"
25 #include <vtkSmartPointer.h>
26 #include <vtkTransform.h>
27 
29 {
30 }
31 
33 {
34 }
35 
37 {
38  double widthInMM = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(0);
39  double heightInMM = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(1);
40 
41  double dispHeight = this->GetRenderer()->GetViewportSize()[1]; // in pixel!
42  double dispWidth = this->GetRenderer()->GetViewportSize()[0];
43 
44  // To get the right zooming factor, we need to set the (half) height to the vtk camera using SetParallelScale.
45  // However, it could be, that our picture is so wide or the display so small, that we cannot take the height of the
46  // picture.
47  // For a wide picture, we have to take the width and adapt the width so that our image fits to the screen.
48  // But we can only set the height. Therefore, if the width is the limiting factor, we need to get the ratio of scaling
49  // for the width and multiply it with the height, so that we have a modified height and set this one. Believe us, we
50  // figured it out...
51  if ((dispWidth / widthInMM) < (dispHeight / heightInMM))
52  {
53  heightInMM = widthInMM / dispWidth * dispHeight;
54  }
55 
56  return heightInMM * 0.5;
57 }
58 
59 void mitk::CameraController::AdjustConstrainedCameraPosition(mitk::Point2D &planePoint)
60 {
61  // TODO: GetExtentInMM is calculated wrong for rotated planes, e.g. crosshair rotation (bug 19105)
62  double widthInMM = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(0);
63  double heightInMM = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(1);
64 
65  mitk::Point2D dispSizeInMM = this->GetRenderer()->GetViewportSizeInMM();
66 
67  double xMin, xMax, yMin, yMax;
68 
69  // different calculation of min/max if display is lager/smaller than image.
70  // note, that the plane Position defines the middle of the display but is in image coordinates
71  //([0,0] is defined by the image, so planePosition can sometimes be negative).
72  if (dispSizeInMM[0] > widthInMM)
73  {
74  xMin = widthInMM - 0.5 * dispSizeInMM[0];
75  xMax = 0.5 * dispSizeInMM[0];
76  }
77  else
78  {
79  xMin = 0.5 * dispSizeInMM[0];
80  xMax = widthInMM - 0.5 * dispSizeInMM[0];
81  }
82 
83  if (dispSizeInMM[1] > heightInMM)
84  {
85  yMin = heightInMM - 0.5 * dispSizeInMM[1];
86  yMax = 0.5 * dispSizeInMM[1];
87  }
88  else
89  {
90  yMin = 0.5 * dispSizeInMM[1];
91  yMax = heightInMM - 0.5 * dispSizeInMM[1];
92  }
93 
94  if (planePoint[0] < xMin)
95  {
96  planePoint[0] = xMin;
97  }
98  if (planePoint[1] < yMin)
99  {
100  planePoint[1] = yMin;
101  }
102  if (planePoint[0] > xMax)
103  {
104  planePoint[0] = xMax;
105  }
106  if (planePoint[1] > yMax)
107  {
108  planePoint[1] = yMax;
109  }
110 }
111 
113 {
114  this->SetStandardView(ANTERIOR);
115 }
116 
118 {
119  this->SetStandardView(POSTERIOR);
120 }
121 
123 {
124  this->SetStandardView(SINISTER);
125 }
126 
128 {
129  this->SetStandardView(DEXTER);
130 }
131 
133 {
134  this->SetStandardView(CRANIAL);
135 }
136 
138 {
139  this->SetStandardView(CAUDAL);
140 }
141 
143 {
144  const mitk::VtkPropRenderer *glRenderer = dynamic_cast<const mitk::VtkPropRenderer *>(m_Renderer);
145  if (glRenderer == NULL)
146  return;
147  vtkRenderer *vtkRenderer = glRenderer->GetVtkRenderer();
148  assert(vtkRenderer);
149 
151  mitk::DataStorage *ds = m_Renderer->GetDataStorage();
152  if (ds != NULL)
153  bb = ds->ComputeBoundingBox();
154  else
155  return;
156 
157  if (m_Renderer->GetMapperID() == mitk::BaseRenderer::Standard3D)
158  {
159  // set up the view for the 3D render window. The views for 2D are set up in the mitkVtkPropRenderer
160  mitk::Point3D middle = bb->GetCenter();
161  vtkRenderer->GetActiveCamera()->SetFocalPoint(middle[0], middle[1], middle[2]);
162  switch (view)
163  {
164  case ANTERIOR:
165  case POSTERIOR:
166  case SINISTER:
167  case DEXTER:
168  vtkRenderer->GetActiveCamera()->SetViewUp(0, 0, 1);
169  break;
170  case CRANIAL:
171  case CAUDAL:
172  vtkRenderer->GetActiveCamera()->SetViewUp(0, -1, 0);
173  break;
174  }
175  switch (view)
176  {
177  case ANTERIOR:
178  vtkRenderer->GetActiveCamera()->SetPosition(middle[0], -100000, middle[2]);
179  break;
180  case POSTERIOR:
181  vtkRenderer->GetActiveCamera()->SetPosition(middle[0], +100000, middle[2]);
182  break;
183  case SINISTER:
184  vtkRenderer->GetActiveCamera()->SetPosition(+100000, middle[1], middle[2]);
185  break;
186  case DEXTER:
187  vtkRenderer->GetActiveCamera()->SetPosition(-100000, middle[1], middle[2]);
188  break;
189  case CRANIAL:
190  vtkRenderer->GetActiveCamera()->SetPosition(middle[0], middle[1], 100000);
191  break;
192  case CAUDAL:
193  vtkRenderer->GetActiveCamera()->SetPosition(middle[0], middle[1], -100000);
194  break;
195  }
196  vtkRenderer->ResetCamera();
197 
198  vtkRenderer->ResetCameraClippingRange();
199  }
200 
201  mitk::RenderingManager *rm = m_Renderer->GetRenderingManager();
202  rm->RequestUpdateAll();
203 }
204 
206 {
207  Point2D moveToPoint = planePoint;
208  AdjustCameraToPlane(moveToPoint);
209 
210  this->Modified();
211 }
212 
214 {
215  MoveCameraToPoint(GetCameraPositionOnPlane() + moveVectorInMM);
216 }
217 
218 void mitk::CameraController::Zoom(ScalarType factor, const Point2D &zoomPointInMM)
219 {
220  if (factor <= 0.0)
221  return;
222  if (this->GetRenderer()->GetMapperID() == BaseRenderer::Standard2D)
223  {
224  double parallelScale = this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->GetParallelScale() / factor;
225  if (this->GetRenderer()->GetConstrainZoomingAndPanning() && factor < 1.0)
226  {
227  double maxParallelScale = ComputeMaxParallelScale();
228  if (maxParallelScale - parallelScale * factor <
229  mitk::eps) // this is not the famous 05-bug... Return if already near max zooming
230  return;
231 
232  if (parallelScale > maxParallelScale)
233  {
234  parallelScale = maxParallelScale;
235  }
236  }
237  this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetParallelScale(parallelScale);
238  // Move camera in a way that the clicked point stays visible on the display where it was.
239  Point2D planePoint = GetCameraPositionOnPlane();
240  MoveCameraToPoint(planePoint + ((zoomPointInMM - planePoint) * (factor - 1)));
241  }
242 }
243 
245 {
246  Point2D CamPosOnPlane;
247  CamPosOnPlane[0] = this->GetRenderer()->GetVtkRenderer()->GetCenter()[0];
248  CamPosOnPlane[1] = this->GetRenderer()->GetVtkRenderer()->GetCenter()[1];
249  this->GetRenderer()->DisplayToPlane(CamPosOnPlane, CamPosOnPlane);
250  return CamPosOnPlane;
251 }
252 
254 {
255  if (this->GetRenderer()->GetMapperID() == BaseRenderer::Standard2D)
256  {
257  AdjustCameraToPlane(GetCameraPositionOnPlane());
258  }
259 }
260 
262 {
263  if (this->GetRenderer()->GetMapperID() == BaseRenderer::Standard2D)
264  {
265  Point2D _planePoint = PlanePoint; // PlanePoint is const...
266  if (this->GetRenderer()->GetConstrainZoomingAndPanning())
267  {
268  double parallelScale = this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->GetParallelScale();
269  double maxParallelScale = ComputeMaxParallelScale();
270  if (parallelScale > maxParallelScale)
271  {
272  this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetParallelScale(maxParallelScale);
273  }
274  AdjustConstrainedCameraPosition(_planePoint);
275  }
276  const PlaneGeometry *planeGeometry = this->GetRenderer()->GetCurrentWorldPlaneGeometry();
277  if (planeGeometry != NULL)
278  {
279  this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetViewUp(0, 1, 0); // set the view-up for the camera
280  this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetPosition(_planePoint[0], _planePoint[1], 900000);
281  this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetFocalPoint(_planePoint[0], _planePoint[1], 0);
282  // Transform the camera to the current position (transversal, coronal and sagittal plane).
283  // This is necessary, because the SetUserTransform() method does not manipulate the vtkCamera.
284  //(Without not all three planes would be visible).
285  vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
286  vtkSmartPointer<vtkMatrix4x4> matrix = vtkSmartPointer<vtkMatrix4x4>::New();
287  Point3D origin;
288  Vector3D right, bottom, normal;
289 
290  origin = planeGeometry->GetOrigin();
291  right = planeGeometry->GetAxisVector(0); // right = Extent of Image in mm (worldspace)
292  bottom = planeGeometry->GetAxisVector(1);
293  normal = planeGeometry->GetNormal();
294 
295  right.Normalize();
296  bottom.Normalize();
297  normal.Normalize();
298 
299  matrix->SetElement(0, 0, right[0]);
300  matrix->SetElement(1, 0, right[1]);
301  matrix->SetElement(2, 0, right[2]);
302  matrix->SetElement(0, 1, bottom[0]);
303  matrix->SetElement(1, 1, bottom[1]);
304  matrix->SetElement(2, 1, bottom[2]);
305  matrix->SetElement(0, 2, normal[0]);
306  matrix->SetElement(1, 2, normal[1]);
307  matrix->SetElement(2, 2, normal[2]);
308  matrix->SetElement(0, 3, origin[0]);
309  matrix->SetElement(1, 3, origin[1]);
310  matrix->SetElement(2, 3, origin[2]);
311  matrix->SetElement(3, 0, 0.0);
312  matrix->SetElement(3, 1, 0.0);
313  matrix->SetElement(3, 2, 0.0);
314  matrix->SetElement(3, 3, 1.0);
315 
316  trans->SetMatrix(matrix);
317  // Transform the camera to the current position (transversal, coronal and sagittal plane).
318  this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->ApplyTransform(trans);
319  }
320  }
321 }
322 
324 {
325  if (this->GetRenderer()->GetMapperID() == BaseRenderer::Standard2D)
326  {
327  this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetParallelScale(ComputeMaxParallelScale());
328 
329  this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetClippingRange(0.1, 1000000);
330  // Reason for huge range: VTK seems to calculate the clipping planes wrong for small values. See VTK bug (id #7823)
331  // in VTK bugtracker.
332 
333  Point2D planePoint;
334  planePoint[0] = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(0) * 0.5;
335  planePoint[1] = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(1) * 0.5;
336  MoveCameraToPoint(planePoint);
337  }
338 }
339 
341 {
342  if (this->GetRenderer()->GetMapperID() != BaseRenderer::Standard2D)
343  {
344  return;
345  }
346 
347  this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetParallelScale(this->GetRenderer()->GetViewportSize()[1] *
348  scale * 0.5);
349 
350  this->Modified();
351 }
Data management class that handles 'was created by' relations.
itk::SmartPointer< Self > Pointer
Baseclass for renderer slice-/camera-control.
CameraController()
Default Constructor.
double ScalarType
void Zoom(ScalarType factor, const Point2D &zoomPointInMM)
void MoveBy(const Vector2D &moveVectorInMM)
Vector< ScalarType, 3 > Vector3D
Definition: mitkVector.h:134
Manager for coordinating the rendering process.
void SetScaleFactorInMMPerDisplayUnit(ScalarType scale)
Set the desired zoom-level to the absolute value given.
Point< ScalarType, 3 > Point3D
Definition: mitkPoint.h:99
mitk::BoundingBox::Pointer ComputeBoundingBox(const char *boolPropertyKey=nullptr, const mitk::BaseRenderer *renderer=nullptr, const char *boolPropertyKey2=nullptr)
Compute the bounding box of data tree structure it -> an iterator to a data tree structure.
virtual void SetStandardView(StandardView view)
MITKCORE_EXPORT const ScalarType eps
void MoveCameraToPoint(const Point2D &planePoint)
MoveCameraToPoint Move camera so that the point on the plane is in the view center.
void AdjustCameraToPlane()
AdjustCameraToPlane Moves the camera of a 2D Renderwindow without panning or zooming, eg. only rotate the camera.
vtkRenderer * GetVtkRenderer() const
void Fit()
Fit Adjust the camera, so that the world bounding box is fully visible.
virtual ~CameraController()
Default Destructor.
void RequestUpdateAll(RequestType type=REQUEST_UPDATE_ALL)
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.