diff --git a/Modules/Core/include/mitkCameraController.h b/Modules/Core/include/mitkCameraController.h index f6ba4dce94..3d1fd353bf 100644 --- a/Modules/Core/include/mitkCameraController.h +++ b/Modules/Core/include/mitkCameraController.h @@ -1,116 +1,121 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef CAMERACONTROLLER_H_HEADER_INCLUDED_C1C53722 #define CAMERACONTROLLER_H_HEADER_INCLUDED_C1C53722 #include #include "mitkBaseController.h" namespace mitk { class KeyEvent; //##Documentation //## @brief controls the camera used by the associated BaseRenderer //## //## Subclass of BaseController. Controls the camera used by the associated //## BaseRenderer. //## @ingroup NavigationControl class MITKCORE_EXPORT CameraController : public BaseController { public: enum StandardView { ANTERIOR,POSTERIOR,SINISTER,DEXTER,CRANIAL,CAUDAL }; mitkClassMacro(CameraController, BaseController); //mitkNewMacro1Param(Self, const char*); itkNewMacro(Self); void SetRenderer(const BaseRenderer* renderer) { m_Renderer = renderer; }; itkGetConstMacro(Renderer,const BaseRenderer*); virtual void SetViewToAnterior(); virtual void SetViewToPosterior(); virtual void SetViewToSinister(); virtual void SetViewToDexter(); virtual void SetViewToCranial(); virtual void SetViewToCaudal(); virtual void SetStandardView(StandardView view); /** * @brief Fit Adjust the camera, so that the world bounding box is fully visible. */ void Fit(); + /** + * @brief Set the desired zoom-level to the absolute value given. + */ + void SetScaleFactorInMMPerDisplayUnit( ScalarType scale ); + /** * @brief MoveCameraToPoint Move camera so that the point on the plane is in the view center. * @param planePoint */ void MoveCameraToPoint(const Point2D &planePoint); void MoveBy(const Vector2D &moveVectorInMM); void Zoom(ScalarType factor, const Point2D &zoomPointInMM); Point2D GetCameraPositionOnPlane(); /** * @brief AdjustCameraToPlane Moves the camera of a 2D Renderwindow without panning or * zooming, eg. only rotate the camera. */ void AdjustCameraToPlane(); protected: /** * @brief Default Constructor **/ CameraController(); /** * @brief Default Destructor **/ virtual ~CameraController(); const BaseRenderer* m_Renderer; ScalarType ComputeMaxParallelScale(); private: /** * @brief AdjustCameraToPlane Moves the camera of a 2D Renderwindow without panning or * zooming, eg. only rotate the camera. * @param PlanePoint point where the camera is moved to during the adjustment. */ void AdjustCameraToPlane(const Point2D& PlanePoint); void AdjustConstrainedCameraPosition(Point2D& planePoint); }; } // namespace mitk #endif /* CAMERACONTROLLER_H_HEADER_INCLUDED_C1C53722 */ diff --git a/Modules/Core/src/Controllers/mitkCameraController.cpp b/Modules/Core/src/Controllers/mitkCameraController.cpp index 9edd58686f..675c2ed01a 100644 --- a/Modules/Core/src/Controllers/mitkCameraController.cpp +++ b/Modules/Core/src/Controllers/mitkCameraController.cpp @@ -1,324 +1,335 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkCameraController.h" #include "mitkVtkPropRenderer.h" #include "mitkRenderingManager.h" #include #include "vtkCommand.h" #include "vtkCamera.h" #include "vtkRenderer.h" #include #include mitk::CameraController::CameraController() : BaseController(), m_Renderer(NULL) { } mitk::CameraController::~CameraController() {} mitk::ScalarType mitk::CameraController::ComputeMaxParallelScale() { double widthInMM = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(0); double heightInMM = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(1); double dispHeight = this->GetRenderer()->GetViewportSize()[1]; //in pixel! double dispWidth = this->GetRenderer()->GetViewportSize()[0]; //To get the right zooming factor, we need to set the (half) height to the vtk camera using SetParallelScale. //However, it could be, that our picture is so wide or the display so small, that we cannot take the height of the picture. //For a wide picture, we have to take the width and adapt the width so that our image fits to the screen. //But we can only set the height. Therefore, if the width is the limiting factor, we need to get the ratio of scaling //for the width and multiply it with the height, so that we have a modified height and set this one. Believe us, we figured it out... if ((dispWidth / widthInMM) < (dispHeight / heightInMM)) { heightInMM = widthInMM / dispWidth * dispHeight; } return heightInMM *0.5; } void mitk::CameraController::AdjustConstrainedCameraPosition(mitk::Point2D &planePoint) { //TODO: GetExtentInMM is calculated wrong for rotated planes, e.g. crosshair rotation (bug 19105) double widthInMM = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(0); double heightInMM = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(1); mitk::Point2D dispSizeInMM = this->GetRenderer()->GetViewportSizeInMM(); double xMin, xMax, yMin, yMax; //different calculation of min/max if display is lager/smaller than image. //note, that the plane Position defines the middle of the display but is in image coordinates //([0,0] is defined by the image, so planePosition can sometimes be negative). if (dispSizeInMM[0] > widthInMM) { xMin = widthInMM - 0.5 * dispSizeInMM[0]; xMax = 0.5 * dispSizeInMM[0] ; } else { xMin = 0.5 * dispSizeInMM[0]; xMax = widthInMM - 0.5 * dispSizeInMM[0]; } if (dispSizeInMM[1] > heightInMM) { yMin = heightInMM - 0.5 * dispSizeInMM[1]; yMax = 0.5 * dispSizeInMM[1] ; } else { yMin = 0.5 * dispSizeInMM[1]; yMax = heightInMM - 0.5 * dispSizeInMM[1]; } if (planePoint[0] < xMin) { planePoint[0] = xMin; } if (planePoint[1] < yMin) { planePoint[1] = yMin; } if (planePoint[0] > xMax) { planePoint[0] = xMax; } if (planePoint[1] > yMax) { planePoint[1] = yMax; } } void mitk::CameraController::SetViewToAnterior() { this->SetStandardView(ANTERIOR); } void mitk::CameraController::SetViewToPosterior() { this->SetStandardView(POSTERIOR); } void mitk::CameraController::SetViewToSinister() { this->SetStandardView(SINISTER); } void mitk::CameraController::SetViewToDexter() { this->SetStandardView(DEXTER); } void mitk::CameraController::SetViewToCranial() { this->SetStandardView(CRANIAL); } void mitk::CameraController::SetViewToCaudal() { this->SetStandardView(CAUDAL); } void mitk::CameraController::SetStandardView(mitk::CameraController::StandardView view) { const mitk::VtkPropRenderer* glRenderer = dynamic_cast(m_Renderer); if (glRenderer == NULL) return; vtkRenderer* vtkRenderer = glRenderer->GetVtkRenderer(); assert(vtkRenderer); mitk::BoundingBox::Pointer bb; mitk::DataStorage* ds = m_Renderer->GetDataStorage(); if (ds != NULL) bb = ds->ComputeBoundingBox(); else return; if (m_Renderer->GetMapperID() == mitk::BaseRenderer::Standard3D) { //set up the view for the 3D render window. The views for 2D are set up in the mitkVtkPropRenderer mitk::Point3D middle = bb->GetCenter(); vtkRenderer->GetActiveCamera()->SetFocalPoint(middle[0], middle[1], middle[2]); switch (view) { case ANTERIOR: case POSTERIOR: case SINISTER: case DEXTER: vtkRenderer->GetActiveCamera()->SetViewUp(0, 0, 1); break; case CRANIAL: case CAUDAL: vtkRenderer->GetActiveCamera()->SetViewUp(0, -1, 0); break; } switch (view) { case ANTERIOR: vtkRenderer->GetActiveCamera()->SetPosition(middle[0], -100000, middle[2]); break; case POSTERIOR: vtkRenderer->GetActiveCamera()->SetPosition(middle[0], +100000, middle[2]); break; case SINISTER: vtkRenderer->GetActiveCamera()->SetPosition(+100000, middle[1], middle[2]); break; case DEXTER: vtkRenderer->GetActiveCamera()->SetPosition(-100000, middle[1], middle[2]); break; case CRANIAL: vtkRenderer->GetActiveCamera()->SetPosition(middle[0], middle[1], 100000); break; case CAUDAL: vtkRenderer->GetActiveCamera()->SetPosition(middle[0], middle[1], -100000); break; } vtkRenderer->ResetCamera(); vtkRenderer->ResetCameraClippingRange(); } mitk::RenderingManager* rm = m_Renderer->GetRenderingManager(); rm->RequestUpdateAll(); } void mitk::CameraController::MoveCameraToPoint(const mitk::Point2D& planePoint) { Point2D moveToPoint = planePoint; AdjustCameraToPlane(moveToPoint); } void mitk::CameraController::MoveBy(const mitk::Vector2D &moveVectorInMM) { MoveCameraToPoint(GetCameraPositionOnPlane() + moveVectorInMM); } void mitk::CameraController::Zoom(ScalarType factor, const Point2D& zoomPointInMM) { if (factor <= 0.0) return; if (this->GetRenderer()->GetMapperID() == BaseRenderer::Standard2D) { double parallelScale = this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->GetParallelScale()/factor; if (this->GetRenderer()->GetConstrainZoomingAndPanning() && factor < 1.0) { double maxParallelScale = ComputeMaxParallelScale(); if (maxParallelScale - parallelScale * factor < mitk::eps)//this is not the famous 05-bug... Return if already near max zooming return; if (parallelScale > maxParallelScale) { parallelScale = maxParallelScale; } } this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetParallelScale(parallelScale); //Move camera in a way that the clicked point stays visible on the display where it was. Point2D planePoint = GetCameraPositionOnPlane(); MoveCameraToPoint(planePoint + ((zoomPointInMM - planePoint) * (factor - 1))); } } mitk::Point2D mitk::CameraController::GetCameraPositionOnPlane() { Point2D CamPosOnPlane; CamPosOnPlane[0] = this->GetRenderer()->GetVtkRenderer()->GetCenter()[0]; CamPosOnPlane[1] = this->GetRenderer()->GetVtkRenderer()->GetCenter()[1]; this->GetRenderer()->DisplayToPlane(CamPosOnPlane, CamPosOnPlane); return CamPosOnPlane; } void mitk::CameraController::AdjustCameraToPlane() { if (this->GetRenderer()->GetMapperID() == BaseRenderer::Standard2D) { AdjustCameraToPlane(GetCameraPositionOnPlane()); } } void mitk::CameraController::AdjustCameraToPlane(const Point2D& PlanePoint) { if (this->GetRenderer()->GetMapperID() == BaseRenderer::Standard2D) { Point2D _planePoint = PlanePoint;//PlanePoint is const... if (this->GetRenderer()->GetConstrainZoomingAndPanning()) { double parallelScale = this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->GetParallelScale(); double maxParallelScale = ComputeMaxParallelScale(); if (parallelScale > maxParallelScale) { this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetParallelScale(maxParallelScale); } AdjustConstrainedCameraPosition(_planePoint); } const PlaneGeometry *planeGeometry = this->GetRenderer()->GetCurrentWorldPlaneGeometry(); if (planeGeometry != NULL) { this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetViewUp(0, 1, 0); //set the view-up for the camera this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetPosition(_planePoint[0], _planePoint[1], 900000); this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetFocalPoint(_planePoint[0], _planePoint[1], 0); //Transform the camera to the current position (transversal, coronal and sagittal plane). //This is necessary, because the SetUserTransform() method does not manipulate the vtkCamera. //(Without not all three planes would be visible). vtkSmartPointer trans = vtkSmartPointer::New(); vtkSmartPointer matrix = vtkSmartPointer::New(); Point3D origin; Vector3D right, bottom, normal; origin = planeGeometry->GetOrigin(); right = planeGeometry->GetAxisVector(0); // right = Extent of Image in mm (worldspace) bottom = planeGeometry->GetAxisVector(1); normal = planeGeometry->GetNormal(); right.Normalize(); bottom.Normalize(); normal.Normalize(); matrix->SetElement(0, 0, right[0]); matrix->SetElement(1, 0, right[1]); matrix->SetElement(2, 0, right[2]); matrix->SetElement(0, 1, bottom[0]); matrix->SetElement(1, 1, bottom[1]); matrix->SetElement(2, 1, bottom[2]); matrix->SetElement(0, 2, normal[0]); matrix->SetElement(1, 2, normal[1]); matrix->SetElement(2, 2, normal[2]); matrix->SetElement(0, 3, origin[0]); matrix->SetElement(1, 3, origin[1]); matrix->SetElement(2, 3, origin[2]); matrix->SetElement(3, 0, 0.0); matrix->SetElement(3, 1, 0.0); matrix->SetElement(3, 2, 0.0); matrix->SetElement(3, 3, 1.0); trans->SetMatrix(matrix); //Transform the camera to the current position (transversal, coronal and sagittal plane). this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->ApplyTransform(trans); } this->Modified(); } } void mitk::CameraController::Fit() { if (this->GetRenderer()->GetMapperID() == BaseRenderer::Standard2D) { this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetParallelScale(ComputeMaxParallelScale()); this->GetRenderer()->GetVtkRenderer()->GetActiveCamera() ->SetClippingRange(0.1, 1000000); //Reason for huge range: VTK seems to calculate the clipping planes wrong for small values. See VTK bug (id #7823) in VTK bugtracker. Point2D planePoint; planePoint[0] = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(0)*0.5; planePoint[1] = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(1)*0.5; MoveCameraToPoint(planePoint); } } + +void mitk::CameraController::SetScaleFactorInMMPerDisplayUnit( ScalarType scale ) +{ + if ( this->GetRenderer()->GetMapperID() != BaseRenderer::Standard2D ) + { + return; + } + + this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetParallelScale( + this->GetRenderer()->GetViewportSize()[1] * scale * 0.5 ); +}