Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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.