diff --git a/Modules/IGT/Rendering/mitkNavigationDataSliceVisualization.cpp b/Modules/IGT/Rendering/mitkNavigationDataSliceVisualization.cpp new file mode 100644 index 0000000000..2eee556a18 --- /dev/null +++ b/Modules/IGT/Rendering/mitkNavigationDataSliceVisualization.cpp @@ -0,0 +1,223 @@ +/*=================================================================== + +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 "mitkNavigationDataSliceVisualization.h" + +#include "mitkBaseRenderer.h" + +mitk::NavigationDataSliceVisualization::NavigationDataSliceVisualization() : mitk::NavigationDataToNavigationDataFilter(), + m_Renderer(NULL), + m_ViewDirection(Axial) +{ + m_TipOffset[0] = 0.0f; + m_TipOffset[1] = 0.0f; + m_TipOffset[2] = 0.0f; + + m_ToolTrajectory[0] = 0; + m_ToolTrajectory[1] = 0; + m_ToolTrajectory[2] = -1; + + m_WorldVerticalVector[0] = 0.0; + m_WorldVerticalVector[1] = 1.0; + m_WorldVerticalVector[2] = 0.0; +} + +void mitk::NavigationDataSliceVisualization::SetToolTrajectory(Vector3D direction) +{ + if (Equal(direction.GetNorm(), 0.0)) + { + MITK_WARN << "Ignoring invalid direction of projection: " << direction; + return; + } + + if (m_ToolTrajectory != direction) + { + m_ToolTrajectory = direction; + this->SetViewDirection(Oblique); + this->Modified(); + } +} + +void mitk::NavigationDataSliceVisualization::GenerateData() +{ + // check if renderer was set + if (m_Renderer.IsNull()) + { + itkExceptionMacro(<< "Renderer was not properly set"); + } + + /* update outputs with tracking data from tools */ + unsigned int numberOfInputs = this->GetNumberOfInputs(); + if (numberOfInputs == 0) + { + return; + } + for (unsigned int i = 0; i < numberOfInputs ; ++i) + { + NavigationData* output = this->GetOutput(i); + assert(output); + const NavigationData* input = this->GetInput(i); + assert(input); + + if (!input->IsDataValid()) + continue; + + output->Graft(input); // First, copy all information from input to output + } + + // Nothing left to do if we don't have an input with valid data + if (numberOfInputs == 0 || !this->GetInput()->IsDataValid()) + return; + + // get position from NavigationData to move the slice to this position + Point3D slicePosition = this->GetInput()->GetPosition(); + + { + NavigationData::OrientationType orientation = this->GetInput()->GetOrientation(); + + Vector3D transformedTipOffset; + transformedTipOffset.SetVnlVector(orientation.rotate(m_TipOffset.GetVnlVector())); + + slicePosition += transformedTipOffset; + + mitk::SliceNavigationController::Pointer snc = m_Renderer->GetSliceNavigationController(); + + if (Axial == m_ViewDirection) + { + snc->SetViewDirection(mitk::SliceNavigationController::Axial); + snc->SelectSliceByPoint(slicePosition); + } + else if (Sagittal == m_ViewDirection) + { + snc->SetViewDirection(mitk::SliceNavigationController::Sagittal); + snc->SelectSliceByPoint(slicePosition); + } + else if (Frontal == m_ViewDirection) + { + snc->SetViewDirection(mitk::SliceNavigationController::Frontal); + snc->SelectSliceByPoint(slicePosition); + } + else if (AxialOblique == m_ViewDirection || SagittalOblique == m_ViewDirection) + { + const int slicingPlaneXAxis = AxialOblique == m_ViewDirection ? 0 : 2; + + // The column 0 is the slicing plane's x-axis, column 1 is the slicing plane's y-axis + const mitk::Geometry2D::TransformType::MatrixType &m = + m_Renderer->GetCurrentWorldGeometry2D()->GetIndexToWorldTransform()->GetMatrix(); + + // Rotate the tool trajectory vector into world coordinate frame (assuming + // NavigationData has passed through a NavigationDataTransformFilter to + // convert it into world coordinate frame) + Vector3D slicingPlaneYAxisVector; + slicingPlaneYAxisVector.SetVnlVector(orientation.rotate(m_ToolTrajectory.GetVnlVector())); + + // Project the tool trajectory onto the plane normal to x-axis of this + // oblique slicing. This defines the y-axis ("up") of the oblique slicing + // plane + slicingPlaneYAxisVector[slicingPlaneXAxis] = 0.0; + + // Do nothing for ambigous/undefined cases: + // - the R-L component of the x-axis is zero (for AxialOblique) + // - the S-I component of the x-axis is zero (for SagittalOblique) + // - the A-P component of the y-axis is zero + if ( m(slicingPlaneXAxis,0) == 0.0 || + m(1,1) == 0.0 || + (slicingPlaneXAxis != 0 && slicingPlaneYAxisVector[0] == 0.0) || + (slicingPlaneXAxis != 1 && slicingPlaneYAxisVector[1] == 0.0) || + (slicingPlaneXAxis != 2 && slicingPlaneYAxisVector[2] == 0.0) ) + { + return; + } + + // Maintain the A-P orientation of the slice's y-axis regardless of what + // direction the tool trajectory points + /// @todo Use std::signbit + if ( (m(1,1) > 0) != (slicingPlaneYAxisVector[1] > 0) ) + { + slicingPlaneYAxisVector *= -1; + } + + Vector3D slicingPlaneXAxisVector; + slicingPlaneXAxisVector.Fill(0.0); + // For AxialOblique: maintain the Left/Right direction of the slice's x-axis + // For SagittalOblique: maintain the Superior/Inferior direction of the slice's x-axis + /// @todo Use std::copysign + slicingPlaneXAxisVector[slicingPlaneXAxis] = m(slicingPlaneXAxis,0) > 0 ? 1.0 : -1.0; + + Point3D origin; + FillVector3D(origin, 0.0, 0.0, 0.0); + snc->ReorientSlices(origin, slicingPlaneXAxisVector, slicingPlaneYAxisVector); + snc->SelectSliceByPoint(slicePosition); + } + else if (Oblique == m_ViewDirection) + { + Vector3D slicingPlaneNormalVector; + slicingPlaneNormalVector.SetVnlVector(orientation.rotate(m_ToolTrajectory.GetVnlVector())); + + // The second column of the Index-to-World matrix is the positive y-axis + // of the current slicing plane in world coordinates. + const mitk::Geometry2D::TransformType::MatrixType &m = + m_Renderer->GetCurrentWorldGeometry2D()->GetIndexToWorldTransform()->GetMatrix(); + mitk::Vector3D currentSlicingPlaneUpVector; + mitk::FillVector3D(currentSlicingPlaneUpVector, m[0][1], m[1][1], m[2][1]); + mitk::Vector3D worldUpVector = m_WorldVerticalVector; + if (angle(worldUpVector.GetVnlVector(), currentSlicingPlaneUpVector.GetVnlVector()) > vnl_math::pi_over_2 ) + { + worldUpVector *= -1; + } + + mitk::PlaneGeometry::Pointer slicingPlane = mitk::PlaneGeometry::New(); + Point3D origin; + FillVector3D(origin, 0.0, 0.0, 0.0); + slicingPlane->InitializePlane(origin, slicingPlaneNormalVector); + + // Now that we have the direction of WorldVerticalVector chosen to be the + // most "up" direction, project it onto the slicing plane to define the + // up vector (y-axis) of the reoriented slices + mitk::Vector3D slicingPlaneUpVector; + if ( slicingPlane->Project(worldUpVector, slicingPlaneUpVector) ) + { + // slicingPlaneUpVector CROSS slicingPlaneNormalVector -> slicingPlaneRightVector + // Math is done in double precision as much as possible to get more + // orthogonal right and up vectors which fixes a VNL SVD error when + // the WorldGeometry matrix is later inverted + itk::Vector slicingPlaneUpVector_double; + FillVector3D(slicingPlaneUpVector_double, + slicingPlaneUpVector[0], slicingPlaneUpVector[1], slicingPlaneUpVector[2]); + itk::Vector slicingPlaneNormalVector_double; + FillVector3D(slicingPlaneNormalVector_double, + slicingPlaneNormalVector[0], slicingPlaneNormalVector[1], slicingPlaneNormalVector[2]); + itk::Vector slicingPlaneRightVector_double = itk::CrossProduct(slicingPlaneUpVector_double, + slicingPlaneNormalVector_double); + + mitk::Vector3D slicingPlaneRightVector; + mitk::FillVector3D(slicingPlaneRightVector, + slicingPlaneRightVector_double[0], slicingPlaneRightVector_double[1], slicingPlaneRightVector_double[2]); + mitk::FillVector3D(slicingPlaneUpVector, + slicingPlaneUpVector_double[0], slicingPlaneUpVector_double[1], slicingPlaneUpVector_double[2]); + + snc->ReorientSlices(origin, slicingPlaneRightVector, slicingPlaneUpVector); + snc->SelectSliceByPoint(slicePosition); + } + } + else + { + MITK_ERROR << "Unsupported ViewDirection: " << m_ViewDirection; + } + + m_Renderer->RequestUpdate(); + } +} + diff --git a/Modules/IGT/Rendering/mitkNavigationDataSliceVisualization.h b/Modules/IGT/Rendering/mitkNavigationDataSliceVisualization.h new file mode 100644 index 0000000000..93af1a84f1 --- /dev/null +++ b/Modules/IGT/Rendering/mitkNavigationDataSliceVisualization.h @@ -0,0 +1,139 @@ +/*=================================================================== + +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 MITKNAVIGATIONDATASLICEVISUALIZATION_H_HEADER_INCLUDED_ +#define MITKNAVIGATIONDATASLICEVISUALIZATION_H_HEADER_INCLUDED_ + +#include "mitkNavigationDataToNavigationDataFilter.h" +#include "mitkBaseRenderer.h" +#include "mitkVector.h" + +namespace mitk +{ +/**Documentation +* \brief Control the position and orientation of rendered slices with NavigationData +* +* A NavigationDataToNavigationDataFilter that takes NavigationData as input and +* sets the position and, optionally, the orientation of the slice plane for a +* user-specified renderer. +* +* \ingroup IGT +*/ +class MitkIGT_EXPORT NavigationDataSliceVisualization : public NavigationDataToNavigationDataFilter +{ + public: + mitkClassMacro(NavigationDataSliceVisualization, NavigationDataToNavigationDataFilter) + itkNewMacro(Self) + + enum ViewDirection + { + /** + * Tracked slice planes are NOT re-oriented, only the position + * of the slice plane is controlled by the input navigation data. + */ + Axial = 0, + Sagittal, + Frontal, + /** + * Axial plane "tilted" about the lateral vector so that it is coplanar + * with the tool trajectory + */ + AxialOblique, + /** + * Sagittal plane "tilted" about the axial vector so that it is coplanar + * with the tool trajectory + */ + SagittalOblique, + /** + * Slice plane normal to the tool trajectory + */ + Oblique + }; + + /** + * \brief Set/get the renderer that visualizes the navigation data + */ + itkSetObjectMacro(Renderer,BaseRenderer) + itkGetConstObjectMacro(Renderer,BaseRenderer) + + /** + * \brief Set/get the tip offset used for plane tracking + * + * This is an additional offset vector applied to the input navigation + * data. It is defined in tool tip coordinates. In other words: + * + * \code + * position_slice = position_input + orient_input.rotate(TipOffset) + * \endcode + * + * Default is [0,0,0]. + */ + itkSetMacro(TipOffset, Vector3D) + itkGetConstMacro(TipOffset,Vector3D) + + /** + * \brief Set/get the tool trajectory used to define the cutting plane + * normal direction. + * + * This vector, defined in tool tip coordinates, applies only when the + * ViewDirection is Oblique. + * + * Default is [0,0,-1]. + */ + virtual void SetToolTrajectory(Vector3D direction); + itkGetConstMacro(ToolTrajectory, Vector3D) + + /** + * \brief Set/get the world vertical vector used to define the y-axis of the + * cutting plane + * + * This vector, defined in world coordinates, applies only when the + * ViewDirection is Oblique. It is projected onto the cutting plane to + * define the vertical orientation of the slice. + * + * The direction of this vector does not matter (i.e. [0,1,0] is the same + * as [0,-1,0]). The direction will be determined automatically by + * choosing the one that is closest to the direction of the y-axis of the + * PlaneGeometry before each update. This way, the anatomical axis + * directions that get set initially will be maintained after every update + * of this filter. + * + * Default is [0,1,0]. + */ + itkSetMacro(WorldVerticalVector, Vector3D) + itkGetConstMacro(WorldVerticalVector, Vector3D) + + /** + * \brief Set/get the orientation of the sliced plane + * + * Default is Axial. + */ + itkSetEnumMacro(ViewDirection,ViewDirection) + itkGetEnumMacro(ViewDirection,ViewDirection) + + protected: + NavigationDataSliceVisualization(); + virtual void GenerateData(); + + BaseRenderer::Pointer m_Renderer; + Vector3D m_TipOffset; + Vector3D m_ToolTrajectory; + Vector3D m_WorldVerticalVector; + ViewDirection m_ViewDirection; +}; + +} // end namespace mitk + +#endif // NEMOSLICEVISUALIZATIONFILTER_H diff --git a/Modules/IGT/files.cmake b/Modules/IGT/files.cmake index a23020d003..d7158c8d67 100644 --- a/Modules/IGT/files.cmake +++ b/Modules/IGT/files.cmake @@ -1,90 +1,91 @@ set(CPP_FILES TestingHelper/mitkNavigationToolStorageTestHelper.cpp Algorithms/mitkNavigationDataDelayFilter.cpp Algorithms/mitkNavigationDataDisplacementFilter.cpp Algorithms/mitkNavigationDataEvaluationFilter.cpp Algorithms/mitkNavigationDataLandmarkTransformFilter.cpp Algorithms/mitkNavigationDataReferenceTransformFilter.cpp Algorithms/mitkNavigationDataSmoothingFilter.cpp Algorithms/mitkNavigationDataToMessageFilter.cpp Algorithms/mitkNavigationDataToNavigationDataFilter.cpp Algorithms/mitkNavigationDataToPointSetFilter.cpp Algorithms/mitkNavigationDataTransformFilter.cpp Common/mitkIGTTimeStamp.cpp Common/mitkSerialCommunication.cpp Common/mitkTrackingTypes.cpp DataManagement/mitkNavigationData.cpp DataManagement/mitkNavigationDataSet.cpp DataManagement/mitkNavigationDataSource.cpp DataManagement/mitkNavigationTool.cpp DataManagement/mitkNavigationToolStorage.cpp DataManagement/mitkTrackingDeviceSourceConfigurator.cpp DataManagement/mitkTrackingDeviceSource.cpp ExceptionHandling/mitkIGTException.cpp ExceptionHandling/mitkIGTHardwareException.cpp ExceptionHandling/mitkIGTIOException.cpp IO/mitkNavigationDataPlayer.cpp IO/mitkNavigationDataPlayerBase.cpp IO/mitkNavigationDataRecorder.cpp IO/mitkNavigationDataRecorderDeprecated.cpp IO/mitkNavigationDataSequentialPlayer.cpp IO/mitkNavigationToolReader.cpp IO/mitkNavigationToolStorageSerializer.cpp IO/mitkNavigationToolStorageDeserializer.cpp IO/mitkNavigationToolWriter.cpp IO/mitkNavigationDataReaderInterface.cpp IO/mitkNavigationDataReaderXML.cpp IO/mitkNavigationDataReaderCSV.cpp IO/mitkNavigationDataSetWriterXML.cpp IO/mitkNavigationDataSetWriterCSV.cpp Rendering/mitkCameraVisualization.cpp Rendering/mitkNavigationDataObjectVisualizationFilter.cpp + Rendering/mitkNavigationDataSliceVisualization.cpp TrackingDevices/mitkClaronTool.cpp TrackingDevices/mitkClaronTrackingDevice.cpp TrackingDevices/mitkInternalTrackingTool.cpp TrackingDevices/mitkNDIPassiveTool.cpp TrackingDevices/mitkNDIProtocol.cpp TrackingDevices/mitkNDITrackingDevice.cpp TrackingDevices/mitkTrackingDevice.cpp TrackingDevices/mitkTrackingTool.cpp TrackingDevices/mitkTrackingVolumeGenerator.cpp TrackingDevices/mitkVirtualTrackingDevice.cpp TrackingDevices/mitkVirtualTrackingTool.cpp TrackingDevices/mitkOptitrackErrorMessages.cpp TrackingDevices/mitkOptitrackTrackingDevice.cpp TrackingDevices/mitkOptitrackTrackingTool.cpp ) set(RESOURCE_FILES ClaronMicron.stl IntuitiveDaVinci.stl NDIAurora.stl NDIAurora_Dome.stl NDIAuroraCompactFG_Dome.stl NDIAuroraPlanarFG_Dome.stl NDIAuroraTabletopFG_Dome.stl NDIAuroraTabletopFG_Prototype_Dome.stl NDIPolarisOldModel.stl NDIPolarisSpectra.stl NDIPolarisSpectraExtendedPyramid.stl NDIPolarisVicra.stl ) if(MITK_USE_MICRON_TRACKER) set(CPP_FILES ${CPP_FILES} TrackingDevices/mitkClaronInterface.cpp) else() set(CPP_FILES ${CPP_FILES} TrackingDevices/mitkClaronInterfaceStub.cpp) endif(MITK_USE_MICRON_TRACKER) if(MITK_USE_MICROBIRD_TRACKER) set(CPP_FILES ${CPP_FILES} TrackingDevices/mitkMicroBirdTrackingDevice.cpp) endif(MITK_USE_MICROBIRD_TRACKER)